mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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>(
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user