LogContext: Fix structured metadata labels being added as stream selectors (#86825)

* LogContext: Fix structured metadata labels being added as stream selectors

* use row index
This commit is contained in:
Sven Grossmann 2024-04-24 09:00:39 +01:00 committed by GitHub
parent 0fa983ad8e
commit a8424f4831
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 24 deletions

View File

@ -37,6 +37,11 @@ const defaultLogRow = {
type: FieldType.time, type: FieldType.time,
values: [0], values: [0],
}, },
{
name: 'labelTypes',
type: FieldType.other,
values: [{ bar: 'I', foo: 'S', xyz: 'I' }],
},
], ],
}), }),
labels: { bar: 'baz', foo: 'uniqueParsedLabel', xyz: 'abc' }, labels: { bar: 'baz', foo: 'uniqueParsedLabel', xyz: 'abc' },
@ -75,7 +80,7 @@ describe('LogContextProvider', () => {
); );
expect(logContextProvider.getInitContextFilters).toBeCalled(); expect(logContextProvider.getInitContextFilters).toBeCalled();
expect(logContextProvider.getInitContextFilters).toHaveBeenCalledWith( expect(logContextProvider.getInitContextFilters).toHaveBeenCalledWith(
{ bar: 'baz', foo: 'uniqueParsedLabel', xyz: 'abc' }, expect.objectContaining({ labels: { bar: 'baz', foo: 'uniqueParsedLabel', xyz: 'abc' } }),
{ expr: '{bar="baz"}', refId: 'A' }, { expr: '{bar="baz"}', refId: 'A' },
{ {
from: dateTime(defaultLogRow.timeEpochMs), from: dateTime(defaultLogRow.timeEpochMs),
@ -399,7 +404,7 @@ describe('LogContextProvider', () => {
}; };
it('should correctly create contextFilters', async () => { it('should correctly create contextFilters', async () => {
const result = await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithoutParser); const result = await logContextProvider.getInitContextFilters(defaultLogRow, queryWithoutParser);
expect(result.contextFilters).toEqual([ expect(result.contextFilters).toEqual([
{ enabled: true, nonIndexed: false, label: 'bar', value: 'baz' }, { enabled: true, nonIndexed: false, label: 'bar', value: 'baz' },
{ enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, { enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' },
@ -409,28 +414,29 @@ describe('LogContextProvider', () => {
}); });
it('should return empty contextFilters if no query', async () => { it('should return empty contextFilters if no query', async () => {
const filters = (await logContextProvider.getInitContextFilters(defaultLogRow.labels, undefined)) const filters = (await logContextProvider.getInitContextFilters(defaultLogRow, undefined)).contextFilters;
.contextFilters;
expect(filters).toEqual([]); expect(filters).toEqual([]);
}); });
it('should return empty contextFilters if no labels', async () => { it('should return empty contextFilters if no labels', async () => {
const filters = (await logContextProvider.getInitContextFilters({}, queryWithoutParser)).contextFilters; const filters = (
await logContextProvider.getInitContextFilters({ labels: [] } as unknown as LogRowModel, queryWithoutParser)
).contextFilters;
expect(filters).toEqual([]); expect(filters).toEqual([]);
}); });
it('should call fetchSeriesLabels if parser', async () => { it('should call fetchSeriesLabels if parser', async () => {
await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser); await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser);
expect(defaultLanguageProviderMock.fetchSeriesLabels).toBeCalled(); expect(defaultLanguageProviderMock.fetchSeriesLabels).toBeCalled();
}); });
it('should call fetchSeriesLabels with given time range', async () => { it('should call fetchSeriesLabels with given time range', async () => {
await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser, timeRange); await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser, timeRange);
expect(defaultLanguageProviderMock.fetchSeriesLabels).toBeCalledWith(`{bar="baz"}`, { timeRange }); expect(defaultLanguageProviderMock.fetchSeriesLabels).toBeCalledWith(`{bar="baz"}`, { timeRange });
}); });
it('should call `languageProvider.start` if no parser with given time range', async () => { it('should call `languageProvider.start` if no parser with given time range', async () => {
await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithoutParser, timeRange); await logContextProvider.getInitContextFilters(defaultLogRow, queryWithoutParser, timeRange);
expect(defaultLanguageProviderMock.start).toBeCalledWith(timeRange); expect(defaultLanguageProviderMock.start).toBeCalledWith(timeRange);
}); });
}); });
@ -442,7 +448,7 @@ describe('LogContextProvider', () => {
}; };
it('should correctly create contextFilters', async () => { it('should correctly create contextFilters', async () => {
const result = await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser); const result = await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser);
expect(result.contextFilters).toEqual([ expect(result.contextFilters).toEqual([
{ enabled: true, nonIndexed: false, label: 'bar', value: 'baz' }, { enabled: true, nonIndexed: false, label: 'bar', value: 'baz' },
{ enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, { enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' },
@ -452,13 +458,14 @@ describe('LogContextProvider', () => {
}); });
it('should return empty contextFilters if no query', async () => { it('should return empty contextFilters if no query', async () => {
const filters = (await logContextProvider.getInitContextFilters(defaultLogRow.labels, undefined)) const filters = (await logContextProvider.getInitContextFilters(defaultLogRow, undefined)).contextFilters;
.contextFilters;
expect(filters).toEqual([]); expect(filters).toEqual([]);
}); });
it('should return empty contextFilters if no labels', async () => { it('should return empty contextFilters if no labels', async () => {
const filters = (await logContextProvider.getInitContextFilters({}, queryWithParser)).contextFilters; const filters = (
await logContextProvider.getInitContextFilters({ labels: [] } as unknown as LogRowModel, queryWithParser)
).contextFilters;
expect(filters).toEqual([]); expect(filters).toEqual([]);
}); });
}); });
@ -477,7 +484,7 @@ describe('LogContextProvider', () => {
selectedExtractedLabels: ['foo'], selectedExtractedLabels: ['foo'],
}) })
); );
const result = await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser); const result = await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser);
expect(result.contextFilters).toEqual([ expect(result.contextFilters).toEqual([
{ enabled: false, nonIndexed: false, label: 'bar', value: 'baz' }, // disabled real label { enabled: false, nonIndexed: false, label: 'bar', value: 'baz' }, // disabled real label
{ enabled: true, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, // enabled parsed label { enabled: true, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, // enabled parsed label
@ -494,7 +501,7 @@ describe('LogContextProvider', () => {
selectedExtractedLabels: ['foo'], selectedExtractedLabels: ['foo'],
}) })
); );
const result = await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser); const result = await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser);
expect(result.contextFilters).toEqual([ expect(result.contextFilters).toEqual([
{ enabled: true, nonIndexed: false, label: 'bar', value: 'baz' }, // enabled real label { enabled: true, nonIndexed: false, label: 'bar', value: 'baz' }, // enabled real label
{ enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, { enabled: false, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' },
@ -511,7 +518,7 @@ describe('LogContextProvider', () => {
selectedExtractedLabels: ['foo', 'new'], selectedExtractedLabels: ['foo', 'new'],
}) })
); );
const result = await logContextProvider.getInitContextFilters(defaultLogRow.labels, queryWithParser); const result = await logContextProvider.getInitContextFilters(defaultLogRow, queryWithParser);
expect(result.contextFilters).toEqual([ expect(result.contextFilters).toEqual([
{ enabled: false, nonIndexed: false, label: 'bar', value: 'baz' }, { enabled: false, nonIndexed: false, label: 'bar', value: 'baz' },
{ enabled: true, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' }, { enabled: true, nonIndexed: true, label: 'foo', value: 'uniqueParsedLabel' },

View File

@ -16,11 +16,10 @@ import {
dateTime, dateTime,
} from '@grafana/data'; } from '@grafana/data';
import { LabelParser, LabelFilter, LineFilters, PipelineStage, Logfmt, Json } from '@grafana/lezer-logql'; import { LabelParser, LabelFilter, LineFilters, PipelineStage, Logfmt, Json } from '@grafana/lezer-logql';
import { Labels } from '@grafana/schema';
import { LokiContextUi } from './components/LokiContextUi'; import { LokiContextUi } from './components/LokiContextUi';
import { LokiDatasource, makeRequest, REF_ID_STARTER_LOG_ROW_CONTEXT } from './datasource'; import { LokiDatasource, makeRequest, REF_ID_STARTER_LOG_ROW_CONTEXT } from './datasource';
import { escapeLabelValueInExactSelector } from './languageUtils'; import { escapeLabelValueInExactSelector, getLabelTypeFromFrame } from './languageUtils';
import { addLabelToQuery, addParserToQuery } from './modifyQuery'; import { addLabelToQuery, addParserToQuery } from './modifyQuery';
import { import {
getNodePositionsFromQuery, getNodePositionsFromQuery,
@ -61,7 +60,7 @@ export class LogContextProvider {
// to use the cached filters, we need to reinitialize them. // to use the cached filters, we need to reinitialize them.
if (this.cachedContextFilters.length === 0 || !cacheFilters) { if (this.cachedContextFilters.length === 0 || !cacheFilters) {
const filters = ( const filters = (
await this.getInitContextFilters(row.labels, origQuery, { await this.getInitContextFilters(row, origQuery, {
from: dateTime(row.timeEpochMs), from: dateTime(row.timeEpochMs),
to: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs),
raw: { from: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs) }, raw: { from: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs) },
@ -312,14 +311,15 @@ export class LogContextProvider {
}; };
getInitContextFilters = async ( getInitContextFilters = async (
labels: Labels, row: LogRowModel,
query?: LokiQuery, query?: LokiQuery,
timeRange?: TimeRange timeRange?: TimeRange
): Promise<{ contextFilters: ContextFilter[]; preservedFiltersApplied: boolean }> => { ): Promise<{ contextFilters: ContextFilter[]; preservedFiltersApplied: boolean }> => {
let preservedFiltersApplied = false; let preservedFiltersApplied = false;
if (!query || isEmpty(labels)) { if (!query || isEmpty(row.labels)) {
return { contextFilters: [], preservedFiltersApplied }; return { contextFilters: [], preservedFiltersApplied };
} }
const rowLabels = row.labels;
// 1. First we need to get all labels from the log row's label // 1. First we need to get all labels from the log row's label
// and correctly set parsed and not parsed labels // and correctly set parsed and not parsed labels
@ -338,12 +338,12 @@ export class LogContextProvider {
} }
const contextFilters: ContextFilter[] = []; const contextFilters: ContextFilter[] = [];
Object.entries(labels).forEach(([label, value]) => { Object.entries(rowLabels).forEach(([label, value]) => {
const filter: ContextFilter = { const filter: ContextFilter = {
label, label,
value: value, value: value,
enabled: allLabels.includes(label), enabled: allLabels.includes(label),
nonIndexed: !allLabels.includes(label), nonIndexed: getLabelTypeFromFrame(label, row.dataFrame, row.rowIndex) !== LabelType.Indexed,
}; };
contextFilters.push(filter); contextFilters.push(filter);

View File

@ -122,7 +122,7 @@ describe('LokiContextUi', () => {
render(<LokiContextUi {...props} />); render(<LokiContextUi {...props} />);
await waitFor(() => { await waitFor(() => {
expect(props.logContextProvider.getInitContextFilters).toHaveBeenCalledWith(props.row.labels, props.origQuery, { expect(props.logContextProvider.getInitContextFilters).toHaveBeenCalledWith(props.row, props.origQuery, {
from: dateTime(props.row.timeEpochMs), from: dateTime(props.row.timeEpochMs),
to: dateTime(props.row.timeEpochMs), to: dateTime(props.row.timeEpochMs),
raw: { from: dateTime(props.row.timeEpochMs), to: dateTime(props.row.timeEpochMs) }, raw: { from: dateTime(props.row.timeEpochMs), to: dateTime(props.row.timeEpochMs) },

View File

@ -205,7 +205,7 @@ export function LokiContextUi(props: LokiContextUiProps) {
useAsync(async () => { useAsync(async () => {
setLoading(true); setLoading(true);
const initContextFilters = await logContextProvider.getInitContextFilters(row.labels, origQuery, { const initContextFilters = await logContextProvider.getInitContextFilters(row, origQuery, {
from: dateTime(row.timeEpochMs), from: dateTime(row.timeEpochMs),
to: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs),
raw: { from: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs) }, raw: { from: dateTime(row.timeEpochMs), to: dateTime(row.timeEpochMs) },