Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: colored logs in new logs explorer #5749

Merged
merged 6 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: colored logs in new logs explorer
  • Loading branch information
vikrantgupta25 committed Aug 22, 2024
commit a51b0c7f80d238f224687f4f93b47b3b655ffdaa
23 changes: 21 additions & 2 deletions frontend/src/components/LogDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import './LogDetails.styles.scss';

import { Color, Spacing } from '@signozhq/design-tokens';
import Convert from 'ansi-to-html';
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
import { RadioChangeEvent } from 'antd/lib';
import cx from 'classnames';
Expand All @@ -10,8 +11,12 @@ import { LOCALSTORAGE } from 'constants/localStorage';
import ContextView from 'container/LogDetailedView/ContextView/ContextView';
import JSONView from 'container/LogDetailedView/JsonView';
import Overview from 'container/LogDetailedView/Overview';
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
import {
aggregateAttributesResourcesToString,
unescapeString,
} from 'container/LogDetailedView/utils';
import { useOptionsMenu } from 'container/OptionsMenu';
import dompurify from 'dompurify';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
Expand All @@ -28,11 +33,14 @@ import { useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';

import { VIEW_TYPES, VIEWS } from './constants';
import { LogDetailProps } from './LogDetail.interfaces';
import QueryBuilderSearchWrapper from './QueryBuilderSearchWrapper';

const convert = new Convert();

function LogDetail({
log,
onClose,
Expand Down Expand Up @@ -90,6 +98,17 @@ function LogDetail({
}
};

const htmlBody = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(unescapeString(log?.body || ''), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}),
[log?.body],
);

const handleJSONCopy = (): void => {
copyToClipboard(LogJsonData);
notifications.success({
Expand Down Expand Up @@ -128,7 +147,7 @@ function LogDetail({
<div className="log-detail-drawer__log">
<Divider type="vertical" className={cx('log-type-indicator', logType)} />
<Tooltip title={log?.body} placement="left">
<Typography.Text className="log-body">{log?.body}</Typography.Text>
<div className="log-body" dangerouslySetInnerHTML={htmlBody} />
YounixM marked this conversation as resolved.
Show resolved Hide resolved
</Tooltip>

<div className="log-overflow-shadow">&nbsp;</div>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Logs/ListLogView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Typography } from 'antd';
import cx from 'classnames';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import { unescapeString } from 'container/LogDetailedView/utils';
import { FontSize } from 'container/OptionsMenu/types';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
Expand Down Expand Up @@ -56,7 +57,7 @@ function LogGeneralField({
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(fieldValue, {
dompurify.sanitize(unescapeString(fieldValue), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/Logs/RawLogView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
import { unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
Expand Down Expand Up @@ -145,7 +146,9 @@ function RawLogView({
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(text, { FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS] }),
dompurify.sanitize(unescapeString(text), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}),
[text],
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/Logs/TableView/useTableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import cx from 'classnames';
import { unescapeString } from 'container/LogDetailedView/utils';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
import { useIsDarkMode } from 'hooks/useDarkMode';
Expand Down Expand Up @@ -115,7 +116,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
<TableBodyContent
dangerouslySetInnerHTML={{
__html: convert.toHtml(
dompurify.sanitize(field, {
dompurify.sanitize(unescapeString(field), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/container/LogDetailedView/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';

import { ActionItemProps } from './ActionItem';
import TableView from './TableView';
import { removeEscapeCharacters } from './utils';

interface OverviewProps {
logData: ILog;
Expand Down Expand Up @@ -124,7 +125,7 @@ function Overview({
children: (
<div className="logs-body-content">
<MEditor
value={logData.body}
value={removeEscapeCharacters(logData.body)}
language="json"
options={options}
onChange={(): void => {}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import './TableViewActions.styles.scss';

import { Color } from '@signozhq/design-tokens';
import Convert from 'ansi-to-html';
import { Button, Popover, Spin, Tooltip, Tree } from 'antd';
import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
import cx from 'classnames';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { OPERATORS } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import dompurify from 'dompurify';
import { isEmpty } from 'lodash-es';
import { ArrowDownToDot, ArrowUpFromDot, Ellipsis } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';

import { DataType } from '../TableView';
import {
filterKeyForField,
jsonToDataNodes,
recursiveParseJSON,
removeEscapeCharacters,
unescapeString,
} from '../utils';

interface ITableViewActionsProps {
Expand All @@ -39,6 +43,8 @@ interface ITableViewActionsProps {
) => () => void;
}

const convert = new Convert();

export function TableViewActions(
props: ITableViewActionsProps,
): React.ReactElement {
Expand Down Expand Up @@ -71,22 +77,43 @@ export function TableViewActions(
);
}
}
const bodyHtml =
record.field === 'body'
? {
__html: convert.toHtml(
dompurify.sanitize(unescapeString(record.value), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}
: { __html: '' };

const fieldFilterKey = filterKeyForField(fieldData.field);

return (
<div className={cx('value-field', isOpen ? 'open-popover' : '')}>
<CopyClipboardHOC textToCopy={textToCopy}>
{record.field === 'body' ? (
<span
style={{
color: Color.BG_SIENNA_400,
whiteSpace: 'pre-wrap',
tabSize: 4,
}}
>
{removeEscapeCharacters(fieldData.value)}
</span>
</CopyClipboardHOC>
dangerouslySetInnerHTML={bodyHtml}
/>
) : (
<CopyClipboardHOC textToCopy={textToCopy}>
<span
style={{
color: Color.BG_SIENNA_400,
whiteSpace: 'pre-wrap',
tabSize: 4,
}}
>
{removeEscapeCharacters(fieldData.value)}
</span>
</CopyClipboardHOC>
)}

{!isListViewPanel && (
<span className="action-btn">
Expand Down
34 changes: 22 additions & 12 deletions frontend/src/container/LogDetailedView/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,18 +251,28 @@ export const getDataTypes = (value: unknown): DataTypes => {
};

export const removeEscapeCharacters = (str: string): string =>
str.replace(/\\([ntfr'"\\])/g, (_: string, char: string) => {
const escapeMap: Record<string, string> = {
n: '\n',
t: '\t',
f: '\f',
r: '\r',
"'": "'",
'"': '"',
'\\': '\\',
};
return escapeMap[char as keyof typeof escapeMap];
});
str
.replace(/\\[btnfrv0'"\\]/g, '')
.replace(/\\x[0-9A-Fa-f]{2}/g, '')
.replace(/\\u[0-9A-Fa-f]{4}/g, '');

export const unescapeString = (str: string): string =>
YounixM marked this conversation as resolved.
Show resolved Hide resolved
str
.replace(/\\n/g, '\n') // Replaces escaped newlines
.replace(/\\r/g, '\r') // Replaces escaped carriage returns
.replace(/\\t/g, '\t') // Replaces escaped tabs
.replace(/\\b/g, '\b') // Replaces escaped backspaces
.replace(/\\f/g, '\f') // Replaces escaped form feeds
.replace(/\\v/g, '\v') // Replaces escaped vertical tabs
.replace(/\\'/g, "'") // Replaces escaped single quotes
.replace(/\\"/g, '"') // Replaces escaped double quotes
.replace(/\\\\/g, '\\') // Replaces escaped backslashes
.replace(/\\x([0-9A-Fa-f]{2})/g, (_, hex) =>
String.fromCharCode(parseInt(hex, 16)),
) // Replaces hexadecimal escape sequences
.replace(/\\u([0-9A-Fa-f]{4})/g, (_, hex) =>
String.fromCharCode(parseInt(hex, 16)),
); // Replaces Unicode escape sequences

export function removeExtraSpaces(input: string): string {
return input.replace(/\s+/g, ' ').trim();
Expand Down