Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plugin-system/src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
11 changes: 10 additions & 1 deletion plugin-system/src/model/log-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -36,4 +36,13 @@ type LogQueryPluginDependencies = {
export interface LogQueryPlugin<Spec = UnknownSpec> extends Plugin<Spec> {
getLogData: (spec: Spec, ctx: LogQueryContext, abortSignal?: AbortSignal) => Promise<LogQueryResult>;
dependsOn?: (spec: Spec, ctx: LogQueryContext) => LogQueryPluginDependencies;
/**
* Optional method to create a TimeSeriesQuery for log volume visualization.
* Returns a QueryDefinition that aggregates log volumes over time, typically grouped by log level.
* Returns null if volume queries are not supported or cannot be generated from the given spec.
*
* @param spec - The log query specification
* @param context - Context containing timeRange for dynamic interval calculation
*/
createVolumeQuery?: (spec: Spec, context?: { timeRange: { start: Date; end: Date } }) => QueryDefinition | null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
createVolumeQuery?: (spec: Spec, context?: { timeRange: { start: Date; end: Date } }) => QueryDefinition | null;
createVolumeQuery?: (spec: Spec, ctx: LogQueryContext) => QueryDefinition | null;

To me we should pass the LogQueryContext like others methods, what do you think?

}
66 changes: 66 additions & 0 deletions plugin-system/src/model/log-volume-utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Loading