mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Logs: Use result range instead of timepicker range for log histogram (#25027)
* Logs: Use result range instead of timepicker range for log histogram If a logs datasource does not send histogram data for the requested time range, the logs model computes a timeseries based on the log row counts, bucketed by an automcatically calculated time interval. Even when this histogram time series did not span the whole requested time range it was still rendered in the graph across the whole range, leaving an empty area at its start. Users find this confusing and are lead to believe their log data is missing. This change fixes this by anchoring the start of the timeseries on the first log row's timestamp from the result, and adds this smaller range as `visibleRange` to the logs model and passes it through to the logs component that optionally takes it into account to not render the empty area. The interval (bucket size) is also adjusted to account for a potentially finer resolution on the shorter visible time interval. The bucketsize multiplier was also changed from 10 to 20 to account for the space between the chart's bars. * Aligned visible range with buckets * Extract bucket size calculation and add test
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
getDisplayProcessor,
|
||||
textUtil,
|
||||
dateTime,
|
||||
AbsoluteTimeRange,
|
||||
} from '@grafana/data';
|
||||
import { getThemeColor } from 'app/core/utils/colors';
|
||||
|
||||
@@ -90,18 +91,14 @@ export function filterLogLevels(logRows: LogRowModel[], hiddenLogLevels: Set<Log
|
||||
});
|
||||
}
|
||||
|
||||
export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number, timeZone: TimeZone): GraphSeriesXY[] {
|
||||
export function makeSeriesForLogs(sortedRows: LogRowModel[], bucketSize: number, timeZone: TimeZone): GraphSeriesXY[] {
|
||||
// currently interval is rangeMs / resolution, which is too low for showing series as bars.
|
||||
// need at least 10px per bucket, so we multiply interval by 10. Should be solved higher up the chain
|
||||
// when executing queries & interval calculated and not here but this is a temporary fix.
|
||||
// intervalMs = intervalMs * 10;
|
||||
// Should be solved higher up the chain when executing queries & interval calculated and not here but this is a temporary fix.
|
||||
|
||||
// Graph time series by log level
|
||||
const seriesByLevel: any = {};
|
||||
const bucketSize = intervalMs * 10;
|
||||
const seriesList: any[] = [];
|
||||
|
||||
const sortedRows = rows.sort(sortInAscendingOrder);
|
||||
for (const row of sortedRows) {
|
||||
let series = seriesByLevel[row.logLevel];
|
||||
|
||||
@@ -198,16 +195,23 @@ function isLogsData(series: DataFrame) {
|
||||
export function dataFrameToLogsModel(
|
||||
dataFrame: DataFrame[],
|
||||
intervalMs: number | undefined,
|
||||
timeZone: TimeZone
|
||||
timeZone: TimeZone,
|
||||
absoluteRange?: AbsoluteTimeRange
|
||||
): LogsModel {
|
||||
const { logSeries, metricSeries } = separateLogsAndMetrics(dataFrame);
|
||||
const logsModel = logSeriesToLogsModel(logSeries);
|
||||
|
||||
if (logsModel) {
|
||||
if (metricSeries.length === 0) {
|
||||
// Create metrics from logs
|
||||
// If interval is not defined or 0 we cannot really compute the series
|
||||
logsModel.series = intervalMs ? makeSeriesForLogs(logsModel.rows, intervalMs, timeZone) : [];
|
||||
// Create histogram metrics from logs using the interval as bucket size for the line count
|
||||
if (intervalMs && logsModel.rows.length > 0) {
|
||||
const sortedRows = logsModel.rows.sort(sortInAscendingOrder);
|
||||
const { visibleRange, bucketSize } = getSeriesProperties(sortedRows, intervalMs, absoluteRange);
|
||||
logsModel.visibleRange = visibleRange;
|
||||
logsModel.series = makeSeriesForLogs(sortedRows, bucketSize, timeZone);
|
||||
} else {
|
||||
logsModel.series = [];
|
||||
}
|
||||
} else {
|
||||
// We got metrics in the dataFrame so process those
|
||||
logsModel.series = getGraphSeriesModel(
|
||||
@@ -234,6 +238,43 @@ export function dataFrameToLogsModel(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clamped time range and interval based on the visible logs and the given range.
|
||||
*
|
||||
* @param sortedRows Log rows from the query response
|
||||
* @param intervalMs Dynamnic data interval based on available pixel width
|
||||
* @param absoluteRange Requested time range
|
||||
* @param pxPerBar Default: 20, buckets will be rendered as bars, assuming 10px per histogram bar plus some free space around it
|
||||
*/
|
||||
export function getSeriesProperties(
|
||||
sortedRows: LogRowModel[],
|
||||
intervalMs: number,
|
||||
absoluteRange: AbsoluteTimeRange,
|
||||
pxPerBar = 20,
|
||||
minimumBucketSize = 1000
|
||||
) {
|
||||
let visibleRange = absoluteRange;
|
||||
let resolutionIntervalMs = intervalMs;
|
||||
let bucketSize = Math.max(resolutionIntervalMs * pxPerBar, minimumBucketSize);
|
||||
// Clamp time range to visible logs otherwise big parts of the graph might look empty
|
||||
if (absoluteRange) {
|
||||
const earliest = sortedRows[0].timeEpochMs;
|
||||
const latest = absoluteRange.to;
|
||||
const visibleRangeMs = latest - earliest;
|
||||
if (visibleRangeMs > 0) {
|
||||
// Adjust interval bucket size for potentially shorter visible range
|
||||
const clampingFactor = visibleRangeMs / (absoluteRange.to - absoluteRange.from);
|
||||
resolutionIntervalMs *= clampingFactor;
|
||||
// Minimum bucketsize of 1s for nicer graphing
|
||||
bucketSize = Math.max(Math.ceil(resolutionIntervalMs * pxPerBar), minimumBucketSize);
|
||||
// makeSeriesForLogs() aligns dataspoints with time buckets, so we do the same here to not cut off data
|
||||
const adjustedEarliest = Math.floor(earliest / bucketSize) * bucketSize;
|
||||
visibleRange = { from: adjustedEarliest, to: latest };
|
||||
}
|
||||
}
|
||||
return { bucketSize, visibleRange };
|
||||
}
|
||||
|
||||
function separateLogsAndMetrics(dataFrames: DataFrame[]) {
|
||||
const metricSeries: DataFrame[] = [];
|
||||
const logSeries: DataFrame[] = [];
|
||||
|
||||
Reference in New Issue
Block a user