Loki: Use fixed bucket size for logs volume (#40873)

* Use fixed bucket size for logs volume

* Fix unit tests
This commit is contained in:
Piotr Jamróz 2021-10-28 10:53:15 +02:00 committed by GitHub
parent fbd68c4e96
commit 877c726246
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 6 deletions

View File

@ -60,7 +60,7 @@ export function LogsVolumePanel(props: Props) {
if (zoomRatio !== undefined && zoomRatio < 1) { if (zoomRatio !== undefined && zoomRatio < 1) {
zoomLevelInfo = ( zoomLevelInfo = (
<> <>
<span className={styles.zoomInfo}>Reload to show higher resolution</span> <span className={styles.zoomInfo}>Reload logs volume</span>
<Button size="xs" icon="sync" variant="secondary" onClick={onLoadLogsVolume} /> <Button size="xs" icon="sync" variant="secondary" onClick={onLoadLogsVolume} />
</> </>
); );

View File

@ -40,6 +40,11 @@ describe('LokiLogsVolumeProvider', () => {
request = ({ request = ({
targets: [{ expr: '{app="app01"}' }, { expr: '{app="app02"}' }], targets: [{ expr: '{app="app01"}' }, { expr: '{app="app02"}' }],
range: { from: 0, to: 1 }, range: { from: 0, to: 1 },
scopedVars: {
__interval_ms: {
value: 1000,
},
},
} as unknown) as DataQueryRequest<LokiQuery>; } as unknown) as DataQueryRequest<LokiQuery>;
volumeProvider = createLokiLogsVolumeProvider((datasource as unknown) as LokiDatasource, request); volumeProvider = createLokiLogsVolumeProvider((datasource as unknown) as LokiDatasource, request);
} }

View File

@ -11,6 +11,7 @@ import {
LoadingState, LoadingState,
LogLevel, LogLevel,
MutableDataFrame, MutableDataFrame,
ScopedVars,
toDataFrame, toDataFrame,
} from '@grafana/data'; } from '@grafana/data';
import { LokiQuery } from '../types'; import { LokiQuery } from '../types';
@ -20,26 +21,36 @@ import LokiDatasource, { isMetricsQuery } from '../datasource';
import { LogLevelColor } from '../../../../core/logs_model'; import { LogLevelColor } from '../../../../core/logs_model';
import { BarAlignment, GraphDrawStyle, StackingMode } from '@grafana/schema'; import { BarAlignment, GraphDrawStyle, StackingMode } from '@grafana/schema';
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
/** /**
* Logs volume query may be expensive as it requires counting all logs in the selected range. If such query * Logs volume query may be expensive as it requires counting all logs in the selected range. If such query
* takes too much time it may need be made more specific to limit number of logs processed under the hood. * takes too much time it may need be made more specific to limit number of logs processed under the hood.
*/ */
const TIMEOUT = 10000; const TIMEOUT = 10 * SECOND;
export function createLokiLogsVolumeProvider( export function createLokiLogsVolumeProvider(
datasource: LokiDatasource, datasource: LokiDatasource,
dataQueryRequest: DataQueryRequest<LokiQuery> dataQueryRequest: DataQueryRequest<LokiQuery>
): Observable<DataQueryResponse> { ): Observable<DataQueryResponse> {
const logsVolumeRequest = cloneDeep(dataQueryRequest); const logsVolumeRequest = cloneDeep(dataQueryRequest);
const intervalInfo = getIntervalInfo(dataQueryRequest.scopedVars);
logsVolumeRequest.targets = logsVolumeRequest.targets logsVolumeRequest.targets = logsVolumeRequest.targets
.filter((target) => target.expr && !isMetricsQuery(target.expr)) .filter((target) => target.expr && !isMetricsQuery(target.expr))
.map((target) => { .map((target) => {
return { return {
...target, ...target,
instant: false, instant: false,
expr: `sum by (level) (count_over_time(${target.expr}[$__interval]))`, expr: `sum by (level) (count_over_time(${target.expr}[${intervalInfo.interval}]))`,
}; };
}); });
logsVolumeRequest.interval = intervalInfo.interval;
if (intervalInfo.intervalMs !== undefined) {
logsVolumeRequest.intervalMs = intervalInfo.intervalMs;
}
return new Observable((observer) => { return new Observable((observer) => {
let rawLogsVolume: DataFrame[] = []; let rawLogsVolume: DataFrame[] = [];
@ -54,7 +65,12 @@ export function createLokiLogsVolumeProvider(
.pipe( .pipe(
timeout({ timeout({
each: TIMEOUT, each: TIMEOUT,
with: () => throwError(new Error('Request timed-out. Please make your query more specific and try again.')), with: () =>
throwError(
new Error(
'Request timed-out. Please try making your query more specific or narrow selected time range and try again.'
)
),
}) })
) )
.subscribe({ .subscribe({
@ -133,8 +149,6 @@ function getFieldConfig(level: LogLevel, levels: number) {
custom: { custom: {
drawStyle: GraphDrawStyle.Bars, drawStyle: GraphDrawStyle.Bars,
barAlignment: BarAlignment.Center, barAlignment: BarAlignment.Center,
barWidthFactor: 0.9,
barMaxWidth: 5,
lineColor: color, lineColor: color,
pointColor: color, pointColor: color,
fillColor: color, fillColor: color,
@ -196,3 +210,27 @@ function getLogLevelFromLabels(labels: Labels): LogLevel {
} }
return levelLabel ? getLogLevelFromKey(labels[levelLabel]) : LogLevel.unknown; return levelLabel ? getLogLevelFromKey(labels[levelLabel]) : LogLevel.unknown;
} }
function getIntervalInfo(scopedVars: ScopedVars): { interval: string; intervalMs?: number } {
if (scopedVars.__interval) {
let intervalMs: number = scopedVars.__interval_ms.value;
let interval = '';
if (intervalMs > HOUR) {
intervalMs = DAY;
interval = '1d';
} else if (intervalMs > MINUTE) {
intervalMs = HOUR;
interval = '1h';
} else if (intervalMs > SECOND) {
intervalMs = MINUTE;
interval = '1m';
} else {
intervalMs = SECOND;
interval = '1s';
}
return { interval, intervalMs };
} else {
return { interval: '$__interval' };
}
}