Explore: Fix using data source line limit when opening logs sample in split view (#66601)

* Logs sample: Use data source max lines setting when opening logs in split view

* Add internal tags
This commit is contained in:
Ivana Huckova
2023-04-19 17:04:47 +02:00
committed by GitHub
parent 42cdec369d
commit 33186e3e23
7 changed files with 121 additions and 56 deletions

View File

@@ -163,6 +163,26 @@ export enum SupplementaryQueryType {
LogsSample = 'LogsSample', LogsSample = 'LogsSample',
} }
/**
* @internal
*/
export type SupplementaryQueryOptions = LogsVolumeOption | LogsSampleOptions;
/**
* @internal
*/
export type LogsVolumeOption = {
type: SupplementaryQueryType.LogsVolume;
};
/**
* @internal
*/
export type LogsSampleOptions = {
type: SupplementaryQueryType.LogsSample;
limit?: number;
};
/** /**
* Types of logs volume responses. A data source may return full range histogram (based on selected range) * Types of logs volume responses. A data source may return full range histogram (based on selected range)
* or limited (based on returned results). This information is attached to DataFrame.meta.custom object. * or limited (based on returned results). This information is attached to DataFrame.meta.custom object.
@@ -206,7 +226,7 @@ export interface DataSourceWithSupplementaryQueriesSupport<TQuery extends DataQu
* Returns a supplementary query to be used to fetch supplementary data based on the provided type and original query. * Returns a supplementary query to be used to fetch supplementary data based on the provided type and original query.
* If provided query is not suitable for provided supplementary query type, undefined should be returned. * If provided query is not suitable for provided supplementary query type, undefined should be returned.
*/ */
getSupplementaryQuery(type: SupplementaryQueryType, query: TQuery): TQuery | undefined; getSupplementaryQuery(options: SupplementaryQueryOptions, originalQuery: TQuery): TQuery | undefined;
} }
export const hasSupplementaryQuerySupport = <TQuery extends DataQuery>( export const hasSupplementaryQuerySupport = <TQuery extends DataQuery>(

View File

@@ -50,7 +50,7 @@ export function LogsSamplePanel(props: Props) {
} }
const logSampleQueries = queries const logSampleQueries = queries
.map((query) => datasourceInstance.getSupplementaryQuery(SupplementaryQueryType.LogsSample, query)) .map((query) => datasourceInstance.getSupplementaryQuery({ type: SupplementaryQueryType.LogsSample }, query))
.filter((query): query is DataQuery => !!query); .filter((query): query is DataQuery => !!query);
if (!logSampleQueries.length) { if (!logSampleQueries.length) {

View File

@@ -13,6 +13,7 @@ import {
LogsVolumeType, LogsVolumeType,
MutableDataFrame, MutableDataFrame,
SupplementaryQueryType, SupplementaryQueryType,
SupplementaryQueryOptions,
toDataFrame, toDataFrame,
} from '@grafana/data'; } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime'; import { getDataSourceSrv } from '@grafana/runtime';
@@ -53,7 +54,7 @@ class MockDataSourceWithSupplementaryQuerySupport
return undefined; return undefined;
} }
getSupplementaryQuery(type: SupplementaryQueryType, query: DataQuery): DataQuery | undefined { getSupplementaryQuery(options: SupplementaryQueryOptions, query: DataQuery): DataQuery | undefined {
return query; return query;
} }

View File

@@ -927,22 +927,28 @@ describe('ElasticDatasource', () => {
it('does not return logs volume query for metric query', () => { it('does not return logs volume query for metric query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, { ds.getSupplementaryQuery(
refId: 'A', { type: SupplementaryQueryType.LogsVolume },
metrics: [{ type: 'count', id: '1' }], {
bucketAggs: [{ type: 'filters', settings: { filters: [{ query: 'foo', label: '' }] }, id: '1' }], refId: 'A',
query: 'foo="bar"', metrics: [{ type: 'count', id: '1' }],
}) bucketAggs: [{ type: 'filters', settings: { filters: [{ query: 'foo', label: '' }] }, id: '1' }],
query: 'foo="bar"',
}
)
).toEqual(undefined); ).toEqual(undefined);
}); });
it('returns logs volume query for log query', () => { it('returns logs volume query for log query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, { ds.getSupplementaryQuery(
refId: 'A', { type: SupplementaryQueryType.LogsVolume },
metrics: [{ type: 'logs', id: '1' }], {
query: 'foo="bar"', refId: 'A',
}) metrics: [{ type: 'logs', id: '1' }],
query: 'foo="bar"',
}
)
).toEqual({ ).toEqual({
bucketAggs: [ bucketAggs: [
{ {

View File

@@ -25,6 +25,7 @@ import {
QueryFixAction, QueryFixAction,
CoreApp, CoreApp,
SupplementaryQueryType, SupplementaryQueryType,
SupplementaryQueryOptions,
DataQueryError, DataQueryError,
rangeUtil, rangeUtil,
Field, Field,
@@ -597,14 +598,14 @@ export class ElasticDatasource
return [SupplementaryQueryType.LogsVolume]; return [SupplementaryQueryType.LogsVolume];
} }
getSupplementaryQuery(type: SupplementaryQueryType, query: ElasticsearchQuery): ElasticsearchQuery | undefined { getSupplementaryQuery(options: SupplementaryQueryOptions, query: ElasticsearchQuery): ElasticsearchQuery | undefined {
if (!this.getSupportedSupplementaryQueryTypes().includes(type)) { if (!this.getSupportedSupplementaryQueryTypes().includes(options.type)) {
return undefined; return undefined;
} }
let isQuerySuitable = false; let isQuerySuitable = false;
switch (type) { switch (options.type) {
case SupplementaryQueryType.LogsVolume: case SupplementaryQueryType.LogsVolume:
// it has to be a logs-producing range-query // it has to be a logs-producing range-query
isQuerySuitable = !!(query.metrics?.length === 1 && query.metrics[0].type === 'logs'); isQuerySuitable = !!(query.metrics?.length === 1 && query.metrics[0].type === 'logs');
@@ -655,7 +656,7 @@ export class ElasticDatasource
getLogsVolumeDataProvider(request: DataQueryRequest<ElasticsearchQuery>): Observable<DataQueryResponse> | undefined { getLogsVolumeDataProvider(request: DataQueryRequest<ElasticsearchQuery>): Observable<DataQueryResponse> | undefined {
const logsVolumeRequest = cloneDeep(request); const logsVolumeRequest = cloneDeep(request);
const targets = logsVolumeRequest.targets const targets = logsVolumeRequest.targets
.map((target) => this.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, target)) .map((target) => this.getSupplementaryQuery({ type: SupplementaryQueryType.LogsVolume }, target))
.filter((query): query is ElasticsearchQuery => !!query); .filter((query): query is ElasticsearchQuery => !!query);
if (!targets.length) { if (!targets.length) {

View File

@@ -923,11 +923,14 @@ describe('LokiDatasource', () => {
describe('logs volume', () => { describe('logs volume', () => {
it('returns logs volume query for range log query', () => { it('returns logs volume query for range log query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, { ds.getSupplementaryQuery(
expr: '{label=value}', { type: SupplementaryQueryType.LogsVolume },
queryType: LokiQueryType.Range, {
refId: 'A', expr: '{label=value}',
}) queryType: LokiQueryType.Range,
refId: 'A',
}
)
).toEqual({ ).toEqual({
expr: 'sum by (level) (count_over_time({label=value}[$__interval]))', expr: 'sum by (level) (count_over_time({label=value}[$__interval]))',
instant: false, instant: false,
@@ -939,21 +942,27 @@ describe('LokiDatasource', () => {
it('does not return logs volume query for instant log query', () => { it('does not return logs volume query for instant log query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, { ds.getSupplementaryQuery(
expr: '{label=value}', { type: SupplementaryQueryType.LogsVolume },
queryType: LokiQueryType.Instant, {
refId: 'A', expr: '{label=value}',
}) queryType: LokiQueryType.Instant,
refId: 'A',
}
)
).toEqual(undefined); ).toEqual(undefined);
}); });
it('does not return logs volume query for metric query', () => { it('does not return logs volume query for metric query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, { ds.getSupplementaryQuery(
expr: 'rate({label=value}[5m]', { type: SupplementaryQueryType.LogsVolume },
queryType: LokiQueryType.Range, {
refId: 'A', expr: 'rate({label=value}[5m]',
}) queryType: LokiQueryType.Range,
refId: 'A',
}
)
).toEqual(undefined); ).toEqual(undefined);
}); });
}); });
@@ -961,41 +970,68 @@ describe('LokiDatasource', () => {
describe('logs sample', () => { describe('logs sample', () => {
it('returns logs sample query for range metric query', () => { it('returns logs sample query for range metric query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsSample, { ds.getSupplementaryQuery(
expr: 'rate({label=value}[5m]', { type: SupplementaryQueryType.LogsSample },
queryType: LokiQueryType.Range, {
refId: 'A', expr: 'rate({label=value}[5m]',
}) queryType: LokiQueryType.Range,
refId: 'A',
}
)
).toEqual({ ).toEqual({
expr: '{label=value}', expr: '{label=value}',
queryType: 'range', queryType: 'range',
refId: 'log-sample-A', refId: 'log-sample-A',
maxLines: 100, maxLines: 20,
}); });
}); });
it('returns logs sample query for instant metric query', () => { it('returns logs sample query for instant metric query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsSample, { ds.getSupplementaryQuery(
expr: 'rate({label=value}[5m]', { type: SupplementaryQueryType.LogsSample },
queryType: LokiQueryType.Instant, {
refId: 'A', expr: 'rate({label=value}[5m]',
}) queryType: LokiQueryType.Instant,
refId: 'A',
}
)
).toEqual({ ).toEqual({
expr: '{label=value}', expr: '{label=value}',
queryType: 'instant', queryType: 'instant',
refId: 'log-sample-A', refId: 'log-sample-A',
maxLines: 100, maxLines: 20,
});
});
it('correctly overrides maxLines if limit is set', () => {
expect(
ds.getSupplementaryQuery(
{ type: SupplementaryQueryType.LogsSample, limit: 5 },
{
expr: 'rate({label=value}[5m]',
queryType: LokiQueryType.Instant,
refId: 'A',
}
)
).toEqual({
expr: '{label=value}',
queryType: 'instant',
refId: 'log-sample-A',
maxLines: 5,
}); });
}); });
it('does not return logs sample query for log query query', () => { it('does not return logs sample query for log query query', () => {
expect( expect(
ds.getSupplementaryQuery(SupplementaryQueryType.LogsSample, { ds.getSupplementaryQuery(
expr: '{label=value}', { type: SupplementaryQueryType.LogsSample },
queryType: LokiQueryType.Range, {
refId: 'A', expr: '{label=value}',
}) queryType: LokiQueryType.Range,
refId: 'A',
}
)
).toEqual(undefined); ).toEqual(undefined);
}); });
}); });

View File

@@ -30,6 +30,7 @@ import {
QueryHint, QueryHint,
rangeUtil, rangeUtil,
ScopedVars, ScopedVars,
SupplementaryQueryOptions,
TimeRange, TimeRange,
LogRowContextOptions, LogRowContextOptions,
} from '@grafana/data'; } from '@grafana/data';
@@ -171,8 +172,8 @@ export class LokiDatasource
return [SupplementaryQueryType.LogsVolume, SupplementaryQueryType.LogsSample]; return [SupplementaryQueryType.LogsVolume, SupplementaryQueryType.LogsSample];
} }
getSupplementaryQuery(type: SupplementaryQueryType, query: LokiQuery): LokiQuery | undefined { getSupplementaryQuery(options: SupplementaryQueryOptions, query: LokiQuery): LokiQuery | undefined {
if (!this.getSupportedSupplementaryQueryTypes().includes(type)) { if (!this.getSupportedSupplementaryQueryTypes().includes(options.type)) {
return undefined; return undefined;
} }
@@ -180,7 +181,7 @@ export class LokiDatasource
const expr = removeCommentsFromQuery(normalizedQuery.expr); const expr = removeCommentsFromQuery(normalizedQuery.expr);
let isQuerySuitable = false; let isQuerySuitable = false;
switch (type) { switch (options.type) {
case SupplementaryQueryType.LogsVolume: case SupplementaryQueryType.LogsVolume:
// it has to be a logs-producing range-query // it has to be a logs-producing range-query
isQuerySuitable = !!(query.expr && isLogsQuery(query.expr) && query.queryType === LokiQueryType.Range); isQuerySuitable = !!(query.expr && isLogsQuery(query.expr) && query.queryType === LokiQueryType.Range);
@@ -206,7 +207,7 @@ export class LokiDatasource
...normalizedQuery, ...normalizedQuery,
refId: `${REF_ID_STARTER_LOG_SAMPLE}${normalizedQuery.refId}`, refId: `${REF_ID_STARTER_LOG_SAMPLE}${normalizedQuery.refId}`,
expr: getLogQueryFromMetricsQuery(expr), expr: getLogQueryFromMetricsQuery(expr),
maxLines: 100, maxLines: Number.isNaN(Number(options.limit)) ? this.maxLines : Number(options.limit),
}; };
default: default:
@@ -217,7 +218,7 @@ export class LokiDatasource
getLogsVolumeDataProvider(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> | undefined { getLogsVolumeDataProvider(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> | undefined {
const logsVolumeRequest = cloneDeep(request); const logsVolumeRequest = cloneDeep(request);
const targets = logsVolumeRequest.targets const targets = logsVolumeRequest.targets
.map((query) => this.getSupplementaryQuery(SupplementaryQueryType.LogsVolume, query)) .map((query) => this.getSupplementaryQuery({ type: SupplementaryQueryType.LogsVolume }, query))
.filter((query): query is LokiQuery => !!query); .filter((query): query is LokiQuery => !!query);
if (!targets.length) { if (!targets.length) {
@@ -238,7 +239,7 @@ export class LokiDatasource
getLogsSampleDataProvider(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> | undefined { getLogsSampleDataProvider(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> | undefined {
const logsSampleRequest = cloneDeep(request); const logsSampleRequest = cloneDeep(request);
const targets = logsSampleRequest.targets const targets = logsSampleRequest.targets
.map((query) => this.getSupplementaryQuery(SupplementaryQueryType.LogsSample, query)) .map((query) => this.getSupplementaryQuery({ type: SupplementaryQueryType.LogsSample, limit: 100 }, query))
.filter((query): query is LokiQuery => !!query); .filter((query): query is LokiQuery => !!query);
if (!targets.length) { if (!targets.length) {