From aacc3ecbae7f7f8d97362b45f883910fc9ffa3eb Mon Sep 17 00:00:00 2001 From: "roman.gaignault" Date: Tue, 4 Feb 2025 18:00:32 +0100 Subject: [PATCH] support error context --- .../domain/console/consoleObservable.spec.ts | 13 ++++++++ .../src/domain/console/consoleObservable.ts | 3 +- packages/core/src/domain/error/error.ts | 20 ++++++++++--- packages/core/src/domain/error/error.types.ts | 2 ++ .../domain/console/consoleCollection.spec.ts | 30 +++++++++++++++++++ .../createErrorFieldFromRawError.spec.ts | 6 ++++ .../domain/createErrorFieldFromRawError.ts | 1 + packages/logs/src/domain/logger.spec.ts | 14 ++++++++- .../domain/report/reportCollection.spec.ts | 1 + .../runtimeErrorCollection.spec.ts | 4 +++ packages/logs/src/rawLogsEvent.types.ts | 1 + .../src/domain/error/errorCollection.spec.ts | 2 ++ .../src/domain/error/errorCollection.ts | 1 + .../domain/error/trackConsoleError.spec.ts | 2 ++ packages/rum-core/src/rawRumEvent.types.ts | 1 + 15 files changed, 95 insertions(+), 6 deletions(-) diff --git a/packages/core/src/domain/console/consoleObservable.spec.ts b/packages/core/src/domain/console/consoleObservable.spec.ts index fbf3b7e20e..fd0bc90d4f 100644 --- a/packages/core/src/domain/console/consoleObservable.spec.ts +++ b/packages/core/src/domain/console/consoleObservable.spec.ts @@ -128,4 +128,17 @@ describe('console error observable', () => { const consoleLog = notifyLog.calls.mostRecent().args[0] expect(consoleLog.error.fingerprint).toBe('2') }) + + it('should retrieve context from error', () => { + interface DatadogError extends Error { + dd_context?: Record + } + const error = new Error('foo') + ;(error as DatadogError).dd_context = { foo: 'bar' } + + console.error(error) + + const consoleLog = notifyLog.calls.mostRecent().args[0] + expect(consoleLog.error.context).toEqual({ foo: 'bar' }) + }) }) diff --git a/packages/core/src/domain/console/consoleObservable.ts b/packages/core/src/domain/console/consoleObservable.ts index 543e11e70a..17e6c23149 100644 --- a/packages/core/src/domain/console/consoleObservable.ts +++ b/packages/core/src/domain/console/consoleObservable.ts @@ -1,4 +1,4 @@ -import { flattenErrorCauses, isError, tryToGetFingerprint } from '../error/error' +import { flattenErrorCauses, isError, tryToGetFingerprint, tryToGetErrorContext } from '../error/error' import { mergeObservables, Observable } from '../../tools/observable' import { ConsoleApiName, globalConsole } from '../../tools/display' import { callMonitored } from '../../tools/monitor' @@ -84,6 +84,7 @@ function buildConsoleLog(params: unknown[], api: ConsoleApiName, handlingStack: source: ErrorSource.CONSOLE, handling: ErrorHandling.HANDLED, handlingStack, + context: tryToGetErrorContext(firstErrorParam), } } diff --git a/packages/core/src/domain/error/error.ts b/packages/core/src/domain/error/error.ts index 94f4e5a91d..d71f5feb59 100644 --- a/packages/core/src/domain/error/error.ts +++ b/packages/core/src/domain/error/error.ts @@ -39,6 +39,7 @@ export function computeRawError({ const causes = isErrorInstance ? flattenErrorCauses(originalError as ErrorWithCause, source) : undefined const type = stackTrace ? stackTrace.name : undefined const fingerprint = tryToGetFingerprint(originalError) + const context = isErrorInstance ? tryToGetErrorContext(originalError) : undefined return { startClocks, @@ -52,6 +53,7 @@ export function computeRawError({ stack, causes, fingerprint, + context, } } @@ -86,6 +88,13 @@ export function tryToGetFingerprint(originalError: unknown) { return isError(originalError) && 'dd_fingerprint' in originalError ? String(originalError.dd_fingerprint) : undefined } +export function tryToGetErrorContext(originalError: unknown) { + if (isError(originalError) && 'dd_context' in originalError) { + return originalError.dd_context as Record + } + return undefined +} + export function getFileFromStackTraceString(stack: string) { return /@ (.+)/.exec(stack)?.[1] } @@ -98,14 +107,17 @@ export function flattenErrorCauses(error: ErrorWithCause, parentSource: ErrorSou let currentError = error const causes: RawErrorCause[] = [] while (isError(currentError?.cause) && causes.length < 10) { - const stackTrace = computeStackTrace(currentError.cause) + const causeError = currentError.cause + const stackTrace = computeStackTrace(causeError) + causes.push({ - message: currentError.cause.message, + message: causeError.message, source: parentSource, type: stackTrace?.name, - stack: stackTrace && toStackTraceString(stackTrace), + stack: stackTrace ? toStackTraceString(stackTrace) : NO_ERROR_STACK_PRESENT_MESSAGE, + context: tryToGetErrorContext(causeError), }) - currentError = currentError.cause + currentError = causeError } return causes.length ? causes : undefined } diff --git a/packages/core/src/domain/error/error.types.ts b/packages/core/src/domain/error/error.types.ts index 12715bec4a..c470646b6d 100644 --- a/packages/core/src/domain/error/error.types.ts +++ b/packages/core/src/domain/error/error.types.ts @@ -18,6 +18,7 @@ export type RawErrorCause = { source: string type?: string stack?: string + context?: Record } export type Csp = { @@ -37,6 +38,7 @@ export interface RawError { causes?: RawErrorCause[] fingerprint?: string csp?: Csp + context?: Record } export const ErrorSource = { diff --git a/packages/logs/src/domain/console/consoleCollection.spec.ts b/packages/logs/src/domain/console/consoleCollection.spec.ts index d3a98ce9a5..f976d463f5 100644 --- a/packages/logs/src/domain/console/consoleCollection.spec.ts +++ b/packages/logs/src/domain/console/consoleCollection.spec.ts @@ -71,6 +71,7 @@ describe('console collection', () => { expect(rawLogsEvents[0].rawLogsEvent.error).toEqual({ stack: undefined, fingerprint: undefined, + context: undefined, causes: undefined, handling: ErrorHandling.HANDLED, kind: undefined, @@ -99,6 +100,32 @@ describe('console collection', () => { handling: ErrorHandling.HANDLED, kind: undefined, message: undefined, + context: undefined, + }) + }) + + it('should retrieve context from console error', () => { + ;({ stop: stopConsoleCollection } = startConsoleCollection( + validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!, + lifeCycle + )) + interface DatadogError extends Error { + dd_context?: Record + } + const error = new Error('foo') + ;(error as DatadogError).dd_context = { foo: 'bar' } + + // eslint-disable-next-line no-console + console.error(error) + + expect(rawLogsEvents[0].rawLogsEvent.error).toEqual({ + stack: jasmine.any(String), + fingerprint: undefined, + causes: undefined, + handling: ErrorHandling.HANDLED, + kind: undefined, + message: undefined, + context: { foo: 'bar' }, }) }) @@ -131,15 +158,18 @@ describe('console collection', () => { type: 'Error', stack: jasmine.any(String), message: 'Mid level error', + context: undefined, }, { source: ErrorSource.CONSOLE, type: 'TypeError', stack: jasmine.any(String), message: 'Low level error', + context: undefined, }, ], fingerprint: undefined, + context: undefined, kind: undefined, message: undefined, }) diff --git a/packages/logs/src/domain/createErrorFieldFromRawError.spec.ts b/packages/logs/src/domain/createErrorFieldFromRawError.spec.ts index 611960da05..4c0a551e57 100644 --- a/packages/logs/src/domain/createErrorFieldFromRawError.spec.ts +++ b/packages/logs/src/domain/createErrorFieldFromRawError.spec.ts @@ -20,6 +20,9 @@ describe('createErrorFieldFromRawError', () => { csp: { disposition: 'enforce', }, + context: { + grault: 'garply', + }, } it('creates an error field from a raw error', () => { @@ -30,6 +33,9 @@ describe('createErrorFieldFromRawError', () => { causes: [], fingerprint: 'corge', handling: ErrorHandling.HANDLED, + context: { + grault: 'garply', + }, }) }) diff --git a/packages/logs/src/domain/createErrorFieldFromRawError.ts b/packages/logs/src/domain/createErrorFieldFromRawError.ts index a098151f95..963a6229f0 100644 --- a/packages/logs/src/domain/createErrorFieldFromRawError.ts +++ b/packages/logs/src/domain/createErrorFieldFromRawError.ts @@ -17,6 +17,7 @@ export function createErrorFieldFromRawError( message: includeMessage ? rawError.message : undefined, causes: rawError.causes, fingerprint: rawError.fingerprint, + context: rawError.context, handling: rawError.handling, } } diff --git a/packages/logs/src/domain/logger.spec.ts b/packages/logs/src/domain/logger.spec.ts index 197d0f9ae0..13396a27c9 100644 --- a/packages/logs/src/domain/logger.spec.ts +++ b/packages/logs/src/domain/logger.spec.ts @@ -53,6 +53,7 @@ describe('Logger', () => { causes: undefined, handling: ErrorHandling.HANDLED, fingerprint: undefined, + context: undefined, }, }) }) @@ -116,6 +117,7 @@ describe('Logger', () => { causes: undefined, handling: ErrorHandling.HANDLED, fingerprint: undefined, + context: undefined, }, }, status: 'error', @@ -158,15 +160,23 @@ describe('Logger', () => { message: 'High level error', handling: ErrorHandling.HANDLED, causes: [ - { message: 'Mid level error', source: 'logger', type: 'Error', stack: 'Error: Mid level error' }, + { + message: 'Mid level error', + source: 'logger', + type: 'Error', + stack: 'Error: Mid level error', + context: undefined, + }, { message: 'Low level error', source: 'logger', type: 'TypeError', stack: 'TypeError: Low level error', + context: undefined, }, ], fingerprint: undefined, + context: undefined, }, }, }) @@ -263,6 +273,7 @@ describe('Logger', () => { causes: undefined, handling: ErrorHandling.HANDLED, fingerprint: undefined, + context: undefined, }) }) @@ -279,6 +290,7 @@ describe('Logger', () => { causes: undefined, handling: ErrorHandling.HANDLED, fingerprint: undefined, + context: undefined, }) }) diff --git a/packages/logs/src/domain/report/reportCollection.spec.ts b/packages/logs/src/domain/report/reportCollection.spec.ts index ce68bac76c..c589d53992 100644 --- a/packages/logs/src/domain/report/reportCollection.spec.ts +++ b/packages/logs/src/domain/report/reportCollection.spec.ts @@ -44,6 +44,7 @@ describe('reports', () => { causes: undefined, fingerprint: undefined, message: undefined, + context: undefined, }, date: jasmine.any(Number), message: 'intervention: foo bar', diff --git a/packages/logs/src/domain/runtimeError/runtimeErrorCollection.spec.ts b/packages/logs/src/domain/runtimeError/runtimeErrorCollection.spec.ts index f2119f103a..3fb03701d7 100644 --- a/packages/logs/src/domain/runtimeError/runtimeErrorCollection.spec.ts +++ b/packages/logs/src/domain/runtimeError/runtimeErrorCollection.spec.ts @@ -47,6 +47,7 @@ describe('runtime error collection', () => { handling: ErrorHandling.UNHANDLED, fingerprint: undefined, message: undefined, + context: undefined, }, message: 'error!', status: StatusType.error, @@ -87,16 +88,19 @@ describe('runtime error collection', () => { type: 'Error', stack: jasmine.any(String), message: 'Mid level error', + context: undefined, }, { source: ErrorSource.SOURCE, type: 'TypeError', stack: jasmine.any(String), message: 'Low level error', + context: undefined, }, ], fingerprint: undefined, message: undefined, + context: undefined, }, message: 'High level error', status: StatusType.error, diff --git a/packages/logs/src/rawLogsEvent.types.ts b/packages/logs/src/rawLogsEvent.types.ts index b1883bdd62..02b657c3b2 100644 --- a/packages/logs/src/rawLogsEvent.types.ts +++ b/packages/logs/src/rawLogsEvent.types.ts @@ -14,6 +14,7 @@ type Error = { kind?: string stack?: string fingerprint?: string + context?: Record causes?: RawErrorCause[] handling: ErrorHandling | undefined } diff --git a/packages/rum-core/src/domain/error/errorCollection.spec.ts b/packages/rum-core/src/domain/error/errorCollection.spec.ts index 67535bd55b..507c37bc1d 100644 --- a/packages/rum-core/src/domain/error/errorCollection.spec.ts +++ b/packages/rum-core/src/domain/error/errorCollection.spec.ts @@ -91,6 +91,7 @@ describe('error collection', () => { causes: undefined, fingerprint: undefined, csp: undefined, + context: undefined, }, type: RumEventType.ERROR, view: { @@ -269,6 +270,7 @@ describe('error collection', () => { causes: undefined, fingerprint: undefined, csp: undefined, + context: undefined, }, view: { in_foreground: true, diff --git a/packages/rum-core/src/domain/error/errorCollection.ts b/packages/rum-core/src/domain/error/errorCollection.ts index 368439b499..52573ffa5b 100644 --- a/packages/rum-core/src/domain/error/errorCollection.ts +++ b/packages/rum-core/src/domain/error/errorCollection.ts @@ -97,6 +97,7 @@ function processError(error: RawError, pageStateHistory: PageStateHistory): RawR source_type: 'browser', fingerprint: error.fingerprint, csp: error.csp, + context: error.context, }, type: RumEventType.ERROR as const, view: { in_foreground: pageStateHistory.wasInPageStateAt(PageState.ACTIVE, error.startClocks.relative) }, diff --git a/packages/rum-core/src/domain/error/trackConsoleError.spec.ts b/packages/rum-core/src/domain/error/trackConsoleError.spec.ts index d1239f5b30..30236284ca 100644 --- a/packages/rum-core/src/domain/error/trackConsoleError.spec.ts +++ b/packages/rum-core/src/domain/error/trackConsoleError.spec.ts @@ -34,6 +34,7 @@ describe('trackConsoleError', () => { message: jasmine.any(String), stack: jasmine.any(String), fingerprint: undefined, + context: undefined, source: ErrorSource.CONSOLE, handling: ErrorHandling.HANDLED, handlingStack: jasmine.any(String), @@ -60,6 +61,7 @@ describe('trackConsoleError', () => { handlingStack: jasmine.any(String), fingerprint: 'my-fingerprint', causes: undefined, + context: undefined, }) }) }) diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 42cd308ead..d915119872 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -82,6 +82,7 @@ export interface RawRumErrorEvent { causes?: RawErrorCause[] source_type: 'browser' csp?: Csp + context?: Record } view?: { in_foreground: boolean