mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-29 01:52:01 +08:00
feat: show/hide timestamp and body fields in logs explorer (raw, default, column views) (#6831)
* feat: show/hide timestamp and body fields in logs explorer (raw, default, column views) * fix: add width to log indicator column to ensure that a single column doesn't take half the space --------- Co-authored-by: Nityananda Gohain <nityanandagohain@gmail.com>
This commit is contained in:
parent
e00e365964
commit
b465f74e4a
@ -219,12 +219,14 @@ function ListLogView({
|
|||||||
<LogStateIndicator type={logType} fontSize={fontSize} />
|
<LogStateIndicator type={logType} fontSize={fontSize} />
|
||||||
<div>
|
<div>
|
||||||
<LogContainer fontSize={fontSize}>
|
<LogContainer fontSize={fontSize}>
|
||||||
<LogGeneralField
|
{updatedSelecedFields.some((field) => field.name === 'body') && (
|
||||||
fieldKey="Log"
|
<LogGeneralField
|
||||||
fieldValue={flattenLogData.body}
|
fieldKey="Log"
|
||||||
linesPerRow={linesPerRow}
|
fieldValue={flattenLogData.body}
|
||||||
fontSize={fontSize}
|
linesPerRow={linesPerRow}
|
||||||
/>
|
fontSize={fontSize}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{flattenLogData.stream && (
|
{flattenLogData.stream && (
|
||||||
<LogGeneralField
|
<LogGeneralField
|
||||||
fieldKey="Stream"
|
fieldKey="Stream"
|
||||||
@ -232,23 +234,27 @@ function ListLogView({
|
|||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<LogGeneralField
|
{updatedSelecedFields.some((field) => field.name === 'timestamp') && (
|
||||||
fieldKey="Timestamp"
|
<LogGeneralField
|
||||||
fieldValue={timestampValue}
|
fieldKey="Timestamp"
|
||||||
fontSize={fontSize}
|
fieldValue={timestampValue}
|
||||||
/>
|
fontSize={fontSize}
|
||||||
|
/>
|
||||||
{updatedSelecedFields.map((field) =>
|
|
||||||
isValidLogField(flattenLogData[field.name] as never) ? (
|
|
||||||
<LogSelectedField
|
|
||||||
key={field.name}
|
|
||||||
fieldKey={field.name}
|
|
||||||
fieldValue={flattenLogData[field.name] as never}
|
|
||||||
onAddToQuery={onAddToQuery}
|
|
||||||
fontSize={fontSize}
|
|
||||||
/>
|
|
||||||
) : null,
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{updatedSelecedFields
|
||||||
|
.filter((field) => !['timestamp', 'body'].includes(field.name))
|
||||||
|
.map((field) =>
|
||||||
|
isValidLogField(flattenLogData[field.name] as never) ? (
|
||||||
|
<LogSelectedField
|
||||||
|
key={field.name}
|
||||||
|
fieldKey={field.name}
|
||||||
|
fieldValue={flattenLogData[field.name] as never}
|
||||||
|
onAddToQuery={onAddToQuery}
|
||||||
|
fontSize={fontSize}
|
||||||
|
/>
|
||||||
|
) : null,
|
||||||
|
)}
|
||||||
</LogContainer>
|
</LogContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,6 +73,7 @@ function RawLogView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const attributesValues = updatedSelecedFields
|
const attributesValues = updatedSelecedFields
|
||||||
|
.filter((field) => !['timestamp', 'body'].includes(field.name))
|
||||||
.map((field) => flattenLogData[field.name])
|
.map((field) => flattenLogData[field.name])
|
||||||
.filter((attribute) => {
|
.filter((attribute) => {
|
||||||
// loadash isEmpty doesnot work with numbers
|
// loadash isEmpty doesnot work with numbers
|
||||||
@ -92,19 +93,40 @@ function RawLogView({
|
|||||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||||
|
|
||||||
const text = useMemo(() => {
|
const text = useMemo(() => {
|
||||||
const date =
|
const parts = [];
|
||||||
typeof data.timestamp === 'string'
|
|
||||||
? formatTimezoneAdjustedTimestamp(data.timestamp, 'YYYY-MM-DD HH:mm:ss.SSS')
|
|
||||||
: formatTimezoneAdjustedTimestamp(
|
|
||||||
data.timestamp / 1e6,
|
|
||||||
'YYYY-MM-DD HH:mm:ss.SSS',
|
|
||||||
);
|
|
||||||
|
|
||||||
return `${date} | ${attributesText} ${data.body}`;
|
// Check if timestamp is selected
|
||||||
|
const showTimestamp = selectedFields.some(
|
||||||
|
(field) => field.name === 'timestamp',
|
||||||
|
);
|
||||||
|
if (showTimestamp) {
|
||||||
|
const date =
|
||||||
|
typeof data.timestamp === 'string'
|
||||||
|
? formatTimezoneAdjustedTimestamp(
|
||||||
|
data.timestamp,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS',
|
||||||
|
)
|
||||||
|
: formatTimezoneAdjustedTimestamp(
|
||||||
|
data.timestamp / 1e6,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS',
|
||||||
|
);
|
||||||
|
parts.push(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if body is selected
|
||||||
|
const showBody = selectedFields.some((field) => field.name === 'body');
|
||||||
|
if (showBody) {
|
||||||
|
parts.push(`${attributesText} ${data.body}`);
|
||||||
|
} else {
|
||||||
|
parts.push(attributesText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join(' | ');
|
||||||
}, [
|
}, [
|
||||||
|
selectedFields,
|
||||||
|
attributesText,
|
||||||
data.timestamp,
|
data.timestamp,
|
||||||
data.body,
|
data.body,
|
||||||
attributesText,
|
|
||||||
formatTimezoneAdjustedTimestamp,
|
formatTimezoneAdjustedTimestamp,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
|
|
||||||
const columns: ColumnsType<Record<string, unknown>> = useMemo(() => {
|
const columns: ColumnsType<Record<string, unknown>> = useMemo(() => {
|
||||||
const fieldColumns: ColumnsType<Record<string, unknown>> = fields
|
const fieldColumns: ColumnsType<Record<string, unknown>> = fields
|
||||||
.filter((e) => e.name !== 'id')
|
.filter((e) => !['id', 'body', 'timestamp'].includes(e.name))
|
||||||
.map(({ name }) => ({
|
.map(({ name }) => ({
|
||||||
title: name,
|
title: name,
|
||||||
dataIndex: name,
|
dataIndex: name,
|
||||||
@ -91,55 +91,67 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
...(fields.some((field) => field.name === 'timestamp')
|
||||||
title: 'timestamp',
|
? [
|
||||||
dataIndex: 'timestamp',
|
{
|
||||||
key: 'timestamp',
|
title: 'timestamp',
|
||||||
// https://github.com/ant-design/ant-design/discussions/36886
|
dataIndex: 'timestamp',
|
||||||
render: (field): ColumnTypeRender<Record<string, unknown>> => {
|
key: 'timestamp',
|
||||||
const date =
|
// https://github.com/ant-design/ant-design/discussions/36886
|
||||||
typeof field === 'string'
|
render: (
|
||||||
? formatTimezoneAdjustedTimestamp(field, 'YYYY-MM-DD HH:mm:ss.SSS')
|
field: string | number,
|
||||||
: formatTimezoneAdjustedTimestamp(
|
): ColumnTypeRender<Record<string, unknown>> => {
|
||||||
field / 1e6,
|
const date =
|
||||||
'YYYY-MM-DD HH:mm:ss.SSS',
|
typeof field === 'string'
|
||||||
);
|
? formatTimezoneAdjustedTimestamp(field, 'YYYY-MM-DD HH:mm:ss.SSS')
|
||||||
return {
|
: formatTimezoneAdjustedTimestamp(
|
||||||
children: (
|
field / 1e6,
|
||||||
<div className="table-timestamp">
|
'YYYY-MM-DD HH:mm:ss.SSS',
|
||||||
<Typography.Paragraph ellipsis className={cx('text', fontSize)}>
|
);
|
||||||
{date}
|
return {
|
||||||
</Typography.Paragraph>
|
children: (
|
||||||
</div>
|
<div className="table-timestamp">
|
||||||
),
|
<Typography.Paragraph ellipsis className={cx('text', fontSize)}>
|
||||||
};
|
{date}
|
||||||
},
|
</Typography.Paragraph>
|
||||||
},
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
...(appendTo === 'center' ? fieldColumns : []),
|
...(appendTo === 'center' ? fieldColumns : []),
|
||||||
{
|
...(fields.some((field) => field.name === 'body')
|
||||||
title: 'body',
|
? [
|
||||||
dataIndex: 'body',
|
{
|
||||||
key: 'body',
|
title: 'body',
|
||||||
render: (field): ColumnTypeRender<Record<string, unknown>> => ({
|
dataIndex: 'body',
|
||||||
props: {
|
key: 'body',
|
||||||
style: defaultTableStyle,
|
render: (
|
||||||
},
|
field: string | number,
|
||||||
children: (
|
): ColumnTypeRender<Record<string, unknown>> => ({
|
||||||
<TableBodyContent
|
props: {
|
||||||
dangerouslySetInnerHTML={{
|
style: defaultTableStyle,
|
||||||
__html: convert.toHtml(
|
},
|
||||||
dompurify.sanitize(unescapeString(field), {
|
children: (
|
||||||
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
<TableBodyContent
|
||||||
}),
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: convert.toHtml(
|
||||||
|
dompurify.sanitize(unescapeString(field as string), {
|
||||||
|
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
fontSize={fontSize}
|
||||||
|
linesPerRow={linesPerRow}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}),
|
||||||
fontSize={fontSize}
|
},
|
||||||
linesPerRow={linesPerRow}
|
]
|
||||||
isDarkMode={isDarkMode}
|
: []),
|
||||||
/>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
...(appendTo === 'end' ? fieldColumns : []),
|
...(appendTo === 'end' ? fieldColumns : []),
|
||||||
];
|
];
|
||||||
}, [
|
}, [
|
||||||
|
@ -121,23 +121,25 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
|||||||
const tableHeader = useCallback(
|
const tableHeader = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<tr>
|
<tr>
|
||||||
{tableColumns.map((column) => {
|
{tableColumns
|
||||||
const isDragColumn = column.key !== 'expand';
|
.filter((column) => column.key)
|
||||||
|
.map((column) => {
|
||||||
|
const isDragColumn = column.key !== 'expand';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHeaderCellStyled
|
<TableHeaderCellStyled
|
||||||
$isLogIndicator={column.key === 'state-indicator'}
|
$isLogIndicator={column.key === 'state-indicator'}
|
||||||
$isDarkMode={isDarkMode}
|
$isDarkMode={isDarkMode}
|
||||||
$isDragColumn={isDragColumn}
|
$isDragColumn={isDragColumn}
|
||||||
key={column.key}
|
key={column.key}
|
||||||
fontSize={tableViewProps?.fontSize}
|
fontSize={tableViewProps?.fontSize}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...(isDragColumn && { className: 'dragHandler' })}
|
{...(isDragColumn && { className: 'dragHandler' })}
|
||||||
>
|
>
|
||||||
{(column.title as string).replace(/^\w/, (c) => c.toUpperCase())}
|
{(column.title as string).replace(/^\w/, (c) => c.toUpperCase())}
|
||||||
</TableHeaderCellStyled>
|
</TableHeaderCellStyled>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
[tableColumns, isDarkMode, tableViewProps?.fontSize],
|
[tableColumns, isDarkMode, tableViewProps?.fontSize],
|
||||||
|
@ -29,7 +29,7 @@ export const TableCellStyled = styled.td<TableHeaderCellStyledProps>`
|
|||||||
props.$isDarkMode ? 'inherit' : themeColors.whiteCream};
|
props.$isDarkMode ? 'inherit' : themeColors.whiteCream};
|
||||||
|
|
||||||
${({ $isLogIndicator }): string =>
|
${({ $isLogIndicator }): string =>
|
||||||
$isLogIndicator ? 'padding: 0 0 0 8px;' : ''}
|
$isLogIndicator ? 'padding: 0 0 0 8px;width: 15px;' : ''}
|
||||||
color: ${(props): string =>
|
color: ${(props): string =>
|
||||||
props.$isDarkMode ? themeColors.white : themeColors.bckgGrey};
|
props.$isDarkMode ? themeColors.white : themeColors.bckgGrey};
|
||||||
`;
|
`;
|
||||||
|
@ -5,7 +5,26 @@ import { FontSize, OptionsQuery } from './types';
|
|||||||
export const URL_OPTIONS = 'options';
|
export const URL_OPTIONS = 'options';
|
||||||
|
|
||||||
export const defaultOptionsQuery: OptionsQuery = {
|
export const defaultOptionsQuery: OptionsQuery = {
|
||||||
selectColumns: [],
|
selectColumns: [
|
||||||
|
{
|
||||||
|
key: 'timestamp',
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
id: 'timestamp--string--tag--true',
|
||||||
|
isIndexed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'body',
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
id: 'body--string--tag--true',
|
||||||
|
isIndexed: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
format: 'raw',
|
format: 'raw',
|
||||||
fontSize: FontSize.SMALL,
|
fontSize: FontSize.SMALL,
|
||||||
|
@ -169,6 +169,15 @@ const useOptionsMenu = ({
|
|||||||
|
|
||||||
const searchedAttributeKeys = useMemo(() => {
|
const searchedAttributeKeys = useMemo(() => {
|
||||||
if (searchedAttributesData?.payload?.attributeKeys?.length) {
|
if (searchedAttributesData?.payload?.attributeKeys?.length) {
|
||||||
|
if (dataSource === DataSource.LOGS) {
|
||||||
|
// add timestamp and body to the list of attributes
|
||||||
|
return [
|
||||||
|
...defaultOptionsQuery.selectColumns,
|
||||||
|
...searchedAttributesData.payload.attributeKeys.filter(
|
||||||
|
(attribute) => attribute.key !== 'body',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
return searchedAttributesData.payload.attributeKeys;
|
return searchedAttributesData.payload.attributeKeys;
|
||||||
}
|
}
|
||||||
if (dataSource === DataSource.TRACES) {
|
if (dataSource === DataSource.TRACES) {
|
||||||
@ -198,12 +207,17 @@ const useOptionsMenu = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const optionsFromAttributeKeys = useMemo(() => {
|
const optionsFromAttributeKeys = useMemo(() => {
|
||||||
const filteredAttributeKeys = searchedAttributeKeys.filter(
|
const filteredAttributeKeys = searchedAttributeKeys.filter((item) => {
|
||||||
(item) => item.key !== 'body',
|
// For other data sources, only filter out 'body' if it exists
|
||||||
);
|
if (dataSource !== DataSource.LOGS) {
|
||||||
|
return item.key !== 'body';
|
||||||
|
}
|
||||||
|
// For LOGS, keep all keys
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
return getOptionsFromKeys(filteredAttributeKeys, selectedColumnKeys);
|
return getOptionsFromKeys(filteredAttributeKeys, selectedColumnKeys);
|
||||||
}, [searchedAttributeKeys, selectedColumnKeys]);
|
}, [dataSource, searchedAttributeKeys, selectedColumnKeys]);
|
||||||
|
|
||||||
const handleRedirectWithOptionsData = useCallback(
|
const handleRedirectWithOptionsData = useCallback(
|
||||||
(newQueryData: OptionsQuery) => {
|
(newQueryData: OptionsQuery) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user