From 99be1970838c42a088df1a6a7d517ffd8c14ebc3 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 11 Jun 2024 11:35:10 +0200 Subject: [PATCH] Enable alternative views dropdown in Embedded mode Add Logger --- packages/jaeger-ui/index.html | 1 + .../src/api/digma/ActionDispatcher.ts | 8 +- .../jaeger-ui/src/api/digma/dispatcher.ts | 6 +- packages/jaeger-ui/src/api/digma/index.ts | 97 ++++++++++++++----- packages/jaeger-ui/src/api/digma/types.ts | 6 +- .../TracePageHeader/AltViewOptions.css | 2 +- .../TracePageHeader/AltViewOptions.tsx | 14 ++- .../TraceTimelineViewer/SpanBarRow.tsx | 2 +- .../TraceTimelineViewer/SpanDetail/index.tsx | 6 +- .../TimelineHeaderRow/TimelineHeaderRow.tsx | 2 +- .../src/components/TracePage/index.tsx | 7 +- packages/jaeger-ui/src/index.jsx | 2 +- packages/jaeger-ui/src/logging/Logger.ts | 87 +++++++++++++++++ packages/jaeger-ui/src/logging/index.ts | 6 ++ packages/jaeger-ui/src/logging/types.ts | 7 ++ packages/jaeger-ui/src/platform.ts | 12 +++ packages/jaeger-ui/typings/custom.d.ts | 1 + 17 files changed, 219 insertions(+), 47 deletions(-) create mode 100644 packages/jaeger-ui/src/logging/Logger.ts create mode 100644 packages/jaeger-ui/src/logging/index.ts create mode 100644 packages/jaeger-ui/src/logging/types.ts create mode 100644 packages/jaeger-ui/src/platform.ts diff --git a/packages/jaeger-ui/index.html b/packages/jaeger-ui/index.html index 580592276c..b1e4634d98 100644 --- a/packages/jaeger-ui/index.html +++ b/packages/jaeger-ui/index.html @@ -46,6 +46,7 @@ window.staticPath; window.enableZoomControls; window.platform; + window.isLoggingEnabled; diff --git a/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts b/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts index 5fa4513eed..1323d1b63e 100644 --- a/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts +++ b/packages/jaeger-ui/src/api/digma/ActionDispatcher.ts @@ -1,6 +1,6 @@ import { ActionListener } from './types'; -export class ActionDispatcher { +class ActionDispatcher { private actions: { [key: string]: ActionListener[]; }; @@ -27,9 +27,11 @@ export class ActionDispatcher { } } - public dispatch(type: string, data?: unknown): void { + public dispatch(timeStamp: number, type: string, data?: unknown): void { if (this.actions[type]) { - this.actions[type].forEach(fn => fn(data)); + this.actions[type].forEach(fn => fn(data, timeStamp)); } } } + +export default ActionDispatcher; diff --git a/packages/jaeger-ui/src/api/digma/dispatcher.ts b/packages/jaeger-ui/src/api/digma/dispatcher.ts index 6b3824a156..d14232ee4a 100644 --- a/packages/jaeger-ui/src/api/digma/dispatcher.ts +++ b/packages/jaeger-ui/src/api/digma/dispatcher.ts @@ -1,3 +1,5 @@ -import { ActionDispatcher } from './ActionDispatcher'; +import ActionDispatcher from './ActionDispatcher'; -export const dispatcher = new ActionDispatcher(); +const dispatcher = new ActionDispatcher(); + +export default dispatcher; diff --git a/packages/jaeger-ui/src/api/digma/index.ts b/packages/jaeger-ui/src/api/digma/index.ts index 40d8bf80d2..f8541d118d 100644 --- a/packages/jaeger-ui/src/api/digma/index.ts +++ b/packages/jaeger-ui/src/api/digma/index.ts @@ -1,43 +1,96 @@ +import logger from '../../logging'; import { isObject } from '../../utils/ts/typeGuards/isObject'; -import { ActionDispatcher } from './ActionDispatcher'; +import ActionDispatcher from './ActionDispatcher'; import { updateState } from './state'; import { DigmaMessageEvent, IDigmaOutgoingMessageData } from './types'; const isDigmaMessageEvent = (e: MessageEvent): e is DigmaMessageEvent => isObject(e.data) && e.data.type === 'digma'; +const OUTGOING_MESSAGE_ACTION_ID_CONSOLE_STYLE = 'color: blue; font-weight: bold'; +const FAILED_OUTGOING_MESSAGE_ACTION_ID_CONSOLE_STYLE = 'color: red; font-weight: bold'; +const INCOMING_MESSAGE_ACTION_ID_CONSOLE_STYLE = 'color: green; font-weight: bold'; + export const initializeDigmaMessageListener = (dispatcher: ActionDispatcher) => { - window.addEventListener('message', e => { + const handleDigmaMessage = (e: MessageEvent) => { if (isDigmaMessageEvent(e)) { - console.debug('Digma message received: ', e); + logger.debug( + `Message received: %c${e.data.action} +%cRaw message: %O`, + INCOMING_MESSAGE_ACTION_ID_CONSOLE_STYLE, + null, + e.data + ); updateState(e.data.action, e.data.payload); - dispatcher.dispatch(e.data.action, e.data.payload); + dispatcher.dispatch(e.timeStamp, e.data.action, e.data.payload); } - }); + }; + + window.addEventListener('message', handleDigmaMessage); + + return () => { + window.removeEventListener('message', handleDigmaMessage); + }; }; -export const sendMessage = (message: IDigmaOutgoingMessageData): string | undefined => { - console.debug('Message to send:', message); +export const sendMessage = (message: IDigmaOutgoingMessageData): string | undefined => { + logger.debug( + `Message to sent: ${message.action} +Raw message: %O`, + message + ); updateState(message.action, message.payload); - if (window.sendMessageToVSCode) { - window.sendMessageToVSCode(message); - console.debug('Message has been sent to VS Code: ', message); - } else if (window.cefQuery) { - return window.cefQuery({ - request: JSON.stringify(message), - onSuccess(response) { - console.debug('cefQuery has been successfully sent: %s', response); - }, - onFailure(errorCode, errorMessage) { - console.error('Failed to send cefQuery: %d, %s', errorCode, errorMessage); - }, - }); - } else if (window.parent !== window) { - window.parent.postMessage(message, '*'); + switch (window.platform) { + case 'VS Code': + if (window.sendMessageToVSCode) { + window.sendMessageToVSCode(message); + logger.debug( + `Message has been successfully sent to VS Code: %c${message.action} +%cRaw message: %O`, + OUTGOING_MESSAGE_ACTION_ID_CONSOLE_STYLE, + null, + message + ); + } + break; + case 'JetBrains': + if (window.cefQuery) { + return window.cefQuery({ + request: JSON.stringify(message), + onSuccess(response) { + logger.debug( + `Message has been successfully handled by JCEF: %c${message.action} +%cRaw message: %O +Response: %O`, + OUTGOING_MESSAGE_ACTION_ID_CONSOLE_STYLE, + null, + message, + response + ); + }, + onFailure(errorCode: number, errorMessage: string) { + logger.error( + `Failed to handle the message by JCEF: %c${message.action} +%cRaw message: %O +%cError code: %d +Error message: %s`, + FAILED_OUTGOING_MESSAGE_ACTION_ID_CONSOLE_STYLE, + null, + errorCode, + errorMessage + ); + }, + }); + } + break; + default: + if (window.parent !== window) { + window.parent.postMessage(message, '*'); + } } return undefined; diff --git a/packages/jaeger-ui/src/api/digma/types.ts b/packages/jaeger-ui/src/api/digma/types.ts index f4f9393b28..29084d63fe 100644 --- a/packages/jaeger-ui/src/api/digma/types.ts +++ b/packages/jaeger-ui/src/api/digma/types.ts @@ -1,6 +1,6 @@ import { InsightType } from '../../components/common/InsightIcon/types'; -export type ActionListener = (data: unknown) => void; +export type ActionListener = (data: unknown, timeStamp: number) => void; export interface IDigmaIncomingMessageData { type: 'digma'; @@ -8,9 +8,9 @@ export interface IDigmaIncomingMessageData { payload?: unknown; } -export interface IDigmaOutgoingMessageData { +export interface IDigmaOutgoingMessageData { action: string; - payload?: Record; + payload?: T; } export type DigmaMessageEvent = MessageEvent; diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.css b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.css index 55d07e07fe..95ffbc6033 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.css +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.css @@ -18,6 +18,6 @@ limitations under the License. border-radius: 4px; height: 32px; line-height: 30px; - margin-right: 1rem; + margin: 0 1rem; padding: 0 8px; } diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx index 1bdeba96c4..c6c41c5ffc 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx @@ -14,7 +14,6 @@ import * as React from 'react'; import { Dropdown, Icon, Menu, Button } from 'antd'; -import { Link } from 'react-router-dom'; import './AltViewOptions.css'; import { @@ -25,7 +24,6 @@ import { trackJsonView, trackRawJsonView, } from './TracePageHeader.track'; -import prefixUrl from '../../../utils/prefix-url'; import { ETraceViewType } from '../types'; type Props = { @@ -83,24 +81,24 @@ export default function AltViewOptions(props: Props) { ))} - Trace JSON - + - Trace JSON (unadjusted) - + ); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx index 5c22411313..8cae86d55c 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx @@ -24,7 +24,7 @@ import { formatDuration, ViewedBoundsFunctionType } from './utils'; import SpanTreeOffset from './SpanTreeOffset'; import SpanBar from './SpanBar'; import Ticks from './Ticks'; -import { dispatcher } from '../../../api/digma/dispatcher'; +import dispatcher from '../../../api/digma/dispatcher'; import { actions } from '../../../api/digma/actions'; import { state as globalState } from '../../../api/digma/state'; import { ISpanInsight, SetSpansDataPayload } from '../../../api/digma/types'; diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx index b4c9c83b5e..48d43eceab 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanDetail/index.tsx @@ -25,7 +25,7 @@ import { formatDuration } from '../utils'; import CopyIcon from '../../../common/CopyIcon'; import LabeledList from '../../../common/LabeledList'; import { actions } from '../../../../api/digma/actions'; -import { dispatcher } from '../../../../api/digma/dispatcher'; +import dispatcher from '../../../../api/digma/dispatcher'; import { state as globalState } from '../../../../api/digma/state'; import { ISpanInsight, SetSpansDataPayload } from '../../../../api/digma/types'; import { getInsightTypeInfo, getInsightTypeOrderPriority } from '../../../common/InsightIcon/utils'; @@ -77,7 +77,9 @@ export default class SpanDetail extends React.Component { scrollBy, scrollTo, }); - dispatcher.dispatch(actions.CLEAR); + window.sendMessageToDigma({ + action: actions.CLEAR, + }); } getSpansWithResolvedLocations(trace: Trace) { @@ -400,7 +401,7 @@ export class TracePageImpl extends React.PureComponent { showArchiveButton: !isEmbedded && archiveEnabled, showShortcutsHelp: !isEmbedded, showStandaloneLink: isEmbedded, - showViewOptions: !isEmbedded, + showViewOptions: true, toSearch: (locationState && locationState.fromSearch) || null, trace: data, updateNextViewRangeTime: this.updateNextViewRangeTime, diff --git a/packages/jaeger-ui/src/index.jsx b/packages/jaeger-ui/src/index.jsx index af11e6b8ec..a491b4a5c2 100644 --- a/packages/jaeger-ui/src/index.jsx +++ b/packages/jaeger-ui/src/index.jsx @@ -25,7 +25,7 @@ import JaegerUIApp from './components/App'; import { context as trackingContext } from './utils/tracking'; import { cancelMessage, initializeDigmaMessageListener, sendMessage } from './api/digma'; -import { dispatcher } from './api/digma/dispatcher'; +import dispatcher from './api/digma/dispatcher'; // these need to go after the App import /* eslint-disable import/first */ diff --git a/packages/jaeger-ui/src/logging/Logger.ts b/packages/jaeger-ui/src/logging/Logger.ts new file mode 100644 index 0000000000..f66d29d6c6 --- /dev/null +++ b/packages/jaeger-ui/src/logging/Logger.ts @@ -0,0 +1,87 @@ +import moment from 'moment'; +import { LOG_LEVEL } from './types'; + +class Logger { + private minLogLevel: number; + private showTimeStamp: boolean; + private showLogLevel: boolean; + + constructor(minLogLevel: LOG_LEVEL, showTimeStamp = true, showLogLevel = true) { + this.minLogLevel = minLogLevel; + this.showTimeStamp = showTimeStamp; + this.showLogLevel = showLogLevel; + } + + private getTimestampTag(): string { + return moment(new Date()).format('HH:mm:ss'); + } + + private getLogLevelTag(): string { + return LOG_LEVEL[this.minLogLevel]; + } + + private getFormattedMessage(tags: string[], message: unknown): string { + if (this.showLogLevel) { + tags.unshift(this.getLogLevelTag()); + } + + if (this.showTimeStamp) { + tags.unshift(this.getTimestampTag()); + } + + const tagsString = tags.map(x => `[${x}]`).join(''); + + return `${tagsString}: ${message as string}`; + } + + public setLogLevel(logLevel: LOG_LEVEL): void { + this.minLogLevel = logLevel; + } + + public log(level: LOG_LEVEL, tags: string[], message?: unknown, ...optionalParams: unknown[]): void { + const formattedMessage = this.getFormattedMessage(tags, message); + + if (this.minLogLevel > level) { + return; + } + + switch (level) { + case LOG_LEVEL.DEBUG: + // eslint-disable-next-line no-console + console.debug(formattedMessage, ...optionalParams); + break; + case LOG_LEVEL.INFO: + // eslint-disable-next-line no-console + console.info(formattedMessage, ...optionalParams); + break; + case LOG_LEVEL.WARN: + // eslint-disable-next-line no-console + console.warn(formattedMessage, ...optionalParams); + break; + case LOG_LEVEL.ERROR: + // eslint-disable-next-line no-console + console.error(formattedMessage, ...optionalParams); + break; + default: + break; + } + } + + public debug(message?: unknown, ...optionalParams: unknown[]): void { + this.log(LOG_LEVEL.DEBUG, [], message, ...optionalParams); + } + + public info(message?: unknown, ...optionalParams: unknown[]): void { + this.log(LOG_LEVEL.INFO, [], message, ...optionalParams); + } + + public warn(message?: unknown, ...optionalParams: unknown[]): void { + this.log(LOG_LEVEL.WARN, [], message, ...optionalParams); + } + + public error(message?: unknown, ...optionalParams: unknown[]): void { + this.log(LOG_LEVEL.ERROR, [], message, ...optionalParams); + } +} + +export default Logger; diff --git a/packages/jaeger-ui/src/logging/index.ts b/packages/jaeger-ui/src/logging/index.ts new file mode 100644 index 0000000000..7c4574e6ef --- /dev/null +++ b/packages/jaeger-ui/src/logging/index.ts @@ -0,0 +1,6 @@ +import Logger from './Logger'; +import { LOG_LEVEL } from './types'; + +const logger = new Logger(window.isLoggingEnabled === true ? LOG_LEVEL.DEBUG : LOG_LEVEL.NONE); + +export default logger; diff --git a/packages/jaeger-ui/src/logging/types.ts b/packages/jaeger-ui/src/logging/types.ts new file mode 100644 index 0000000000..0d36ddc8da --- /dev/null +++ b/packages/jaeger-ui/src/logging/types.ts @@ -0,0 +1,7 @@ +export enum LOG_LEVEL { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + NONE = 4, +} diff --git a/packages/jaeger-ui/src/platform.ts b/packages/jaeger-ui/src/platform.ts new file mode 100644 index 0000000000..f8e0ae2751 --- /dev/null +++ b/packages/jaeger-ui/src/platform.ts @@ -0,0 +1,12 @@ +import { isString } from './utils/ts/typeGuards/isString'; + +type Platform = 'JetBrains' | 'VS Code' | 'Web'; + +const PLATFORMS = ['JetBrains', 'VS Code', 'Web']; + +const isPlatform = (platform: unknown): platform is Platform => + isString(platform) && PLATFORMS.includes(platform); + +export const getPlatform = (platform: unknown): Platform | null => (isPlatform(platform) ? platform : null); + +export const platform = getPlatform(window.platform); diff --git a/packages/jaeger-ui/typings/custom.d.ts b/packages/jaeger-ui/typings/custom.d.ts index ba948ed536..86bf133514 100644 --- a/packages/jaeger-ui/typings/custom.d.ts +++ b/packages/jaeger-ui/typings/custom.d.ts @@ -40,6 +40,7 @@ declare interface Window { isUserDefinedJaegerQueryURL?: unknown; staticPath?: unknown; enableZoomControls?: unknown; + isLoggingEnabled?: boolean; } declare const __REACT_APP_GA_DEBUG__: string | undefined;