mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Log Context: Fix bug where variables are not replaced in dashboards (#100433)
* Log Context: Fix bug where variables are not replaced in dashboards * add objects to act as `row` and `options` * run prettier * fix lint
This commit is contained in:
parent
df84d928e2
commit
d199c33d7e
@ -4,6 +4,7 @@ import { DataQuery, LogsSortOrder } from '@grafana/schema';
|
||||
|
||||
import { BusEventWithPayload } from '../events/types';
|
||||
|
||||
import { ScopedVars } from './ScopedVars';
|
||||
import { KeyValue, Labels } from './data';
|
||||
import { DataFrame } from './dataFrame';
|
||||
import { DataQueryRequest, DataQueryResponse, DataSourceApi, QueryFixAction, QueryFixType } from './datasource';
|
||||
@ -134,6 +135,7 @@ export enum LogsDedupDescription {
|
||||
export interface LogRowContextOptions {
|
||||
direction?: LogRowContextQueryDirection;
|
||||
limit?: number;
|
||||
scopedVars?: ScopedVars;
|
||||
}
|
||||
|
||||
export enum LogRowContextQueryDirection {
|
||||
@ -172,7 +174,12 @@ export interface DataSourceWithLogsContextSupport<TQuery extends DataQuery = Dat
|
||||
* @alpha
|
||||
* @internal
|
||||
*/
|
||||
getLogRowContextUi?(row: LogRowModel, runContextQuery?: () => void, origQuery?: TQuery): React.ReactNode;
|
||||
getLogRowContextUi?(
|
||||
row: LogRowModel,
|
||||
runContextQuery?: () => void,
|
||||
origQuery?: TQuery,
|
||||
scopedVars?: ScopedVars
|
||||
): React.ReactNode;
|
||||
}
|
||||
|
||||
export const hasLogsContextSupport = (datasource: unknown): datasource is DataSourceWithLogsContextSupport => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { of } from 'rxjs';
|
||||
import { initTemplateSrv } from 'test/helpers/initTemplateSrv';
|
||||
|
||||
import {
|
||||
DataQueryResponse,
|
||||
@ -8,6 +9,7 @@ import {
|
||||
createDataFrame,
|
||||
dateTime,
|
||||
} from '@grafana/data';
|
||||
import { setTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import LokiLanguageProvider from './LanguageProvider';
|
||||
import {
|
||||
@ -24,10 +26,6 @@ const defaultLanguageProviderMock = {
|
||||
getLabelKeys: jest.fn(() => ['bar', 'xyz']),
|
||||
} as unknown as LokiLanguageProvider;
|
||||
|
||||
const defaultDatasourceMock = createLokiDatasource();
|
||||
defaultDatasourceMock.query = jest.fn(() => of({ data: [] } as DataQueryResponse));
|
||||
defaultDatasourceMock.languageProvider = defaultLanguageProviderMock;
|
||||
|
||||
const defaultLogRow = {
|
||||
rowIndex: 0,
|
||||
dataFrame: createDataFrame({
|
||||
@ -68,6 +66,11 @@ const frameWithoutTypes = {
|
||||
describe('LogContextProvider', () => {
|
||||
let logContextProvider: LogContextProvider;
|
||||
beforeEach(() => {
|
||||
const templateSrv = initTemplateSrv('key', [{ type: 'query', name: 'foo', current: { value: 'baz' } }]);
|
||||
setTemplateSrv(templateSrv);
|
||||
const defaultDatasourceMock = createLokiDatasource(templateSrv);
|
||||
defaultDatasourceMock.query = jest.fn(() => of({ data: [] } as DataQueryResponse));
|
||||
defaultDatasourceMock.languageProvider = defaultLanguageProviderMock;
|
||||
logContextProvider = new LogContextProvider(defaultDatasourceMock);
|
||||
});
|
||||
|
||||
@ -107,6 +110,32 @@ describe('LogContextProvider', () => {
|
||||
expect(logContextProvider.cachedContextFilters).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should replace variables before getInitContextFilters', async () => {
|
||||
logContextProvider.getInitContextFilters = jest.fn().mockResolvedValue({
|
||||
contextFilters: [{ value: 'baz', enabled: true, nonIndexed: false, label: 'bar' }],
|
||||
preservedFiltersApplied: false,
|
||||
});
|
||||
|
||||
expect(logContextProvider.cachedContextFilters).toHaveLength(0);
|
||||
await logContextProvider.getLogRowContext(
|
||||
defaultLogRow,
|
||||
{
|
||||
limit: 10,
|
||||
direction: LogRowContextQueryDirection.Backward,
|
||||
scopedVars: { test: { value: 'baz', text: 'baz' } },
|
||||
},
|
||||
{
|
||||
expr: '{bar="$test"}',
|
||||
refId: 'A',
|
||||
}
|
||||
);
|
||||
expect(logContextProvider.getInitContextFilters).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({ expr: '{bar="baz"}', refId: 'A' }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('should not call getInitContextFilters if cachedContextFilters', async () => {
|
||||
logContextProvider.getInitContextFilters = jest
|
||||
.fn()
|
||||
@ -140,6 +169,26 @@ describe('LogContextProvider', () => {
|
||||
expect(logContextProvider.getInitContextFilters).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should replace variables before getInitContextFilters', async () => {
|
||||
logContextProvider.getInitContextFilters = jest.fn().mockResolvedValue({
|
||||
contextFilters: [{ value: 'baz', enabled: true, nonIndexed: false, label: 'bar' }],
|
||||
preservedFiltersApplied: false,
|
||||
});
|
||||
|
||||
const query = await logContextProvider.getLogRowContextQuery(
|
||||
defaultLogRow,
|
||||
{
|
||||
limit: 10,
|
||||
direction: LogRowContextQueryDirection.Backward,
|
||||
},
|
||||
{
|
||||
expr: '{bar="$test"}',
|
||||
refId: 'A',
|
||||
}
|
||||
);
|
||||
expect(query.expr).toBe('{bar="baz"}');
|
||||
});
|
||||
|
||||
it('should also call getInitContextFilters if cacheFilters is not set', async () => {
|
||||
logContextProvider.getInitContextFilters = jest.fn().mockResolvedValue({
|
||||
contextFilters: [{ value: 'baz', enabled: true, nonIndexed: false, label: 'bar' }],
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
LogRowContextQueryDirection,
|
||||
LogRowContextOptions,
|
||||
dateTime,
|
||||
ScopedVars,
|
||||
} from '@grafana/data';
|
||||
import { LabelParser, LabelFilter, LineFilters, PipelineStage, Logfmt, Json } from '@grafana/lezer-logql';
|
||||
|
||||
@ -79,6 +80,9 @@ export class LogContextProvider {
|
||||
origQuery?: LokiQuery,
|
||||
cacheFilters = true
|
||||
): Promise<LokiQuery> => {
|
||||
if (origQuery && options?.scopedVars) {
|
||||
origQuery = this.datasource.applyTemplateVariables(origQuery, options?.scopedVars);
|
||||
}
|
||||
const { query } = await this.getQueryAndRange(row, options, origQuery, cacheFilters);
|
||||
|
||||
if (!cacheFilters) {
|
||||
@ -94,6 +98,9 @@ export class LogContextProvider {
|
||||
options?: LogRowContextOptions,
|
||||
origQuery?: LokiQuery
|
||||
): Promise<{ data: DataFrame[] }> => {
|
||||
if (origQuery && options?.scopedVars) {
|
||||
origQuery = this.datasource.applyTemplateVariables(origQuery, options?.scopedVars);
|
||||
}
|
||||
const direction = (options && options.direction) || LogRowContextQueryDirection.Backward;
|
||||
const { query, range } = await this.getQueryAndRange(row, options, origQuery);
|
||||
|
||||
@ -185,7 +192,15 @@ export class LogContextProvider {
|
||||
};
|
||||
}
|
||||
|
||||
getLogRowContextUi(row: LogRowModel, runContextQuery?: () => void, origQuery?: LokiQuery): React.ReactNode {
|
||||
getLogRowContextUi(
|
||||
row: LogRowModel,
|
||||
runContextQuery?: () => void,
|
||||
origQuery?: LokiQuery,
|
||||
scopedVars?: ScopedVars
|
||||
): React.ReactNode {
|
||||
if (origQuery && scopedVars) {
|
||||
origQuery = this.datasource.applyTemplateVariables(origQuery, scopedVars);
|
||||
}
|
||||
const updateFilter = (contextFilters: ContextFilter[]) => {
|
||||
this.cachedContextFilters = contextFilters;
|
||||
|
||||
|
@ -1014,8 +1014,18 @@ export class LokiDatasource
|
||||
* Part of `DataSourceWithLogsContextSupport`, used to retrieve the log context UI for the provided log row and original query.
|
||||
* @returns A React component or element representing the log context UI for the log row.
|
||||
*/
|
||||
getLogRowContextUi(row: LogRowModel, runContextQuery: () => void, origQuery: DataQuery): React.ReactNode {
|
||||
return this.logContextProvider.getLogRowContextUi(row, runContextQuery, getLokiQueryFromDataQuery(origQuery));
|
||||
getLogRowContextUi(
|
||||
row: LogRowModel,
|
||||
runContextQuery: () => void,
|
||||
origQuery: DataQuery,
|
||||
scopedVars?: ScopedVars
|
||||
): React.ReactNode {
|
||||
return this.logContextProvider.getLogRowContextUi(
|
||||
row,
|
||||
runContextQuery,
|
||||
getLokiQueryFromDataQuery(origQuery),
|
||||
scopedVars
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -414,7 +414,7 @@ describe('LogsPanel', () => {
|
||||
await userEvent.click(screen.getByLabelText(/show context/i));
|
||||
|
||||
const getRowContextCb = logRowContextModalMock.mock.calls[0][0].getRowContext;
|
||||
getRowContextCb();
|
||||
getRowContextCb({}, {});
|
||||
expect(showContextDs.getLogRowContext).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -232,9 +232,11 @@ export const LogsPanel = ({
|
||||
return Promise.resolve({ data: [] });
|
||||
}
|
||||
|
||||
options.scopedVars = panelData.request?.scopedVars;
|
||||
|
||||
return dataSource.getLogRowContext(row, options, query);
|
||||
},
|
||||
[panelData.request?.targets, dataSourcesMap]
|
||||
[panelData.request?.targets, panelData.request?.scopedVars, dataSourcesMap]
|
||||
);
|
||||
|
||||
const getLogRowContextUi = useCallback(
|
||||
@ -257,9 +259,9 @@ export const LogsPanel = ({
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return dataSource.getLogRowContextUi(origRow, runContextQuery, query);
|
||||
return dataSource.getLogRowContextUi(origRow, runContextQuery, query, panelData.request?.scopedVars);
|
||||
},
|
||||
[panelData.request?.targets, dataSourcesMap]
|
||||
[panelData.request?.targets, panelData.request?.scopedVars, dataSourcesMap]
|
||||
);
|
||||
|
||||
// Important to memoize stuff here, as panel rerenders a lot for example when resizing.
|
||||
|
Loading…
Reference in New Issue
Block a user