From cd350a9d436b1203619ded9e8ada4d0ec933fb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Zugmeyer?= Date: Thu, 3 Aug 2023 19:01:24 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20better=20event=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/tabs/eventsTab/eventRow.tsx | 162 ++++++++++++++---- developer-extension/src/panel/formatNumber.ts | 4 + 2 files changed, 132 insertions(+), 34 deletions(-) diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx index 42b4aa86ff..745367320c 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx @@ -1,8 +1,18 @@ -import { Badge } from '@mantine/core' +import { Badge, Box } from '@mantine/core' +import type { ReactNode } from 'react' import React, { useRef, useState } from 'react' +import type { TelemetryEvent } from '../../../../../../packages/core/src/domain/telemetry' +import type { LogsEvent } from '../../../../../../packages/logs/src/logsEvent.types' +import type { + RumActionEvent, + RumErrorEvent, + RumLongTaskEvent, + RumResourceEvent, + RumViewEvent, +} from '../../../../../../packages/rum-core/src/rumEvent.types' import type { SdkEvent } from '../../../sdkEvent' -import { isTelemetryEvent, isRumEvent } from '../../../sdkEvent' -import { safeTruncate } from '../../../../../../packages/core/src/tools/utils/stringUtils' +import { isTelemetryEvent, isLogEvent, isRumEvent } from '../../../sdkEvent' +import { formatDuration } from '../../../formatNumber' import { Json } from '../../json' import { LazyCollapse } from '../../lazyCollapse' @@ -22,6 +32,19 @@ const LOG_STATUS_COLOR = { debug: 'cyan', } +const RESOURCE_TYPE_LABELS: Record = { + xhr: 'XHR', + fetch: 'Fetch', + document: 'Document', + beacon: 'Beacon', + css: 'CSS', + js: 'JS', + image: 'Image', + font: 'Font', + media: 'Media', + other: 'Other', +} + export function EventRow({ event }: { event: SdkEvent }) { const [isCollapsed, setIsCollapsed] = useState(true) const collapseRef = useRef(null) @@ -49,7 +72,7 @@ export function EventRow({ event }: { event: SdkEvent }) { )} - {getRumEventDescription(event)}{' '} + @@ -58,46 +81,117 @@ export function EventRow({ event }: { event: SdkEvent }) { ) } -function getRumEventDescription(event: SdkEvent): string | undefined { +export const EventDescription = React.memo(({ event }: { event: SdkEvent }) => { if (isRumEvent(event)) { switch (event.type) { case 'view': - return `${event.view.loading_type || ''} ${getViewPage(event.view)}` - case 'action': - return `${event.action.type} action ${event.action.target?.name || ''} on page ${getViewPage(event.view)} ` - case 'resource': - return `${event.resource.type} ${event.resource.url}` - case 'error': - return `${event.error.source} error ${event.error.message}` + return case 'long_task': - return `long task of ${(event.long_task.duration / 1000).toLocaleString()} ms` - } - } else if (isTelemetryEvent(event)) { - switch (event.telemetry.type) { - case 'log': - return event.telemetry.message - case 'configuration': - return jsonOverview(event.telemetry.configuration) - default: - return '' + return + case 'error': + return + case 'resource': + return + case 'action': + return } + } else if (isLogEvent(event)) { + return } else { - return event.message + return } +}) + +function LogDescription({ event }: { event: LogsEvent }) { + return <>{event.message} } -function getViewPage(view: { name?: string; url: string }) { - return `${view.name || new URL(view.url).pathname}` +function TelemetryDescription({ event }: { event: TelemetryEvent }) { + if (event.telemetry.type === 'configuration') { + return Configuration + } + return <>{event.telemetry.message} } -function jsonOverview(jsonObject: object) { - const replacer = (key: any, value: any): any => { - if (key && typeof value === 'object') { - return '{...}' - } - return value +function ViewDescription({ event }: { event: RumViewEvent }) { + const isRouteChange = event.view.loading_type === 'route_change' + + return ( + <> + {isRouteChange ? 'SPA Route Change' : 'Load Page'} {getViewName(event.view)} + + ) +} + +function ActionDescription({ event }: { event: RumActionEvent }) { + const actionName = event.action.target?.name + const frustrationTypes = event.action.frustration?.type + + if (event.action.type === 'custom') { + return ( + <> + Custom user action {event.action.target?.name} + + ) } - const overview = JSON.stringify(jsonObject, replacer) - const unquoted = overview.replace(/"([^"]+)":/g, '$1:') - return safeTruncate(unquoted, 100, '...') + + return ( + <> + {frustrationTypes && frustrationTypes.length > 0 && '😡 '} + {event.action.type} + {actionName && ( + <> + {' '} + on {actionName} + + )} + + ) +} +function LongTaskDescription({ event }: { event: RumLongTaskEvent }) { + return ( + <> + Long task of {formatDuration(event.long_task.duration)} + + ) +} + +function ErrorDescription({ event }: { event: RumErrorEvent }) { + return ( + <> + {event.error.source} error {event.error.type}: {event.error.message} + + ) +} + +function ResourceDescription({ event }: { event: RumResourceEvent }) { + const resourceType = event.resource.type + const isAsset = resourceType !== 'xhr' && resourceType !== 'fetch' + + if (isAsset) { + return ( + <> + Load {RESOURCE_TYPE_LABELS[resourceType] || RESOURCE_TYPE_LABELS.other} file{' '} + {event.resource.url} + + ) + } + + return ( + <> + {RESOURCE_TYPE_LABELS[resourceType]} request {event.resource.url} + + ) +} + +function Emphasis({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} + +function getViewName(view: { name?: string; url: string }) { + return `${view.name || new URL(view.url).pathname}` } diff --git a/developer-extension/src/panel/formatNumber.ts b/developer-extension/src/panel/formatNumber.ts index 702a204f64..c1020773aa 100644 --- a/developer-extension/src/panel/formatNumber.ts +++ b/developer-extension/src/panel/formatNumber.ts @@ -1,3 +1,7 @@ export function formatNumber(n: number): string { return new Intl.NumberFormat('en-US', {}).format(n) } + +export function formatDuration(ns: number): string { + return new Intl.NumberFormat('en-US', { style: 'unit', unit: 'millisecond' }).format(ns / 1_000_000) +}