diff --git a/plugin-system/src/model/index.ts b/plugin-system/src/model/index.ts index 350c4190..3cc30c4a 100644 --- a/plugin-system/src/model/index.ts +++ b/plugin-system/src/model/index.ts @@ -13,6 +13,8 @@ export * from './datasource'; export * from './legend'; +export * from './log-queries'; +export * from './log-volume-utils'; export * from './panels'; export * from './plugins'; export * from './plugin-base'; diff --git a/plugin-system/src/model/log-queries.ts b/plugin-system/src/model/log-queries.ts index 1d6b30fa..2a023917 100644 --- a/plugin-system/src/model/log-queries.ts +++ b/plugin-system/src/model/log-queries.ts @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { AbsoluteTimeRange, UnknownSpec, LogData } from '@perses-dev/spec'; +import { AbsoluteTimeRange, UnknownSpec, LogData, QueryDefinition } from '@perses-dev/spec'; import { DatasourceStore, Plugin, VariableStateMap } from '@perses-dev/plugin-system'; export interface LogQueryResult { @@ -36,4 +36,5 @@ type LogQueryPluginDependencies = { export interface LogQueryPlugin extends Plugin { getLogData: (spec: Spec, ctx: LogQueryContext, abortSignal?: AbortSignal) => Promise; dependsOn?: (spec: Spec, ctx: LogQueryContext) => LogQueryPluginDependencies; + createVolumeQuery?: (spec: Spec, ctx: LogQueryContext) => QueryDefinition | null; } diff --git a/plugin-system/src/model/log-volume-utils.ts b/plugin-system/src/model/log-volume-utils.ts new file mode 100644 index 00000000..9e27a255 --- /dev/null +++ b/plugin-system/src/model/log-volume-utils.ts @@ -0,0 +1,66 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Target number of bars for log volume histogram +const TARGET_HISTOGRAM_BARS = 40; + +// Standard intervals for histogram calculations (in milliseconds) +const STANDARD_INTERVALS = [ + { ms: 1000, label: '1s' }, + { ms: 2000, label: '2s' }, + { ms: 5000, label: '5s' }, + { ms: 10000, label: '10s' }, + { ms: 15000, label: '15s' }, + { ms: 30000, label: '30s' }, + { ms: 60000, label: '1m' }, + { ms: 120000, label: '2m' }, + { ms: 300000, label: '5m' }, + { ms: 600000, label: '10m' }, + { ms: 900000, label: '15m' }, + { ms: 1800000, label: '30m' }, + { ms: 3600000, label: '1h' }, + { ms: 7200000, label: '2h' }, + { ms: 21600000, label: '6h' }, + { ms: 43200000, label: '12h' }, + { ms: 86400000, label: '1d' }, + { ms: 604800000, label: '7d' }, + { ms: 2592000000, label: '30d' }, +] as const; + +/** + * Calculates an appropriate interval for log volume histograms based on time range. + * Uses standard round intervals for better alignment with time-series data. + */ +export function calculateVolumeInterval(timeRangeMs: number): string { + // Prefer smallest interval that produces 20-100 bars (optimal range) + for (const interval of STANDARD_INTERVALS) { + const barCount = timeRangeMs / interval.ms; + if (barCount >= 20 && barCount <= 100) { + return interval.label; + } + } + + // Fallback: find closest to target if no interval fits optimal range + let bestInterval = STANDARD_INTERVALS[STANDARD_INTERVALS.length - 1]!; + let bestDistance = Infinity; + for (const interval of STANDARD_INTERVALS) { + const barCount = timeRangeMs / interval.ms; + const distance = Math.abs(barCount - TARGET_HISTOGRAM_BARS); + if (distance < bestDistance) { + bestDistance = distance; + bestInterval = interval; + } + } + + return bestInterval.label; +}