mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 10:24:54 -06:00
Loki: Add $__range variable (#36175)
* Add variable to Loki * Add tests * Update, remove redundant options * Add missing import * Update docs/sources/datasources/loki.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/datasources/loki.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
This commit is contained in:
parent
3a6ec01c05
commit
666656c925
@ -154,6 +154,9 @@ provides the following functions you can use in the `Query` input field.
|
||||
| `label_values(label)` | Returns a list of label values for the `label`. |
|
||||
| `label_values(log stream selector, label)` | Returns a list of label values for the `label` in the specified `log stream selector`.|
|
||||
|
||||
### Using interval and range variables
|
||||
|
||||
You can use some global built-in variables in query variables; `$__interval`, `$__interval_ms`, `$__range`, `$__range_s` and `$__range_ms`. For more information, refer to [Global built-in variables]({{< relref "../variables/variable-types/global-variables.md" >}}).
|
||||
## Annotations
|
||||
|
||||
You can use any non-metric Loki query as a source for [annotations]({{< relref "../dashboards/annotations" >}}). Log content will be used as annotation text and your log stream labels as tags, so there is no need for additional mapping.
|
||||
|
@ -70,7 +70,7 @@ This variable is the ID of the current organization.
|
||||
|
||||
## $__range
|
||||
|
||||
Currently only supported for Prometheus data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
|
||||
Currently only supported for Prometheus and Loki data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
|
||||
|
||||
## $__rate_interval
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AnnotationQueryRequest, CoreApp, DataFrame, dateTime, FieldCache, TimeSeries } from '@grafana/data';
|
||||
import { AnnotationQueryRequest, CoreApp, DataFrame, dateTime, FieldCache, TimeSeries, toUtc } from '@grafana/data';
|
||||
import { BackendSrvRequest, FetchResponse } from '@grafana/runtime';
|
||||
|
||||
import LokiDatasource from './datasource';
|
||||
import { LokiQuery, LokiResponse, LokiResultType } from './types';
|
||||
import { getQueryOptions } from 'test/helpers/getQueryOptions';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { CustomVariableModel } from '../../../features/variables/types';
|
||||
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer';
|
||||
@ -19,11 +20,21 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => backendSrv,
|
||||
}));
|
||||
|
||||
const timeSrvStub = {
|
||||
const rawRange = {
|
||||
from: toUtc('2018-04-25 10:00'),
|
||||
to: toUtc('2018-04-25 11:00'),
|
||||
};
|
||||
|
||||
const timeSrvStub = ({
|
||||
timeRange: () => ({
|
||||
from: new Date(0),
|
||||
to: new Date(1),
|
||||
from: rawRange.from,
|
||||
to: rawRange.to,
|
||||
raw: rawRange,
|
||||
}),
|
||||
} as unknown) as TimeSrv;
|
||||
|
||||
const templateSrvStub = {
|
||||
replace: jest.fn((a: string, ...rest: any) => a),
|
||||
};
|
||||
|
||||
const testLogsResponse: FetchResponse<LokiResponse> = {
|
||||
@ -322,6 +333,33 @@ describe('LokiDatasource', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('__range, __range_s and __range_ms variables', () => {
|
||||
const options = {
|
||||
targets: [{ expr: 'rate(process_cpu_seconds_total[$__range])', refId: 'A' }],
|
||||
range: {
|
||||
from: rawRange.from,
|
||||
to: rawRange.to,
|
||||
raw: rawRange,
|
||||
},
|
||||
};
|
||||
|
||||
const ds = new LokiDatasource({} as any, templateSrvStub as any, timeSrvStub as any);
|
||||
|
||||
beforeEach(() => {
|
||||
templateSrvStub.replace.mockClear();
|
||||
});
|
||||
|
||||
it('should be correctly interpolated', () => {
|
||||
ds.query(options as any);
|
||||
const range = templateSrvStub.replace.mock.calls[0][1].__range;
|
||||
const rangeMs = templateSrvStub.replace.mock.calls[0][1].__range_ms;
|
||||
const rangeS = templateSrvStub.replace.mock.calls[0][1].__range_s;
|
||||
expect(range).toEqual({ text: '3600s', value: '3600s' });
|
||||
expect(rangeMs).toEqual({ text: 3600000, value: 3600000 });
|
||||
expect(rangeS).toEqual({ text: 3600, value: 3600 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when interpolating variables', () => {
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
LogRowModel,
|
||||
QueryResultMeta,
|
||||
ScopedVars,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv, TemplateSrv, BackendSrvRequest, FetchError, getBackendSrv } from '@grafana/runtime';
|
||||
import { addLabelToQuery } from 'app/plugins/datasource/prometheus/add_label_to_query';
|
||||
@ -95,11 +96,15 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
|
||||
query(options: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> {
|
||||
const subQueries: Array<Observable<DataQueryResponse>> = [];
|
||||
const scopedVars = {
|
||||
...options.scopedVars,
|
||||
...this.getRangeScopedVars(options.range),
|
||||
};
|
||||
const filteredTargets = options.targets
|
||||
.filter((target) => target.expr && !target.hide)
|
||||
.map((target) => ({
|
||||
...target,
|
||||
expr: this.templateSrv.replace(target.expr, options.scopedVars, this.interpolateQueryExpr),
|
||||
expr: this.templateSrv.replace(target.expr, scopedVars, this.interpolateQueryExpr),
|
||||
}));
|
||||
|
||||
for (const target of filteredTargets) {
|
||||
@ -219,6 +224,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
return this.runLiveQuery(target, maxDataPoints);
|
||||
}
|
||||
const query = this.createRangeQuery(target, options, maxDataPoints);
|
||||
|
||||
return this._request(RANGE_QUERY_ENDPOINT, query).pipe(
|
||||
catchError((err: any) => this.throwUnless(err, err.status === 404, target)),
|
||||
switchMap((response: { data: LokiResponse; status: number }) =>
|
||||
@ -270,6 +276,16 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
);
|
||||
};
|
||||
|
||||
getRangeScopedVars(range: TimeRange = this.timeSrv.timeRange()) {
|
||||
const msRange = range.to.diff(range.from);
|
||||
const sRange = Math.round(msRange / 1000);
|
||||
return {
|
||||
__range_ms: { text: msRange, value: msRange },
|
||||
__range_s: { text: sRange, value: sRange },
|
||||
__range: { text: sRange + 's', value: sRange + 's' },
|
||||
};
|
||||
}
|
||||
|
||||
interpolateVariablesInQueries(queries: LokiQuery[], scopedVars: ScopedVars): LokiQuery[] {
|
||||
let expandedQueries = queries;
|
||||
if (queries && queries.length) {
|
||||
@ -305,6 +321,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
if (!query) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const interpolated = this.templateSrv.replace(query, {}, this.interpolateQueryExpr);
|
||||
return await this.processMetricFindQuery(interpolated);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user