diff --git a/public/app/features/explore/Logs/LogsTable.test.tsx b/public/app/features/explore/Logs/LogsTable.test.tsx index ee8562a9ceb..93de51e84a4 100644 --- a/public/app/features/explore/Logs/LogsTable.test.tsx +++ b/public/app/features/explore/Logs/LogsTable.test.tsx @@ -122,6 +122,12 @@ describe('LogsTable', () => { it('should render extracted labels as columns (elastic)', async () => { setup({ logsFrames: [getMockElasticFrame()], + columnsWithMeta: { + counter: { active: true, percentOfLinesWithLabel: 3 }, + level: { active: true, percentOfLinesWithLabel: 3 }, + line: { active: true, percentOfLinesWithLabel: 3 }, + '@timestamp': { active: true, percentOfLinesWithLabel: 3 }, + }, }); await waitFor(() => { @@ -137,6 +143,8 @@ describe('LogsTable', () => { setup({ columnsWithMeta: { foo: { active: true, percentOfLinesWithLabel: 3 }, + Time: { active: true, percentOfLinesWithLabel: 3 }, + line: { active: true, percentOfLinesWithLabel: 3 }, }, }); @@ -196,7 +204,16 @@ describe('LogsTable', () => { }); it('should render a datalink for each row', async () => { - render(getComponent({}, getMockLokiFrameDataPlane())); + render( + getComponent( + { + columnsWithMeta: { + traceID: { active: true, percentOfLinesWithLabel: 3 }, + }, + }, + getMockLokiFrameDataPlane() + ) + ); await waitFor(() => { const links = screen.getAllByRole('link'); @@ -229,12 +246,13 @@ describe('LogsTable', () => { setup({ columnsWithMeta: { foo: { active: true, percentOfLinesWithLabel: 3 }, + line: { active: true, percentOfLinesWithLabel: 3 }, + Time: { active: true, percentOfLinesWithLabel: 3 }, }, }); await waitFor(() => { const columns = screen.getAllByRole('columnheader'); - expect(columns[0].textContent).toContain('Time'); expect(columns[1].textContent).toContain('line'); expect(columns[2].textContent).toContain('foo'); diff --git a/public/app/features/explore/Logs/LogsTable.tsx b/public/app/features/explore/Logs/LogsTable.tsx index f482d33b311..08c39ca56f6 100644 --- a/public/app/features/explore/Logs/LogsTable.tsx +++ b/public/app/features/explore/Logs/LogsTable.tsx @@ -19,7 +19,6 @@ import { import { config } from '@grafana/runtime'; import { AdHocFilterItem, Table } from '@grafana/ui'; import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR } from '@grafana/ui/src/components/Table/types'; -import { separateVisibleFields } from 'app/features/logs/components/logParser'; import { LogsFrame, parseLogsFrame } from 'app/features/logs/logsFrame'; import { getFieldLinksForExplore } from '../utils/links'; @@ -110,17 +109,25 @@ export function LogsTable(props: Props) { let dataFrame = logFrameRaw; // create extract JSON transformation for every field that is `json.RawMessage` - const transformations: Array = - extractFieldsAndExclude(dataFrame); + const transformations: Array = extractFields(dataFrame); - // remove hidden fields - transformations.push(...removeHiddenFields(dataFrame)); - let labelFilters = buildLabelFilters(columnsWithMeta, logsFrame); + let labelFilters = buildLabelFilters(columnsWithMeta); // Add the label filters to the transformations const transform = getLabelFiltersTransform(labelFilters); if (transform) { transformations.push(transform); + } else { + // If no fields are filtered, filter the default fields, so we don't render all columns + transformations.push({ + id: 'organize', + options: { + includeByName: { + [logsFrame.bodyField.name]: true, + [logsFrame.timeField.name]: true, + }, + }, + }); } if (transformations.length > 0) { @@ -181,7 +188,7 @@ const isFieldFilterable = (field: Field, logsFrame?: LogsFrame | undefined) => { // TODO: explore if `logsFrame.ts` can help us with getting the right fields // TODO Why is typeInfo not defined on the Field interface? -function extractFieldsAndExclude(dataFrame: DataFrame) { +function extractFields(dataFrame: DataFrame) { return dataFrame.fields .filter((field: Field & { typeInfo?: { frame: string } }) => { const isFieldLokiLabels = @@ -203,72 +210,19 @@ function extractFieldsAndExclude(dataFrame: DataFrame) { source: field.name, }, }, - // hide the field that was extracted - { - id: 'organize', - options: { - excludeByName: { - [field.name]: true, - }, - }, - }, ]; }); } -function removeHiddenFields(dataFrame: DataFrame): Array { - const transformations: Array = []; - const hiddenFields = separateVisibleFields(dataFrame, { keepBody: true, keepTimestamp: true }).hidden; - hiddenFields.forEach((field: Field) => { - transformations.push({ - id: 'organize', - options: { - excludeByName: { - [field.name]: true, - }, - }, - }); - }); - - return transformations; -} - -function buildLabelFilters(columnsWithMeta: Record, logsFrame: LogsFrame) { - // Create object of label filters to filter out any columns not selected by the user +function buildLabelFilters(columnsWithMeta: Record) { + // Create object of label filters to include columns selected by the user let labelFilters: Record = {}; Object.keys(columnsWithMeta) - .filter((key) => !columnsWithMeta[key].active) + .filter((key) => columnsWithMeta[key].active) .forEach((key) => { labelFilters[key] = true; }); - // We could be getting fresh data - const uniqueLabels = new Set(); - const logFrameLabels = logsFrame?.getLogFrameLabelsAsLabels(); - - // Populate the set with all labels from latest dataframe - logFrameLabels?.forEach((labels) => { - Object.keys(labels).forEach((label) => { - uniqueLabels.add(label); - }); - }); - - // Check if there are labels in the data, that aren't yet in the labelFilters, and set them to be hidden by the transform - Object.keys(labelFilters).forEach((label) => { - if (!uniqueLabels.has(label)) { - labelFilters[label] = true; - } - }); - - // Check if there are labels in the label filters that aren't yet in the data, and set those to also be hidden - // The next time the column filters are synced any extras will be removed - Array.from(uniqueLabels).forEach((label) => { - if (label in columnsWithMeta && !columnsWithMeta[label]?.active) { - labelFilters[label] = true; - } else if (!labelFilters[label] && !(label in columnsWithMeta)) { - labelFilters[label] = true; - } - }); return labelFilters; } @@ -277,7 +231,7 @@ function getLabelFiltersTransform(labelFilters: Record) { return { id: 'organize', options: { - excludeByName: labelFilters, + includeByName: labelFilters, }, }; }