Skip to content

Commit

Permalink
✨ [RUM-4014] DD_LOGS: add handling stack in beforeSend context (#2786)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-lebeau authored Jun 11, 2024
1 parent 8517226 commit b839f8c
Show file tree
Hide file tree
Showing 27 changed files with 340 additions and 125 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/domain/console/consoleObservable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ConsoleLog {
message: string
api: ConsoleApiName
stack?: string
handlingStack?: string
handlingStack: string
fingerprint?: string
causes?: RawErrorCause[]
}
Expand Down
3 changes: 2 additions & 1 deletion packages/logs/src/boot/logsPublicApi.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { TimeStamp } from '@datadog/browser-core'
import { monitor, display, removeStorageListeners } from '@datadog/browser-core'
import type { Logger, LogsMessage } from '../domain/logger'
import { HandlerType, StatusType } from '../domain/logger'
import { HandlerType } from '../domain/logger'
import { StatusType } from '../domain/logger/isAuthorized'
import type { CommonContext } from '../rawLogsEvent.types'
import type { LogsPublicApi } from './logsPublicApi'
import { makeLogsPublicApi } from './logsPublicApi'
Expand Down
3 changes: 2 additions & 1 deletion packages/logs/src/boot/logsPublicApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
createTrackingConsentState,
} from '@datadog/browser-core'
import type { LogsInitConfiguration } from '../domain/configuration'
import type { HandlerType, StatusType } from '../domain/logger'
import type { HandlerType } from '../domain/logger'
import type { StatusType } from '../domain/logger/isAuthorized'
import { Logger } from '../domain/logger'
import { buildCommonContext } from '../domain/contexts/commonContext'
import type { InternalContext } from '../domain/contexts/internalContext'
Expand Down
7 changes: 4 additions & 3 deletions packages/logs/src/boot/preStartLogs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import type { TimeStamp, TrackingConsentState } from '@datadog/browser-core'
import { ONE_SECOND, TrackingConsent, createTrackingConsentState, display } from '@datadog/browser-core'
import type { CommonContext } from '../rawLogsEvent.types'
import type { HybridInitConfiguration, LogsConfiguration, LogsInitConfiguration } from '../domain/configuration'
import { StatusType, type Logger } from '../domain/logger'
import type { Logger } from '../domain/logger'
import { StatusType } from '../domain/logger/isAuthorized'
import type { Strategy } from './logsPublicApi'
import { createPreStartStrategy } from './preStartLogs'
import type { StartLogsResult } from './startLogs'
Expand All @@ -21,8 +22,8 @@ describe('preStartLogs', () => {
let clock: Clock

function getLoggedMessage(index: number) {
const [message, logger, savedCommonContext, savedDate] = handleLogSpy.calls.argsFor(index)
return { message, logger, savedCommonContext, savedDate }
const [message, logger, handlingStack, savedCommonContext, savedDate] = handleLogSpy.calls.argsFor(index)
return { message, logger, handlingStack, savedCommonContext, savedDate }
}

beforeEach(() => {
Expand Down
6 changes: 4 additions & 2 deletions packages/logs/src/boot/preStartLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ export function createPreStartStrategy(

getInternalContext: noop as () => undefined,

handleLog(message, statusType, context = getCommonContext(), date = timeStampNow()) {
bufferApiCalls.add((startLogsResult) => startLogsResult.handleLog(message, statusType, context, date))
handleLog(message, statusType, handlingStack, context = getCommonContext(), date = timeStampNow()) {
bufferApiCalls.add((startLogsResult) =>
startLogsResult.handleLog(message, statusType, handlingStack, context, date)
)
},
}
}
Expand Down
10 changes: 8 additions & 2 deletions packages/logs/src/boot/startLogs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import {

import type { LogsConfiguration } from '../domain/configuration'
import { validateAndBuildLogsConfiguration } from '../domain/configuration'
import { HandlerType, Logger, StatusType } from '../domain/logger'
import { HandlerType, Logger } from '../domain/logger'
import { StatusType } from '../domain/logger/isAuthorized'
import type { startLoggerCollection } from '../domain/logger/loggerCollection'
import type { LogsEvent } from '../logsEvent.types'
import { startLogs } from './startLogs'
Expand Down Expand Up @@ -94,7 +95,12 @@ describe('logs', () => {
))
registerCleanupTask(stopLogs)

handleLog({ message: 'message', status: StatusType.warn, context: { foo: 'bar' } }, logger, COMMON_CONTEXT)
handleLog(
{ message: 'message', status: StatusType.warn, context: { foo: 'bar' } },
logger,
'fake-handling-stack',
COMMON_CONTEXT
)

expect(requests.length).toEqual(1)
expect(requests[0].url).toContain(baseConfiguration.logsEndpointBuilder.build('xhr', DEFAULT_PAYLOAD))
Expand Down
3 changes: 2 additions & 1 deletion packages/logs/src/domain/assembly.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import type { CommonContext } from '../rawLogsEvent.types'
import { startLogsAssembly } from './assembly'
import type { LogsConfiguration } from './configuration'
import { validateAndBuildLogsConfiguration } from './configuration'
import { Logger, StatusType } from './logger'
import { Logger } from './logger'
import { StatusType } from './logger/isAuthorized'
import type { LogsSessionManager } from './logsSessionManager'
import { LifeCycle, LifeCycleEventType } from './lifeCycle'

Expand Down
24 changes: 23 additions & 1 deletion packages/logs/src/domain/console/consoleCollection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ErrorWithCause } from '@datadog/browser-core'
import { ErrorSource, noop, objectEntries } from '@datadog/browser-core'
import { ErrorSource, ExperimentalFeature, noop, objectEntries } from '@datadog/browser-core'
import { mockExperimentalFeatures } from '@datadog/browser-core/test'
import type { RawConsoleLogsEvent } from '../../rawLogsEvent.types'
import { validateAndBuildLogsConfiguration } from '../configuration'
import type { RawLogsEventCollectedData } from '../lifeCycle'
Expand Down Expand Up @@ -51,6 +52,27 @@ describe('console collection', () => {
error: whatever(),
})

expect(rawLogsEvents[0].domainContext).not.toBeDefined()

expect(consoleSpies[api]).toHaveBeenCalled()
})
})

objectEntries(LogStatusForApi).forEach(([api]) => {
it(`should add domainContext to logs from console.${api}`, () => {
mockExperimentalFeatures([ExperimentalFeature.MICRO_FRONTEND])
;({ stop: stopConsoleCollection } = startConsoleCollection(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: 'all' })!,
lifeCycle
))

/* eslint-disable-next-line no-console */
console[api as keyof typeof LogStatusForApi]('foo', 'bar')

expect(rawLogsEvents[0].domainContext).toEqual({
handlingStack: jasmine.any(String),
})

expect(consoleSpies[api]).toHaveBeenCalled()
})
})
Expand Down
26 changes: 21 additions & 5 deletions packages/logs/src/domain/console/consoleCollection.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import type { Context, ClocksState, ConsoleLog } from '@datadog/browser-core'
import { timeStampNow, ConsoleApiName, ErrorSource, initConsoleObservable } from '@datadog/browser-core'
import {
timeStampNow,
ConsoleApiName,
ErrorSource,
initConsoleObservable,
isExperimentalFeatureEnabled,
ExperimentalFeature,
} from '@datadog/browser-core'
import type { LogsConfiguration } from '../configuration'
import type { LifeCycle } from '../lifeCycle'
import type { LifeCycle, RawLogsEventCollectedData } from '../lifeCycle'
import { LifeCycleEventType } from '../lifeCycle'
import { StatusType } from '../logger'
import { StatusType } from '../logger/isAuthorized'
import type { RawLogsEvent } from '../../rawLogsEvent.types'

export interface ProvidedError {
startClocks: ClocksState
Expand All @@ -21,7 +29,7 @@ export const LogStatusForApi = {
}
export function startConsoleCollection(configuration: LogsConfiguration, lifeCycle: LifeCycle) {
const consoleSubscription = initConsoleObservable(configuration.forwardConsoleLogs).subscribe((log: ConsoleLog) => {
lifeCycle.notify(LifeCycleEventType.RAW_LOG_COLLECTED, {
const collectedData: RawLogsEventCollectedData<RawLogsEvent> = {
rawLogsEvent: {
date: timeStampNow(),
message: log.message,
Expand All @@ -36,7 +44,15 @@ export function startConsoleCollection(configuration: LogsConfiguration, lifeCyc
: undefined,
status: LogStatusForApi[log.api],
},
})
}

if (isExperimentalFeatureEnabled(ExperimentalFeature.MICRO_FRONTEND)) {
collectedData.domainContext = {
handlingStack: log.handlingStack,
}
}

lifeCycle.notify(LifeCycleEventType.RAW_LOG_COLLECTED, collectedData)
})

return {
Expand Down
42 changes: 40 additions & 2 deletions packages/logs/src/domain/logger.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { ErrorWithCause } from '@datadog/browser-core'
import { NO_ERROR_STACK_PRESENT_MESSAGE, createCustomerDataTracker, noop } from '@datadog/browser-core'
import type { LogsMessage } from './logger'
import { HandlerType, Logger, STATUSES, StatusType } from './logger'
import { HandlerType, Logger, STATUSES } from './logger'
import { StatusType } from './logger/isAuthorized'

describe('Logger', () => {
let logger: Logger
let handleLogSpy: jasmine.Spy<(message: LogsMessage, logger: Logger) => void>
let handleLogSpy: jasmine.Spy<(message: LogsMessage, logger: Logger, handlingStack?: string) => void>

function getLoggedMessage(index: number) {
return handleLogSpy.calls.argsFor(index)[0]
Expand All @@ -15,12 +16,20 @@ describe('Logger', () => {
return handleLogSpy.calls.argsFor(index)[1]
}

function getLoggedHandlingStack(index: number) {
return handleLogSpy.calls.argsFor(index)[2]
}

beforeEach(() => {
handleLogSpy = jasmine.createSpy()
logger = new Logger(handleLogSpy, createCustomerDataTracker(noop))
})

describe('log methods', () => {
beforeEach(() => {
logger.setLevel(StatusType.ok)
})

it("'logger.log' should have info status by default", () => {
logger.log('message')

Expand All @@ -45,6 +54,35 @@ describe('Logger', () => {
},
})
})

it(`'logger.${status}' should create an handling stack`, () => {
logger[status]('message')

expect(getLoggedHandlingStack(0)).toBeDefined()
})

it(`'logger.${status}' should not create an handling stack if the handler is 'console'`, () => {
logger.setHandler(HandlerType.console)
logger[status]('message')

expect(getLoggedHandlingStack(0)).not.toBeDefined()
})

it(`'logger.${status}' should not create an handling stack if the handler is 'silent'`, () => {
logger.setHandler(HandlerType.silent)
logger[status]('message')

expect(getLoggedHandlingStack(0)).not.toBeDefined()
})
})

it('should not create an handling stack if level is below the logger level', () => {
logger.setLevel(StatusType.warn)
logger.log('message')
logger.warn('message')

expect(getLoggedHandlingStack(0)).not.toBeDefined()
expect(getLoggedHandlingStack(1)).toBeDefined()
})

it("'logger.log' should send the log message", () => {
Expand Down
Loading

0 comments on commit b839f8c

Please sign in to comment.