Logs Panel: Add ISO8601 date to log download files (#102932)
* feat: add ISO8601 date to log download files
This commit is contained in:
@@ -273,7 +273,7 @@ describe('LogsMetaRow', () => {
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: ['1970-01-02T00:00:00Z'],
|
||||
values: [1],
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
@@ -315,6 +315,6 @@ describe('LogsMetaRow', () => {
|
||||
const blob = (saveAs as unknown as jest.Mock).mock.lastCall[0];
|
||||
expect(blob.type).toBe('text/csv;charset=utf-8');
|
||||
const text = await blob.text();
|
||||
expect(text).toBe(`"time","message bar"\r\n1970-01-02T00:00:00Z,INFO 1`);
|
||||
expect(text).toBe(`"Date","time","message bar"\r\n1970-01-01T00:00:00.001Z,1,INFO 1`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import saveAs from 'file-saver';
|
||||
import { memo } from 'react';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { lastValueFrom, map, Observable } from 'rxjs';
|
||||
|
||||
import {
|
||||
LogsDedupStrategy,
|
||||
@@ -15,6 +15,9 @@ import {
|
||||
CustomTransformOperator,
|
||||
Labels,
|
||||
DataFrame,
|
||||
Field,
|
||||
getTimeField,
|
||||
dateTime,
|
||||
} from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Dropdown, Menu, ToolbarButton, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
@@ -96,15 +99,18 @@ export const LogsMetaRow = memo(
|
||||
});
|
||||
dataFrameMap.forEach(async (dataFrame) => {
|
||||
const transforms: Array<DataTransformerConfig | CustomTransformOperator> = getLogsExtractFields(dataFrame);
|
||||
transforms.push({
|
||||
id: 'organize',
|
||||
options: {
|
||||
excludeByName: {
|
||||
['labels']: true,
|
||||
['labelTypes']: true,
|
||||
transforms.push(
|
||||
{
|
||||
id: 'organize',
|
||||
options: {
|
||||
excludeByName: {
|
||||
['labels']: true,
|
||||
['labelTypes']: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addISODateTransformation
|
||||
);
|
||||
const transformedDataFrame = await lastValueFrom(transformDataFrame(transforms, [dataFrame]));
|
||||
downloadDataFrameAsCsv(transformedDataFrame[0], `Explore-logs-${dataFrame.refId}`);
|
||||
});
|
||||
@@ -215,3 +221,23 @@ function renderMetaItem(value: string | number | Labels, kind: LogsMetaKind) {
|
||||
console.error(`Meta type ${typeof value} ${value} not recognized.`);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const addISODateTransformation: CustomTransformOperator = () => (source: Observable<DataFrame[]>) => {
|
||||
return source.pipe(
|
||||
map((data: DataFrame[]) => {
|
||||
return data.map((frame: DataFrame) => {
|
||||
const timeField = getTimeField(frame);
|
||||
return {
|
||||
...frame,
|
||||
fields: [
|
||||
{
|
||||
name: 'Date',
|
||||
values: timeField.timeField?.values.map((v) => dateTime(v).toISOString()),
|
||||
} as Field,
|
||||
...frame.fields,
|
||||
],
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ describe('inspector download', () => {
|
||||
rows: [{ timeEpochMs: 100, entry: 'testEntry' } as unknown as LogRowModel],
|
||||
},
|
||||
'test',
|
||||
`testLabel: 1\nsecondTestLabel: 2\n\n\n100\ttestEntry\n`,
|
||||
`testLabel: 1\nsecondTestLabel: 2\n\n\n100\t1970-01-01T00:00:00.100Z\ttestEntry\n`,
|
||||
],
|
||||
])('should, when logsModel is %s and title is %s, resolve in %s', async (logsModel, title, expected) => {
|
||||
downloadLogsModelAsTxt(logsModel, title);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
CSVConfig,
|
||||
DataFrame,
|
||||
DataTransformerID,
|
||||
dateTime,
|
||||
dateTimeFormat,
|
||||
LogsModel,
|
||||
MutableDataFrame,
|
||||
@@ -30,7 +31,7 @@ export function downloadLogsModelAsTxt(logsModel: Pick<LogsModel, 'meta' | 'rows
|
||||
textToDownload = textToDownload + '\n\n';
|
||||
|
||||
logsModel.rows.forEach((row) => {
|
||||
const newRow = row.timeEpochMs + '\t' + row.entry + '\n';
|
||||
const newRow = row.timeEpochMs + '\t' + dateTime(row.timeEpochMs).toISOString() + '\t' + row.entry + '\n';
|
||||
textToDownload = textToDownload + newRow;
|
||||
});
|
||||
|
||||
|
||||
@@ -356,13 +356,22 @@ describe('logRowsToReadableJson', () => {
|
||||
it('should format a single row', () => {
|
||||
const result = logRowsToReadableJson([testRow]);
|
||||
|
||||
expect(result).toEqual([{ line: 'test entry', timestamp: '123456789', fields: { foo: 'bar' } }]);
|
||||
expect(result).toEqual([
|
||||
{ date: '1970-01-01T00:00:00.010Z', line: 'test entry', timestamp: '123456789', fields: { foo: 'bar' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should format a df field row', () => {
|
||||
const result = logRowsToReadableJson([testRow2]);
|
||||
|
||||
expect(result).toEqual([{ line: 'test entry', timestamp: '123456789', fields: { foo: 'bar', foo2: 'bar2' } }]);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
date: '1970-01-01T00:00:00.010Z',
|
||||
line: 'test entry',
|
||||
timestamp: '123456789',
|
||||
fields: { foo: 'bar', foo2: 'bar2' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
getDefaultTimeRange,
|
||||
locationUtil,
|
||||
urlUtil,
|
||||
dateTime,
|
||||
} from '@grafana/data';
|
||||
import { getConfig } from 'app/core/config';
|
||||
|
||||
@@ -176,6 +177,7 @@ export function logRowsToReadableJson(logs: LogRowModel[]) {
|
||||
return {
|
||||
line: log.entry,
|
||||
timestamp: log.timeEpochNs,
|
||||
date: dateTime(log.timeEpochMs).toISOString(),
|
||||
fields: {
|
||||
...fields,
|
||||
...log.labels,
|
||||
|
||||
Reference in New Issue
Block a user