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:
Ivana Huckova 2021-07-06 02:56:01 -04:00 committed by GitHub
parent 3a6ec01c05
commit 666656c925
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 6 deletions

View File

@ -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.

View File

@ -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

View File

@ -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', () => {

View File

@ -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);
}