@datadog/browser-logs
Version:
130 lines (106 loc) • 4.53 kB
text/typescript
import type { TrackingConsentState } from '@datadog/browser-core'
import {
createBoundedBuffer,
canUseEventBridge,
display,
displayAlreadyInitializedError,
initFeatureFlags,
initFetchObservable,
noop,
timeStampNow,
buildAccountContextManager,
CustomerContextKey,
bufferContextCalls,
addTelemetryConfiguration,
buildGlobalContextManager,
buildUserContextManager,
startTelemetry,
TelemetryService,
mockable,
} from '@datadog/browser-core'
import type { Hooks } from '../domain/hooks'
import { createHooks } from '../domain/hooks'
import type { LogsConfiguration, LogsInitConfiguration } from '../domain/configuration'
import { serializeLogsConfiguration, validateAndBuildLogsConfiguration } from '../domain/configuration'
import type { CommonContext } from '../rawLogsEvent.types'
import type { Strategy } from './logsPublicApi'
import type { StartLogsResult } from './startLogs'
export type DoStartLogs = (
initConfiguration: LogsInitConfiguration,
configuration: LogsConfiguration,
hooks: Hooks
) => StartLogsResult
export function createPreStartStrategy(
getCommonContext: () => CommonContext,
trackingConsentState: TrackingConsentState,
doStartLogs: DoStartLogs
): Strategy {
const bufferApiCalls = createBoundedBuffer<StartLogsResult>()
// TODO next major: remove the globalContext, accountContextManager, userContext from preStartStrategy and use an empty context instead
const globalContext = buildGlobalContextManager()
bufferContextCalls(globalContext, CustomerContextKey.globalContext, bufferApiCalls)
const accountContext = buildAccountContextManager()
bufferContextCalls(accountContext, CustomerContextKey.accountContext, bufferApiCalls)
const userContext = buildUserContextManager()
bufferContextCalls(userContext, CustomerContextKey.userContext, bufferApiCalls)
let cachedInitConfiguration: LogsInitConfiguration | undefined
let cachedConfiguration: LogsConfiguration | undefined
const hooks = createHooks()
const trackingConsentStateSubscription = trackingConsentState.observable.subscribe(tryStartLogs)
function tryStartLogs() {
if (!cachedConfiguration || !cachedInitConfiguration || !trackingConsentState.isGranted()) {
return
}
mockable(startTelemetry)(TelemetryService.LOGS, cachedConfiguration, hooks)
trackingConsentStateSubscription.unsubscribe()
const startLogsResult = doStartLogs(cachedInitConfiguration, cachedConfiguration, hooks)
bufferApiCalls.drain(startLogsResult)
}
return {
init(initConfiguration, errorStack) {
if (!initConfiguration) {
display.error('Missing configuration')
return
}
// Set the experimental feature flags as early as possible, so we can use them in most places
initFeatureFlags(initConfiguration.enableExperimentalFeatures)
if (canUseEventBridge()) {
initConfiguration = overrideInitConfigurationForBridge(initConfiguration)
}
// Expose the initial configuration regardless of initialization success.
cachedInitConfiguration = initConfiguration
addTelemetryConfiguration(serializeLogsConfiguration(initConfiguration))
if (cachedConfiguration) {
displayAlreadyInitializedError('DD_LOGS', initConfiguration)
return
}
const configuration = validateAndBuildLogsConfiguration(initConfiguration, errorStack)
if (!configuration) {
return
}
cachedConfiguration = configuration
// Instrument fetch to track network requests
// This is needed in case the consent is not granted and some customer
// library (Apollo Client) is storing uninstrumented fetch to be used later
// The subscrption is needed so that the instrumentation process is completed
initFetchObservable().subscribe(noop)
trackingConsentState.tryToInit(configuration.trackingConsent)
tryStartLogs()
},
get initConfiguration() {
return cachedInitConfiguration
},
globalContext,
accountContext,
userContext,
getInternalContext: noop as () => undefined,
handleLog(message, statusType, handlingStack, context = getCommonContext(), date = timeStampNow()) {
bufferApiCalls.add((startLogsResult) =>
startLogsResult.handleLog(message, statusType, handlingStack, context, date)
)
},
}
}
function overrideInitConfigurationForBridge(initConfiguration: LogsInitConfiguration): LogsInitConfiguration {
return { ...initConfiguration, clientToken: 'empty' }
}