Skip to content

Commit

Permalink
✨ [RUMF-1071] forward internal monitoring to bridge (#1177)
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque authored Dec 6, 2021
1 parent ca94e24 commit 2287507
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 78 deletions.
13 changes: 13 additions & 0 deletions packages/core/src/domain/internalMonitoring/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export {
InternalMonitoring,
MonitoringMessage,
monitored,
monitor,
callMonitored,
addMonitoringMessage,
addMonitoringError,
startFakeInternalMonitoring,
resetInternalMonitoring,
setDebugMode,
startInternalMonitoring,
} from './internalMonitoring'
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import sinon from 'sinon'
import { Clock, mockClock, stubEndpointBuilder } from '../../test/specHelper'

import { Configuration } from './configuration'
import { resetExperimentalFeatures, updateExperimentalFeatures } from '../..'
import {
Clock,
deleteEventBridgeStub,
initEventBridgeStub,
mockClock,
stubEndpointBuilder,
} from '../../../test/specHelper'

import { Configuration } from '../configuration'
import {
InternalMonitoring,
monitor,
Expand Down Expand Up @@ -171,13 +178,12 @@ describe('internal monitoring', () => {
})
})

describe('request', () => {
describe('transport', () => {
const FAKE_DATE = 123456
let server: sinon.SinonFakeServer
let clock: Clock

beforeEach(() => {
startInternalMonitoring(configuration as Configuration)
server = sinon.fakeServer.create()
clock = mockClock(new Date(FAKE_DATE))
})
Expand All @@ -186,9 +192,13 @@ describe('internal monitoring', () => {
resetInternalMonitoring()
server.restore()
clock.cleanup()
resetExperimentalFeatures()
deleteEventBridgeStub()
})

it('should send the needed data', () => {
startInternalMonitoring(configuration as Configuration)

callMonitored(() => {
throw new Error('message')
})
Expand All @@ -205,6 +215,8 @@ describe('internal monitoring', () => {
})

it('should cap the data sent', () => {
startInternalMonitoring(configuration as Configuration)

const max = configuration.maxInternalMonitoringMessagesPerPage!
for (let i = 0; i < max + 3; i += 1) {
callMonitored(() => {
Expand All @@ -214,6 +226,19 @@ describe('internal monitoring', () => {

expect(server.requests.length).toEqual(max)
})

it('should send bridge event when bridge is present', () => {
updateExperimentalFeatures(['event-bridge'])
const sendSpy = spyOn(initEventBridgeStub(), 'send')
startInternalMonitoring(configuration as Configuration)

callMonitored(() => {
throw new Error('message')
})

expect(server.requests.length).toEqual(0)
expect(sendSpy).toHaveBeenCalled()
})
})

describe('external context', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Context } from '../tools/context'
import { display } from '../tools/display'
import { toStackTraceString } from '../tools/error'
import { assign, combine, jsonStringify, Parameters, ThisParameterType } from '../tools/utils'
import { Batch, HttpRequest } from '../transport'
import { Configuration } from './configuration'
import { EndpointBuilder } from './configuration/endpointBuilder'
import { computeStackTrace } from './tracekit'
import { Context } from '../../tools/context'
import { display } from '../../tools/display'
import { toStackTraceString } from '../../tools/error'
import { assign, combine, jsonStringify, Parameters, ThisParameterType } from '../../tools/utils'
import { canUseEventBridge, getEventBridge } from '../../transport'
import { Configuration } from '../configuration'
import { computeStackTrace } from '../tracekit'
import { startMonitoringBatch } from './startMonitoringBatch'

enum StatusType {
info = 'info',
Expand All @@ -26,85 +26,61 @@ export interface MonitoringMessage extends Context {
}

const monitoringConfiguration: {
batch?: Batch
debugMode?: boolean
maxMessagesPerPage: number
sentMessageCount: number
} = { maxMessagesPerPage: 0, sentMessageCount: 0 }

let externalContextProvider: () => Context
let onInternalMonitoringMessageCollected: ((message: MonitoringMessage) => void) | undefined

export function startInternalMonitoring(configuration: Configuration): InternalMonitoring {
if (configuration.internalMonitoringEndpointBuilder) {
const batch = startMonitoringBatch(configuration)

assign(monitoringConfiguration, {
batch,
maxMessagesPerPage: configuration.maxInternalMonitoringMessagesPerPage,
sentMessageCount: 0,
})
}
return {
setExternalContextProvider: (provider: () => Context) => {
externalContextProvider = provider
},
}
}
let externalContextProvider: () => Context

function startMonitoringBatch(configuration: Configuration) {
const primaryBatch = createMonitoringBatch(configuration.internalMonitoringEndpointBuilder!)
let replicaBatch: Batch | undefined
if (configuration.replica !== undefined) {
replicaBatch = createMonitoringBatch(configuration.replica.internalMonitoringEndpointBuilder)
if (canUseEventBridge()) {
const bridge = getEventBridge()!
onInternalMonitoringMessageCollected = (message: MonitoringMessage) =>
bridge.send('internal_log', withContext(message))
} else if (configuration.internalMonitoringEndpointBuilder) {
const batch = startMonitoringBatch(configuration)
onInternalMonitoringMessageCollected = (message: MonitoringMessage) => batch.add(withContext(message))
}

function createMonitoringBatch(endpointBuilder: EndpointBuilder) {
return new Batch(
new HttpRequest(endpointBuilder, configuration.batchBytesLimit),
configuration.maxBatchSize,
configuration.batchBytesLimit,
configuration.maxMessageSize,
configuration.flushTimeout
)
}
assign(monitoringConfiguration, {
maxMessagesPerPage: configuration.maxInternalMonitoringMessagesPerPage,
sentMessageCount: 0,
})

function withContext(message: MonitoringMessage) {
return combine(
{
date: new Date().getTime(),
},
{ date: new Date().getTime() },
externalContextProvider !== undefined ? externalContextProvider() : {},
message
)
}

return {
add(message: MonitoringMessage) {
const contextualizedMessage = withContext(message)
primaryBatch.add(contextualizedMessage)
if (replicaBatch) {
replicaBatch.add(contextualizedMessage)
}
setExternalContextProvider: (provider: () => Context) => {
externalContextProvider = provider
},
}
}

export function startFakeInternalMonitoring() {
const messages: MonitoringMessage[] = []
assign(monitoringConfiguration, {
batch: {
add(message: MonitoringMessage) {
messages.push(message)
},
},
maxMessagesPerPage: Infinity,
sentMessageCount: 0,
})

onInternalMonitoringMessageCollected = (message: MonitoringMessage) => {
messages.push(message)
}

return messages
}

export function resetInternalMonitoring() {
monitoringConfiguration.batch = undefined
onInternalMonitoringMessageCollected = undefined
}

export function monitored<T extends (...params: any[]) => unknown>(
Expand All @@ -114,7 +90,7 @@ export function monitored<T extends (...params: any[]) => unknown>(
) {
const originalMethod = descriptor.value!
descriptor.value = function (this: any, ...args: Parameters<T>) {
const decorated = monitoringConfiguration.batch ? monitor(originalMethod) : originalMethod
const decorated = onInternalMonitoringMessageCollected ? monitor(originalMethod) : originalMethod
return decorated.apply(this, args) as ReturnType<T>
} as T
}
Expand Down Expand Up @@ -143,7 +119,7 @@ export function callMonitored<T extends (...args: any[]) => any>(
} catch (e) {
logErrorIfDebug(e)
try {
addErrorToMonitoringBatch(e)
addMonitoringError(e)
} catch (e) {
logErrorIfDebug(e)
}
Expand All @@ -152,28 +128,27 @@ export function callMonitored<T extends (...args: any[]) => any>(

export function addMonitoringMessage(message: string, context?: Context) {
logMessageIfDebug(message, context)
addToMonitoringBatch({
addToMonitoring({
message,
...context,
status: StatusType.info,
})
}

export function addErrorToMonitoringBatch(e: unknown) {
addToMonitoringBatch({
export function addMonitoringError(e: unknown) {
addToMonitoring({
...formatError(e),
status: StatusType.error,
})
}

function addToMonitoringBatch(message: MonitoringMessage) {
function addToMonitoring(message: MonitoringMessage) {
if (
monitoringConfiguration.batch &&
onInternalMonitoringMessageCollected &&
monitoringConfiguration.sentMessageCount < monitoringConfiguration.maxMessagesPerPage
) {
monitoringConfiguration.sentMessageCount += 1

monitoringConfiguration.batch.add(message)
onInternalMonitoringMessageCollected(message)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Batch, HttpRequest } from '../../transport'
import { Configuration, EndpointBuilder, MonitoringMessage } from '../..'

export function startMonitoringBatch(configuration: Configuration) {
const primaryBatch = createMonitoringBatch(configuration.internalMonitoringEndpointBuilder!)
let replicaBatch: Batch | undefined
if (configuration.replica !== undefined) {
replicaBatch = createMonitoringBatch(configuration.replica.internalMonitoringEndpointBuilder)
}

function createMonitoringBatch(endpointBuilder: EndpointBuilder) {
return new Batch(
new HttpRequest(endpointBuilder, configuration.batchBytesLimit),
configuration.maxBatchSize,
configuration.batchBytesLimit,
configuration.maxMessageSize,
configuration.flushTimeout
)
}

return {
add(message: MonitoringMessage) {
primaryBatch.add(message)
if (replicaBatch) {
replicaBatch.add(message)
}
},
}
}
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export {
monitor,
callMonitored,
addMonitoringMessage,
addErrorToMonitoringBatch,
addMonitoringError,
startFakeInternalMonitoring,
resetInternalMonitoring,
setDebugMode,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/transport/eventBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function getEventBridge<T, E>() {

export function canUseEventBridge(): boolean {
const bridge = getEventBridge()

return !!bridge && includes(bridge.getAllowedWebViewHosts(), window.location.hostname)
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/transport/httpRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EndpointBuilder } from '../domain/configuration/endpointBuilder'
import { monitor, addErrorToMonitoringBatch, addMonitoringMessage } from '../domain/internalMonitoring'
import { monitor, addMonitoringError, addMonitoringMessage } from '../domain/internalMonitoring'

let hasReportedXhrError = false

Expand Down Expand Up @@ -69,6 +69,6 @@ let hasReportedBeaconError = false
function reportBeaconError(e: unknown) {
if (!hasReportedBeaconError) {
hasReportedBeaconError = true
addErrorToMonitoringBatch(e)
addMonitoringError(e)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addErrorToMonitoringBatch, display, includes, monitor } from '@datadog/browser-core'
import { addMonitoringError, display, includes, monitor } from '@datadog/browser-core'
import { createDeflateWorker, DeflateWorker } from './deflateWorker'

/**
Expand Down Expand Up @@ -102,11 +102,11 @@ function onError(error: ErrorEvent | Error | string) {
'https://docs.datadoghq.com/real_user_monitoring/faq/content_security_policy'
)
} else {
addErrorToMonitoringBatch(error)
addMonitoringError(error)
}
state.callbacks.forEach((callback) => callback())
state = { status: DeflateWorkerStatus.Error }
} else {
addErrorToMonitoringBatch(error)
addMonitoringError(error)
}
}
12 changes: 11 additions & 1 deletion test/e2e/lib/framework/pageSetups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,17 @@ function setupEventBridge(servers: Servers) {
send(e) {
const { eventType, event } = JSON.parse(e)
const request = new XMLHttpRequest()
const endpoint = eventType === 'log' ? 'logs' : 'rum'
let endpoint
switch (eventType) {
case 'internal_log':
endpoint = 'internalMonitoring'
break
case 'log':
endpoint = 'logs'
break
default:
endpoint = 'rum'
}
request.open('POST', \`${servers.intake.url}/v1/input/\${endpoint}?bridge=1\`, true)
request.send(JSON.stringify(event))
},
Expand Down
Loading

0 comments on commit 2287507

Please sign in to comment.