diff --git a/public/app/features/explore/Logs/Logs.test.tsx b/public/app/features/explore/Logs/Logs.test.tsx index d00412560ee..69f5c7f439b 100644 --- a/public/app/features/explore/Logs/Logs.test.tsx +++ b/public/app/features/explore/Logs/Logs.test.tsx @@ -61,6 +61,16 @@ jest.mock('../state/query', () => ({ }, })); +jest.mock('app/core/context/GrafanaContext', () => ({ + ...jest.requireActual('app/core/context/GrafanaContext'), + useGrafana: () => ({ + location: { + getSearchObject: jest.fn().mockReturnValue({}), + partial: jest.fn(), + }, + }), +})); + describe('Logs', () => { let originalHref = window.location.href; diff --git a/public/app/features/logs/components/otel/formats.ts b/public/app/features/logs/components/otel/formats.ts index c8d7b7e7c9e..23e6ec3559f 100644 --- a/public/app/features/logs/components/otel/formats.ts +++ b/public/app/features/logs/components/otel/formats.ts @@ -42,7 +42,10 @@ function getDisplayedFieldsForLanguages(logs: LogListModel[] | LogRowModel[], la }); return displayedFields.filter( - (field) => field === OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME || logs.some((log) => log.labels[field] !== undefined) + (field) => + field === LOG_LINE_BODY_FIELD_NAME || + field === OTEL_LOG_LINE_ATTRIBUTES_FIELD_NAME || + logs.some((log) => log.labels[field] !== undefined) ); } diff --git a/public/app/features/logs/components/panel/LogListContext.tsx b/public/app/features/logs/components/panel/LogListContext.tsx index 6533d3e0aa8..45933397b1e 100644 --- a/public/app/features/logs/components/panel/LogListContext.tsx +++ b/public/app/features/logs/components/panel/LogListContext.tsx @@ -308,9 +308,13 @@ export const LogListContextProvider = ({ // ['Time', 'detected_level', '___LOG_LINE_BODY___', '___OTEL_LOG_ATTRIBUTES___'] const defaultDisplayedFields = useMemo(() => { const orderedFields: string[] = tableDefaultFields; - // Always add LOG_LINE_BODY + + // Always add LOG_LINE_BODY before OTel fields orderedFields.push(LOG_LINE_BODY_FIELD_NAME); - orderedFields.push(...otelDisplayedFields); + + // Add OTel fields, excluding LOG_LINE_BODY_FIELD_NAME if it's already there to avoid duplicates + const otelFieldsWithoutBody = otelDisplayedFields.filter((field) => field !== LOG_LINE_BODY_FIELD_NAME); + orderedFields.push(...otelFieldsWithoutBody); return orderedFields; }, [tableDefaultFields, otelDisplayedFields]); diff --git a/public/app/features/logs/utils.ts b/public/app/features/logs/utils.ts index 763f6a3a942..0b1bac0704a 100644 --- a/public/app/features/logs/utils.ts +++ b/public/app/features/logs/utils.ts @@ -156,30 +156,34 @@ export const sortInDescendingOrder = (a: LogRowModel, b: LogRowModel) => { return 0; }; +export function sortLogRows(logRows: LogRowModel[], sortOrder: LogsSortOrder) { + return sortOrder === LogsSortOrder.Ascending + ? logRows.sort(sortInAscendingOrder) + : logRows.sort(sortInDescendingOrder); +} + export const sortLogsResult = (logsResult: LogsModel | null, sortOrder: LogsSortOrder): LogsModel => { const rows = logsResult ? sortLogRows(logsResult.rows, sortOrder) : []; return logsResult ? { ...logsResult, rows } : { hasUniqueLabels: false, rows }; }; -export const sortLogRows = (logRows: LogRowModel[], sortOrder: LogsSortOrder) => - sortOrder === LogsSortOrder.Ascending ? logRows.sort(sortInAscendingOrder) : logRows.sort(sortInDescendingOrder); - // Currently supports only error condition in Loki logs -export const checkLogsError = (logRow: LogRowModel): string | undefined => { +export function checkLogsError(logRow: LogRowModel): string | undefined { return logRow.labels.__error__; -}; +} -export const checkLogsSampled = (logRow: LogRowModel): string | undefined => { +export function checkLogsSampled(logRow: LogRowModel): string | undefined { if (!logRow.labels.__adaptive_logs_sampled__) { return undefined; } return logRow.labels.__adaptive_logs_sampled__ === 'true' ? 'Logs like this one have been dropped by Adaptive Logs' : `${logRow.labels.__adaptive_logs_sampled__}% of logs like this one have been dropped by Adaptive Logs`; -}; +} -export const escapeUnescapedString = (string: string) => - string.replace(/\\r\\n|\\n|\\t|\\r/g, (match: string) => (match.slice(1) === 't' ? '\t' : '\n')); +export function escapeUnescapedString(string: string) { + return string.replace(/\\r\\n|\\n|\\t|\\r/g, (match: string) => (match.slice(1) === 't' ? '\t' : '\n')); +} export function logRowsToReadableJson(logs: LogRowModel[], pickFields: string[] = []) { return logs.map((log) => {