Skip to content

Commit

Permalink
♻️ Create a console collection
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque committed Mar 28, 2022
1 parent aa2bd37 commit bb5793b
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 108 deletions.
75 changes: 4 additions & 71 deletions packages/logs/src/boot/startLogs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { ConsoleLog, Context, RawReport, TimeStamp } from '@datadog/browser-core'
import type { Context, RawReport, TimeStamp } from '@datadog/browser-core'
import {
noop,
Observable,
resetExperimentalFeatures,
updateExperimentalFeatures,
stopSessionManager,
display,
initConsoleObservable,
} from '@datadog/browser-core'
import sinon from 'sinon'
import { deleteEventBridgeStub, initEventBridgeStub, stubEndpointBuilder } from '../../../core/test/specHelper'
Expand All @@ -15,7 +13,7 @@ import type { LogsConfiguration } from '../domain/configuration'
import { validateAndBuildLogsConfiguration } from '../domain/configuration'

import type { LogsMessage } from '../domain/logger'
import { StatusType, HandlerType } from '../domain/logger'
import { StatusType } from '../domain/logger'
import type { LogsSessionManager } from '../domain/logsSessionManager'
import type { Sender } from '../domain/sender'
import { createSender } from '../domain/sender'
Expand Down Expand Up @@ -53,8 +51,7 @@ describe('logs', () => {
let sessionIsTracked: boolean
let server: sinon.SinonFakeServer
let reportObservable: Observable<RawReport>
let consoleObservable: Observable<ConsoleLog>
let consoleLogSpy: jasmine.Spy

const sessionManager: LogsSessionManager = {
findTrackedSession: () => (sessionIsTracked ? { id: SESSION_ID } : undefined),
}
Expand All @@ -64,14 +61,7 @@ describe('logs', () => {
configuration: configurationOverrides,
}: { sender?: Sender; configuration?: Partial<LogsConfiguration> } = {}) => {
const configuration = { ...baseConfiguration, ...configurationOverrides }
const startLogs = doStartLogs(
configuration,
() => undefined,
consoleObservable,
reportObservable,
sessionManager,
sender
)
const startLogs = doStartLogs(configuration, () => undefined, reportObservable, sessionManager, sender)
stopLogs = startLogs.stop
return startLogs.send
}
Expand All @@ -83,10 +73,8 @@ describe('logs', () => {
maxBatchSize: 1,
}
sessionIsTracked = true
consoleObservable = new Observable<ConsoleLog>()
reportObservable = new Observable<RawReport>()
server = sinon.fakeServer.create()
consoleLogSpy = spyOn(console, 'log').and.callFake(() => true)
})

afterEach(() => {
Expand Down Expand Up @@ -162,61 +150,6 @@ describe('logs', () => {
event: jasmine.objectContaining({ message: 'message' }),
})
})

it('should not print the log twice when console handler is enabled', () => {
const sender = createSender(noop)
const logErrorSpy = spyOn(sender, 'sendToHttp')
const displaySpy = spyOn(display, 'log')

consoleObservable = initConsoleObservable(['log'])
startLogs({ sender })
sender.setHandler([HandlerType.console])
/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(logErrorSpy).toHaveBeenCalled()
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
expect(displaySpy).not.toHaveBeenCalled()

resetExperimentalFeatures()
})

it('should send console logs when ff forward-logs is enabled', () => {
const sender = createSender(noop)
const logErrorSpy = spyOn(sender, 'sendToHttp')

updateExperimentalFeatures(['forward-logs'])
const { stop } = originalStartLogs(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!,
sender
)

/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(logErrorSpy).toHaveBeenCalled()
expect(consoleLogSpy).toHaveBeenCalled()

resetExperimentalFeatures()
stop()
})

it('should not send console logs when ff forward-logs is disabled', () => {
const sender = createSender(noop)
const logErrorSpy = spyOn(sender, 'sendToHttp')

const { stop } = originalStartLogs(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!,
sender
)

/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(logErrorSpy).not.toHaveBeenCalled()
expect(consoleLogSpy).toHaveBeenCalled()
stop()
})
})

describe('reports', () => {
Expand Down
42 changes: 5 additions & 37 deletions packages/logs/src/boot/startLogs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import type {
ConsoleLog,
Context,
RawError,
MonitoringMessage,
TelemetryEvent,
RawReport,
Observable,
} from '@datadog/browser-core'
import type { Context, RawError, MonitoringMessage, TelemetryEvent, RawReport, Observable } from '@datadog/browser-core'
import {
areCookiesAuthorized,
combine,
Expand All @@ -15,8 +7,6 @@ import {
startInternalMonitoring,
RawReportType,
initReportObservable,
initConsoleObservable,
ConsoleApiName,
ErrorSource,
getFileFromStackTraceString,
startBatchWithReplica,
Expand All @@ -30,14 +20,7 @@ import type { LogsEvent } from '../logsEvent.types'
import { buildAssemble, getRUMInternalContext } from '../domain/assemble'
import type { Sender } from '../domain/sender'
import { startRawErrorCollection } from '../domain/logsCollection/rawErrorCollection'

const LogStatusForApi = {
[ConsoleApiName.log]: StatusType.info,
[ConsoleApiName.debug]: StatusType.debug,
[ConsoleApiName.info]: StatusType.info,
[ConsoleApiName.warn]: StatusType.warn,
[ConsoleApiName.error]: StatusType.error,
}
import { startConsoleCollection } from '../domain/logsCollection/consoleCollection'

const LogStatusForReport = {
[RawReportType.cspViolation]: StatusType.error,
Expand Down Expand Up @@ -68,15 +51,16 @@ export function startLogs(configuration: LogsConfiguration, sender: Sender) {
}))

const { reportRawError } = startRawErrorCollection(configuration, sender)
const consoleObservable = initConsoleObservable(configuration.forwardConsoleLogs)
startConsoleCollection(configuration, sender)

const reportObservable = initReportObservable(configuration.forwardReports)

const session =
areCookiesAuthorized(configuration.cookieOptions) && !canUseEventBridge()
? startLogsSessionManager(configuration)
: startLogsSessionManagerStub(configuration)

return doStartLogs(configuration, reportRawError, consoleObservable, reportObservable, session, sender)
return doStartLogs(configuration, reportRawError, reportObservable, session, sender)
}

function startLogsInternalMonitoring(configuration: LogsConfiguration) {
Expand Down Expand Up @@ -107,7 +91,6 @@ function startLogsInternalMonitoring(configuration: LogsConfiguration) {
export function doStartLogs(
configuration: LogsConfiguration,
reportRawError: (error: RawError) => void,
consoleObservable: Observable<ConsoleLog>,
reportObservable: Observable<RawReport>,
sessionManager: LogsSessionManager,
sender: Sender
Expand All @@ -127,19 +110,6 @@ export function doStartLogs(
onLogEventCollected = (message) => batch.add(message)
}

function reportConsoleLog(log: ConsoleLog) {
let messageContext: Partial<LogsEvent> | undefined
if (log.api === ConsoleApiName.error) {
messageContext = {
error: {
origin: ErrorSource.CONSOLE,
stack: log.stack,
},
}
}
sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api])
}

function logReport(report: RawReport) {
let message = report.message
let messageContext: Partial<LogsEvent> | undefined
Expand All @@ -159,12 +129,10 @@ export function doStartLogs(
sender.sendToHttp(message, messageContext, logStatus)
}

const consoleSubscription = consoleObservable.subscribe(reportConsoleLog)
const reportSubscription = reportObservable.subscribe(logReport)

return {
stop: () => {
consoleSubscription.unsubscribe()
reportSubscription.unsubscribe()
},
send: (message: LogsMessage, currentContext: Context) => {
Expand Down
91 changes: 91 additions & 0 deletions packages/logs/src/domain/logsCollection/consoleCollection.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ErrorSource, resetExperimentalFeatures, updateExperimentalFeatures, display } from '@datadog/browser-core'
import { validateAndBuildLogsConfiguration } from '../configuration'
import { HandlerType, StatusType } from '../logger'
import { createSender } from '../sender'
import { startConsoleCollection } from './consoleCollection'

describe('error collection', () => {
const initConfiguration = { clientToken: 'xxx', service: 'service' }
let sendLogSpy: jasmine.Spy
let consoleLogSpy: jasmine.Spy

beforeEach(() => {
sendLogSpy = jasmine.createSpy('sendLogSpy')
consoleLogSpy = spyOn(console, 'log').and.callFake(() => true)
spyOn(console, 'error').and.callFake(() => true)
})

it('should send console logs when ff forward-logs is enabled', () => {
updateExperimentalFeatures(['forward-logs'])
const { stop } = startConsoleCollection(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!,
createSender(sendLogSpy)
)

/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(sendLogSpy).toHaveBeenCalledWith({
message: 'console log: foo bar',
status: StatusType.info,
})

expect(consoleLogSpy).toHaveBeenCalled()

resetExperimentalFeatures()
stop()
})

it('should not send console logs when ff forward-logs is disabled', () => {
const { stop } = startConsoleCollection(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!,
createSender(sendLogSpy)
)

/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(sendLogSpy).not.toHaveBeenCalled()
expect(consoleLogSpy).toHaveBeenCalled()

stop()
})

it('console error should have an error object defined', () => {
const { stop } = startConsoleCollection(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardErrorsToLogs: true })!,
createSender(sendLogSpy)
)

/* eslint-disable-next-line no-console */
console.error('foo', 'bar')

expect(sendLogSpy.calls.mostRecent().args[0].error).toEqual({
origin: ErrorSource.CONSOLE,
stack: undefined,
})

stop()
})

it('should not print the log twice when console handler is enabled', () => {
updateExperimentalFeatures(['forward-logs'])

const sender = createSender(sendLogSpy)
const displaySpy = spyOn(display, 'log')
const { stop } = startConsoleCollection(
validateAndBuildLogsConfiguration({ ...initConfiguration, forwardConsoleLogs: ['log'] })!,
sender
)

sender.setHandler([HandlerType.console])
/* eslint-disable-next-line no-console */
console.log('foo', 'bar')

expect(consoleLogSpy).toHaveBeenCalledTimes(1)
expect(displaySpy).not.toHaveBeenCalled()

resetExperimentalFeatures()
stop()
})
})
44 changes: 44 additions & 0 deletions packages/logs/src/domain/logsCollection/consoleCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Context, ClocksState, ConsoleLog } from '@datadog/browser-core'
import { ConsoleApiName, ErrorSource, initConsoleObservable } from '@datadog/browser-core'
import type { LogsEvent } from '../../logsEvent.types'
import type { LogsConfiguration } from '../configuration'
import { StatusType } from '../logger'
import type { Sender } from '../sender'

export interface ProvidedError {
startClocks: ClocksState
error: unknown
context?: Context
handlingStack: string
}

const LogStatusForApi = {
[ConsoleApiName.log]: StatusType.info,
[ConsoleApiName.debug]: StatusType.debug,
[ConsoleApiName.info]: StatusType.info,
[ConsoleApiName.warn]: StatusType.warn,
[ConsoleApiName.error]: StatusType.error,
}
export function startConsoleCollection(configuration: LogsConfiguration, sender: Sender) {
const consoleObservable = initConsoleObservable(configuration.forwardConsoleLogs)
const consoleSubscription = consoleObservable.subscribe(reportConsoleLog)

function reportConsoleLog(log: ConsoleLog) {
let messageContext: Partial<LogsEvent> | undefined
if (log.api === ConsoleApiName.error) {
messageContext = {
error: {
origin: ErrorSource.CONSOLE,
stack: log.stack,
},
}
}
sender.sendToHttp(log.message, messageContext, LogStatusForApi[log.api])
}

return {
stop: () => {
consoleSubscription.unsubscribe()
},
}
}

0 comments on commit bb5793b

Please sign in to comment.