mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-08 17:39:06 +08:00
FE: Log Detail View: Support for dots in log attributes (#4252)
* chore: add support for showing attrib names containing dots in log details * chore: add support for field names with dot in filter actions * chore: minor cleanup * chore: add support for filtering by clicking fields with dots in log details
This commit is contained in:
parent
f8bb42a13c
commit
43d5ee9651
@ -22,6 +22,7 @@ import { ILog } from 'types/api/logs/log';
|
|||||||
import ActionItem, { ActionItemProps } from './ActionItem';
|
import ActionItem, { ActionItemProps } from './ActionItem';
|
||||||
import FieldRenderer from './FieldRenderer';
|
import FieldRenderer from './FieldRenderer';
|
||||||
import {
|
import {
|
||||||
|
filterKeyForField,
|
||||||
flattenObject,
|
flattenObject,
|
||||||
jsonToDataNodes,
|
jsonToDataNodes,
|
||||||
recursiveParseJSON,
|
recursiveParseJSON,
|
||||||
@ -98,11 +99,12 @@ function TableView({
|
|||||||
title: 'Action',
|
title: 'Action',
|
||||||
width: 11,
|
width: 11,
|
||||||
render: (fieldData: Record<string, string>): JSX.Element | null => {
|
render: (fieldData: Record<string, string>): JSX.Element | null => {
|
||||||
const fieldKey = fieldData.field.split('.').slice(-1);
|
const fieldFilterKey = filterKeyForField(fieldData.field);
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
|
||||||
|
if (!RESTRICTED_FIELDS.includes(fieldFilterKey)) {
|
||||||
return (
|
return (
|
||||||
<ActionItem
|
<ActionItem
|
||||||
fieldKey={fieldKey[0]}
|
fieldKey={fieldFilterKey}
|
||||||
fieldValue={fieldData.value}
|
fieldValue={fieldData.value}
|
||||||
onClickActionItem={onClickActionItem}
|
onClickActionItem={onClickActionItem}
|
||||||
/>
|
/>
|
||||||
@ -119,7 +121,6 @@ function TableView({
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (field: string, record): JSX.Element => {
|
render: (field: string, record): JSX.Element => {
|
||||||
const fieldKey = field.split('.').slice(-1);
|
|
||||||
const renderedField = <FieldRenderer field={field} />;
|
const renderedField = <FieldRenderer field={field} />;
|
||||||
|
|
||||||
if (record.field === 'trace_id') {
|
if (record.field === 'trace_id') {
|
||||||
@ -148,10 +149,11 @@ function TableView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
const fieldFilterKey = filterKeyForField(field);
|
||||||
|
if (!RESTRICTED_FIELDS.includes(fieldFilterKey)) {
|
||||||
return (
|
return (
|
||||||
<AddToQueryHOC
|
<AddToQueryHOC
|
||||||
fieldKey={fieldKey[0]}
|
fieldKey={fieldFilterKey}
|
||||||
fieldValue={flattenLogData[field]}
|
fieldValue={flattenLogData[field]}
|
||||||
onAddToQuery={onAddToQuery}
|
onAddToQuery={onAddToQuery}
|
||||||
>
|
>
|
||||||
|
@ -132,6 +132,16 @@ export const generateFieldKeyForArray = (
|
|||||||
export const removeObjectFromString = (str: string): string =>
|
export const removeObjectFromString = (str: string): string =>
|
||||||
str.replace(/\[object Object\]./g, '');
|
str.replace(/\[object Object\]./g, '');
|
||||||
|
|
||||||
|
// Split `str` on the first occurrence of `delimiter`
|
||||||
|
// For example, will return `['a', 'b.c']` when splitting `'a.b.c'` at dots
|
||||||
|
const splitOnce = (str: string, delimiter: string): string[] => {
|
||||||
|
const parts = str.split(delimiter);
|
||||||
|
if (parts.length < 2) {
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
return [parts[0], parts.slice(1).join(delimiter)];
|
||||||
|
};
|
||||||
|
|
||||||
export const getFieldAttributes = (field: string): IFieldAttributes => {
|
export const getFieldAttributes = (field: string): IFieldAttributes => {
|
||||||
let dataType;
|
let dataType;
|
||||||
let newField;
|
let newField;
|
||||||
@ -140,18 +150,30 @@ export const getFieldAttributes = (field: string): IFieldAttributes => {
|
|||||||
if (field.startsWith('attributes_')) {
|
if (field.startsWith('attributes_')) {
|
||||||
logType = MetricsType.Tag;
|
logType = MetricsType.Tag;
|
||||||
const stringWithoutPrefix = field.slice('attributes_'.length);
|
const stringWithoutPrefix = field.slice('attributes_'.length);
|
||||||
const parts = stringWithoutPrefix.split('.');
|
const parts = splitOnce(stringWithoutPrefix, '.');
|
||||||
[dataType, newField] = parts;
|
[dataType, newField] = parts;
|
||||||
} else if (field.startsWith('resources_')) {
|
} else if (field.startsWith('resources_')) {
|
||||||
logType = MetricsType.Resource;
|
logType = MetricsType.Resource;
|
||||||
const stringWithoutPrefix = field.slice('resources_'.length);
|
const stringWithoutPrefix = field.slice('resources_'.length);
|
||||||
const parts = stringWithoutPrefix.split('.');
|
const parts = splitOnce(stringWithoutPrefix, '.');
|
||||||
[dataType, newField] = parts;
|
[dataType, newField] = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { dataType, newField, logType };
|
return { dataType, newField, logType };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns key to be used when filtering for `field` via
|
||||||
|
// the query builder. This is useful for powering filtering
|
||||||
|
// by field values from log details view.
|
||||||
|
export const filterKeyForField = (field: string): string => {
|
||||||
|
// Must work for all 3 of the following types of cases
|
||||||
|
// timestamp -> timestamp
|
||||||
|
// attributes_string.log.file -> log.file
|
||||||
|
// resources_string.k8s.pod.name -> k8s.pod.name
|
||||||
|
const fieldAttribs = getFieldAttributes(field);
|
||||||
|
return fieldAttribs?.newField || field;
|
||||||
|
};
|
||||||
|
|
||||||
export const aggregateAttributesResourcesToString = (logData: ILog): string => {
|
export const aggregateAttributesResourcesToString = (logData: ILog): string => {
|
||||||
const outputJson: ILogAggregateAttributesResources = {
|
const outputJson: ILogAggregateAttributesResources = {
|
||||||
body: logData.body,
|
body: logData.body,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user