Logs: update tests, check for detected_level
This commit is contained in:
@@ -48,7 +48,7 @@ describe('LogsMetaRow', () => {
|
||||
});
|
||||
|
||||
it('renders the show original line button', () => {
|
||||
setup({ displayedFields: ['test'] });
|
||||
setup({ displayedFields: ['test'], defaultDisplayedFields: ['Time', 'detected_level', '___LOG_LINE_BODY___'] });
|
||||
expect(
|
||||
screen.getByRole('button', {
|
||||
name: 'Show original line',
|
||||
@@ -66,13 +66,20 @@ describe('LogsMetaRow', () => {
|
||||
});
|
||||
|
||||
it('renders the displayed fields', async () => {
|
||||
setup({ displayedFields: ['testField1234'] });
|
||||
setup({
|
||||
displayedFields: ['testField1234'],
|
||||
defaultDisplayedFields: ['Time', 'detected_level', '___LOG_LINE_BODY___'],
|
||||
});
|
||||
expect(await screen.findByText('testField1234')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a button to clear displayedfields', () => {
|
||||
const clearSpy = jest.fn();
|
||||
setup({ displayedFields: ['testField1234'], clearDisplayedFields: clearSpy });
|
||||
setup({
|
||||
displayedFields: ['testField1234'],
|
||||
defaultDisplayedFields: ['Time', 'detected_level', '___LOG_LINE_BODY___'],
|
||||
clearDisplayedFields: clearSpy,
|
||||
});
|
||||
fireEvent(
|
||||
screen.getByRole('button', {
|
||||
name: 'Show original line',
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('LogsTableWrap', () => {
|
||||
await waitFor(() => {
|
||||
expect(updatePanelState).toBeCalledWith({
|
||||
visualisationType: 'table',
|
||||
columns: { 0: 'app', 1: 'Line', 2: 'Time' },
|
||||
displayedFields: ['app', '___LOG_LINE_BODY___', 'Time'],
|
||||
labelFieldName: 'labels',
|
||||
});
|
||||
});
|
||||
@@ -97,7 +97,7 @@ describe('LogsTableWrap', () => {
|
||||
await waitFor(() => {
|
||||
expect(updatePanelState).toBeCalledWith({
|
||||
visualisationType: 'table',
|
||||
columns: { 0: 'Line', 1: 'Time' },
|
||||
displayedFields: ['___LOG_LINE_BODY___', 'Time'],
|
||||
labelFieldName: 'labels',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ export const LOG_LINE_BODY_FIELD_NAME = '___LOG_LINE_BODY___';
|
||||
export const TABLE_TIME_FIELD_NAME = 'Time';
|
||||
export const TABLE_LINE_FIELD_NAME = 'Line';
|
||||
export const TABLE_DETECTED_LEVEL_FIELD_NAME = 'detected_level';
|
||||
export const TABLE_LEVEL_FIELD_NAME = 'level';
|
||||
|
||||
export const LogDetailsBody = (props: Props) => {
|
||||
const showField = () => {
|
||||
|
||||
@@ -42,10 +42,7 @@ function getDisplayedFieldsForLanguages(logs: LogListModel[] | LogRowModel[], la
|
||||
});
|
||||
|
||||
return displayedFields.filter(
|
||||
(field) =>
|
||||
field === LOG_LINE_BODY_FIELD_NAME ||
|
||||
field === OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME ||
|
||||
logs.some((log) => log.labels[field] !== undefined)
|
||||
(field) => field === OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME || logs.some((log) => log.labels[field] !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { disablePopoverMenu, enablePopoverMenu, isPopoverMenuDisabled } from '../../utils';
|
||||
import { LOG_LINE_BODY_FIELD_NAME } from '../LogDetailsBody';
|
||||
import { LOG_LINE_BODY_FIELD_NAME, TABLE_TIME_FIELD_NAME, TABLE_DETECTED_LEVEL_FIELD_NAME } from '../LogDetailsBody';
|
||||
import { createLogLine, createLogRow } from '../mocks/logRow';
|
||||
import { OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME, OTEL_PROBE_FIELD } from '../otel/formats';
|
||||
|
||||
@@ -129,8 +129,18 @@ describe('LogList', () => {
|
||||
<LogList {...defaultProps} onLogOptionsChange={onLogOptionsChange} setDisplayedFields={setDisplayedFields} />
|
||||
);
|
||||
expect(screen.getByText('log message 1')).toBeInTheDocument();
|
||||
expect(onLogOptionsChange).not.toHaveBeenCalled();
|
||||
expect(setDisplayedFields).not.toHaveBeenCalled();
|
||||
// Even when OTel is disabled, we still report table defaults
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('defaultDisplayedFields', [
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
]);
|
||||
// setDisplayedFields is called with the default fields
|
||||
expect(setDisplayedFields).toHaveBeenCalledWith([
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
]);
|
||||
|
||||
config.featureToggles.otelLogsFormatting = originalState;
|
||||
});
|
||||
@@ -144,10 +154,19 @@ describe('LogList', () => {
|
||||
<LogList {...defaultProps} onLogOptionsChange={onLogOptionsChange} setDisplayedFields={setDisplayedFields} />
|
||||
);
|
||||
expect(screen.getByText('log message 1')).toBeInTheDocument();
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('defaultDisplayedFields', []);
|
||||
// For non-OTel logs, we report table defaults only (no OTel attributes field)
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('defaultDisplayedFields', [
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
]);
|
||||
|
||||
// No fields to display, no call
|
||||
expect(setDisplayedFields).not.toHaveBeenCalled();
|
||||
// setDisplayedFields is called with the default fields
|
||||
expect(setDisplayedFields).toHaveBeenCalledWith([
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
]);
|
||||
|
||||
config.featureToggles.otelLogsFormatting = originalState;
|
||||
});
|
||||
@@ -168,11 +187,19 @@ describe('LogList', () => {
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('log message 1')).toBeInTheDocument();
|
||||
// For OTel logs, we report table defaults + OTel fields
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('defaultDisplayedFields', [
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME,
|
||||
]);
|
||||
expect(setDisplayedFields).toHaveBeenCalledWith([
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME,
|
||||
]);
|
||||
expect(setDisplayedFields).toHaveBeenCalledWith([LOG_LINE_BODY_FIELD_NAME, OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME]);
|
||||
|
||||
config.featureToggles.otelLogsFormatting = originalState;
|
||||
});
|
||||
|
||||
@@ -27,9 +27,14 @@ import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { PopoverContent } from '@grafana/ui';
|
||||
|
||||
import { checkLogsError, checkLogsSampled, downloadLogs as download, DownloadFormat } from '../../utils';
|
||||
import { LOG_LINE_BODY_FIELD_NAME, TABLE_TIME_FIELD_NAME, TABLE_DETECTED_LEVEL_FIELD_NAME } from '../LogDetailsBody';
|
||||
import {
|
||||
LOG_LINE_BODY_FIELD_NAME,
|
||||
TABLE_TIME_FIELD_NAME,
|
||||
TABLE_DETECTED_LEVEL_FIELD_NAME,
|
||||
TABLE_LEVEL_FIELD_NAME,
|
||||
} from '../LogDetailsBody';
|
||||
import { getFieldSelectorState } from '../fieldSelector/FieldSelector';
|
||||
import { OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME, getDisplayedFieldsForLogs } from '../otel/formats';
|
||||
import { getDisplayedFieldsForLogs } from '../otel/formats';
|
||||
|
||||
import { getDefaultDetailsMode, getDetailsWidth } from './LogDetailsContext';
|
||||
import { LogLineTimestampResolution } from './LogLine';
|
||||
@@ -121,11 +126,23 @@ export const useLogIsPermalinked = (log: LogListModel) => {
|
||||
|
||||
/**
|
||||
* Get default table fields.
|
||||
* Always returns the table field constants: Time and detected_level (excluding Line).
|
||||
* Always returns Time, and detected_level if it exists in the logs (excluding Line).
|
||||
*/
|
||||
function getTableDefaultFields(logs: LogRowModel[]): string[] {
|
||||
// Always return the table field constants (Time and detected_level, excluding Line)
|
||||
return [TABLE_TIME_FIELD_NAME, TABLE_DETECTED_LEVEL_FIELD_NAME];
|
||||
const fields: string[] = [TABLE_TIME_FIELD_NAME];
|
||||
|
||||
// Check if detected_level exists in any log's labels, fall back to level if not found
|
||||
const hasDetectedLevel = logs.some((log) => log.labels?.[TABLE_DETECTED_LEVEL_FIELD_NAME] !== undefined);
|
||||
const hasLevel = !hasDetectedLevel && logs.some((log) => log.labels?.[TABLE_LEVEL_FIELD_NAME] !== undefined);
|
||||
|
||||
if (hasDetectedLevel) {
|
||||
fields.push(TABLE_DETECTED_LEVEL_FIELD_NAME);
|
||||
} else if (hasLevel) {
|
||||
// Fall back to level if detected_level is not present
|
||||
fields.push(TABLE_LEVEL_FIELD_NAME);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
export type LogListState = Pick<
|
||||
@@ -273,11 +290,14 @@ export const LogListContextProvider = ({
|
||||
}, []);
|
||||
|
||||
const otelDisplayedFields = useMemo(() => {
|
||||
if (!config.featureToggles.otelLogsFormatting || !setDisplayedFields || showLogAttributes === false) {
|
||||
if (!config.featureToggles.otelLogsFormatting) {
|
||||
return [];
|
||||
}
|
||||
if (showLogAttributes === false) {
|
||||
return [];
|
||||
}
|
||||
return getDisplayedFieldsForLogs(logs);
|
||||
}, [logs, setDisplayedFields, showLogAttributes]);
|
||||
}, [logs, showLogAttributes]);
|
||||
|
||||
// Get table default fields
|
||||
const tableDefaultFields = useMemo(() => {
|
||||
@@ -287,34 +307,10 @@ export const LogListContextProvider = ({
|
||||
// Combine table defaults with OTel defaults in specific order:
|
||||
// ['Time', 'detected_level', '___LOG_LINE_BODY___', '___OTEL_LOG_ATTRIBUTES___']
|
||||
const defaultDisplayedFields = useMemo(() => {
|
||||
const orderedFields: string[] = [];
|
||||
|
||||
// 1. Add Time from table defaults
|
||||
if (tableDefaultFields.includes(TABLE_TIME_FIELD_NAME)) {
|
||||
orderedFields.push(TABLE_TIME_FIELD_NAME);
|
||||
}
|
||||
|
||||
// 2. Add detected_level from table defaults
|
||||
if (tableDefaultFields.includes(TABLE_DETECTED_LEVEL_FIELD_NAME)) {
|
||||
orderedFields.push(TABLE_DETECTED_LEVEL_FIELD_NAME);
|
||||
}
|
||||
|
||||
// 3. Always add LOG_LINE_BODY
|
||||
const orderedFields: string[] = tableDefaultFields;
|
||||
// Always add LOG_LINE_BODY
|
||||
orderedFields.push(LOG_LINE_BODY_FIELD_NAME);
|
||||
|
||||
// 4. Always add OTEL_LOG_ATTRIBUTES
|
||||
orderedFields.push(OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME);
|
||||
|
||||
// 5. Add any other OTel fields that aren't already included
|
||||
otelDisplayedFields.forEach((field) => {
|
||||
if (
|
||||
field !== LOG_LINE_BODY_FIELD_NAME &&
|
||||
field !== OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME &&
|
||||
!orderedFields.includes(field)
|
||||
) {
|
||||
orderedFields.push(field);
|
||||
}
|
||||
});
|
||||
orderedFields.push(...otelDisplayedFields);
|
||||
|
||||
return orderedFields;
|
||||
}, [tableDefaultFields, otelDisplayedFields]);
|
||||
|
||||
@@ -223,6 +223,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
await userEvent.click(screen.getByLabelText(OLDEST_LOGS_LABEL_REGEX));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('sortOrder', LogsSortOrder.Descending);
|
||||
@@ -235,6 +237,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
await userEvent.click(screen.getByLabelText(DEDUPE_LABEL_COPY));
|
||||
await userEvent.click(screen.getByText('Numbers'));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
|
||||
@@ -286,6 +290,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
await userEvent.click(screen.getByLabelText(SHOW_TIMESTAMP_LABEL_COPY));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('showTime', true);
|
||||
@@ -298,6 +304,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
await userEvent.click(screen.getByLabelText(WRAP_LINES_LABEL_COPY));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('wrapLogMessage', true);
|
||||
@@ -318,6 +326,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Wrap disabled'));
|
||||
await userEvent.click(screen.getByText('Enable line wrapping'));
|
||||
@@ -353,6 +363,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
|
||||
await userEvent.click(screen.getByLabelText(TIMESTAMP_LABEL_COPY));
|
||||
await userEvent.click(screen.getByText('Show millisecond timestamps'));
|
||||
@@ -381,6 +393,8 @@ describe('LogListControls', () => {
|
||||
<LogListControls eventBus={new EventBusSrv()} />
|
||||
</LogListContextProvider>
|
||||
);
|
||||
// Clear any initial calls (e.g., from defaultDisplayedFields)
|
||||
onLogOptionsChange.mockClear();
|
||||
await userEvent.click(screen.getByLabelText(ENABLE_HIGHLIGHTING_LABEL_COPY));
|
||||
expect(onLogOptionsChange).toHaveBeenCalledTimes(1);
|
||||
expect(onLogOptionsChange).toHaveBeenCalledWith('syntaxHighlighting', true);
|
||||
|
||||
Reference in New Issue
Block a user