UNPKG

@datadog/mobile-react-native

Version:

A client-side React Native module to interact with Datadog

224 lines (223 loc) 10.3 kB
/* * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ import { InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { validateContext } from '../utils/argsUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; import { DdAttributes } from './DdAttributes'; import { generateActionEventMapper } from './eventMappers/actionEventMapper'; import { generateErrorEventMapper } from './eventMappers/errorEventMapper'; import { generateResourceEventMapper } from './eventMappers/resourceEventMapper'; import { DatadogTracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier'; import { TracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/TracingIdentifier'; import { getTracingContext, getTracingContextForPropagators } from './instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders'; import { clearCachedSessionId, getCachedSessionId, setCachedSessionId } from './sessionId/sessionIdHelper'; const generateEmptyPromise = () => new Promise(resolve => resolve()); class DdRumWrapper { // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires nativeRum = require('../specs/NativeDdRum').default; errorEventMapper = generateErrorEventMapper(undefined); resourceEventMapper = generateResourceEventMapper(undefined); actionEventMapper = generateActionEventMapper(undefined); timeProvider = new DefaultTimeProvider(); startView = (key, name, context = {}, timestampMs = this.timeProvider.now()) => { InternalLog.log(`Starting RUM View “${name}” #${key}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.startView(key, name, validateContext(context), timestampMs)); }; stopView = (key, context = {}, timestampMs = this.timeProvider.now()) => { InternalLog.log(`Stopping RUM View #${key}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.stopView(key, validateContext(context), timestampMs)); }; startAction = (type, name, context = {}, timestampMs = this.timeProvider.now()) => { InternalLog.log(`Starting RUM Action “${name}” (${type})`, SdkVerbosity.DEBUG); this.lastActionData = { type, name }; return bufferVoidNativeCall(() => this.nativeRum.startAction(type, name, validateContext(context), timestampMs)); }; stopAction = (...args) => { InternalLog.log('Stopping current RUM Action', SdkVerbosity.DEBUG); const nativeCallArgs = this.getStopActionNativeCallArgs(args); this.lastActionData = undefined; if (!nativeCallArgs) { return generateEmptyPromise(); } return this.callNativeStopAction(...nativeCallArgs); }; setTimeProvider = timeProvider => { this.timeProvider = timeProvider; }; addAction = (type, name, context = {}, timestampMs = this.timeProvider.now(), actionContext) => { const mappedEvent = this.actionEventMapper.applyEventMapper({ type, name, context, timestampMs, actionContext }); if (!mappedEvent) { return generateEmptyPromise(); } const validatedContext = validateContext(mappedEvent.context); InternalLog.log(`Adding RUM Action “${name}” (${type})`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.addAction(mappedEvent.type, mappedEvent.name, validatedContext, mappedEvent.timestampMs)); }; startResource = (key, method, url, context = {}, timestampMs = this.timeProvider.now()) => { InternalLog.log(`Starting RUM Resource #${key} ${method}: ${url}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.startResource(key, method, url, validateContext(context), timestampMs)); }; stopResource = (key, statusCode, kind, size = -1, context = {}, timestampMs = this.timeProvider.now(), resourceContext) => { const mappedEvent = this.resourceEventMapper.applyEventMapper({ key, statusCode, kind, size, context, timestampMs, resourceContext }); if (!mappedEvent) { /** * To drop the resource we call `stopResource` and pass the `_dd.drop_resource` attribute in the context. * It will be picked up by the resource mappers we implement on the native side that will drop the resource. * This ensures we don't have any "started" resource left in memory on the native side. */ return bufferVoidNativeCall(() => this.nativeRum.stopResource(key, statusCode, kind, size, { '_dd.resource.drop_resource': true }, timestampMs)); } const validatedContext = validateContext(mappedEvent.context); InternalLog.log(`Stopping RUM Resource #${key} status:${statusCode}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.stopResource(mappedEvent.key, mappedEvent.statusCode, mappedEvent.kind, mappedEvent.size, validatedContext, mappedEvent.timestampMs)); }; addError = (message, source, stacktrace, context = {}, timestampMs = this.timeProvider.now(), fingerprint) => { const mappedEvent = this.errorEventMapper.applyEventMapper({ message, source, stacktrace, context, timestampMs, fingerprint: fingerprint ?? '' }); if (!mappedEvent) { return generateEmptyPromise(); } InternalLog.log(`Adding RUM Error “${message}”`, SdkVerbosity.DEBUG); const updatedContext = validateContext(mappedEvent.context); updatedContext[DdAttributes.errorSourceType] = 'react-native'; const _debugId = debugId; if (_debugId) { updatedContext[DdAttributes.debugId] = _debugId; } return bufferVoidNativeCall(() => this.nativeRum.addError(mappedEvent.message, mappedEvent.source, mappedEvent.stacktrace, updatedContext, mappedEvent.timestampMs, mappedEvent.fingerprint)); }; addTiming = name => { InternalLog.log(`Adding timing “${name}” to RUM View`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.addTiming(name)); }; addViewLoadingTime = overwrite => { InternalLog.log(overwrite ? 'Adding and overwriting view loading to RUM View' : 'Adding view loading to RUM View', SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.addViewLoadingTime(overwrite)); }; stopSession = () => { InternalLog.log('Stopping RUM Session', SdkVerbosity.DEBUG); clearCachedSessionId(); return bufferVoidNativeCall(() => this.nativeRum.stopSession()); }; addFeatureFlagEvaluation = (name, value) => { InternalLog.log(`Adding feature flag evaluation for name: ${name} with value: ${JSON.stringify(value)}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.addFeatureFlagEvaluation(name, { value })); }; async getCurrentSessionId() { if (!GlobalState.instance.isInitialized) { return undefined; } const sessionId = await this.nativeRum.getCurrentSessionId(); if (sessionId) { setCachedSessionId(sessionId); } return sessionId; } getTracingContext = (url, tracingSamplingRate, firstPartyHosts) => { return getTracingContext(url, tracingSamplingRate, firstPartyHosts, getCachedSessionId()); }; getTracingContextForPropagators = (propagators, tracingSamplingRate) => { return getTracingContextForPropagators(propagators, tracingSamplingRate, getCachedSessionId()); }; generateTraceId() { return new DatadogTracingIdentifier(TracingIdentifier.createTraceId()); } generateSpanId() { return new DatadogTracingIdentifier(TracingIdentifier.createSpanId()); } registerErrorEventMapper(errorEventMapper) { this.errorEventMapper = generateErrorEventMapper(errorEventMapper); } unregisterErrorEventMapper() { this.errorEventMapper = generateErrorEventMapper(undefined); } registerResourceEventMapper(resourceEventMapper) { this.resourceEventMapper = generateResourceEventMapper(resourceEventMapper); } unregisterResourceEventMapper() { this.resourceEventMapper = generateResourceEventMapper(undefined); } registerActionEventMapper(actionEventMapper) { this.actionEventMapper = generateActionEventMapper(actionEventMapper); } unregisterActionEventMapper() { this.actionEventMapper = generateActionEventMapper(undefined); } callNativeStopAction = (type, name, context, timestampMs) => { const mappedEvent = this.actionEventMapper.applyEventMapper({ type, name, context, timestampMs }); if (!mappedEvent) { return bufferVoidNativeCall(() => this.nativeRum.stopAction(type, name, { '_dd.action.drop_action': true }, timestampMs)); } const validatedContext = validateContext(mappedEvent.context); return bufferVoidNativeCall(() => this.nativeRum.stopAction(mappedEvent.type, mappedEvent.name, validatedContext, mappedEvent.timestampMs)); }; getStopActionNativeCallArgs = args => { if (isNewStopActionAPI(args)) { return [args[0], args[1], args[2] || {}, args[3] || this.timeProvider.now()]; } if (isOldStopActionAPI(args)) { if (this.lastActionData) { DdSdk.telemetryDebug('DDdRum.stopAction called with the old signature'); const { type, name } = this.lastActionData; return [type, name, args[0] || {}, args[1] || this.timeProvider.now()]; } InternalLog.log('DdRum.startAction needs to be called before DdRum.stopAction', SdkVerbosity.WARN); } else { InternalLog.log('DdRum.stopAction was called with wrong arguments', SdkVerbosity.WARN); } return null; }; } const isNewStopActionAPI = args => { return typeof args[0] === 'string'; }; const isOldStopActionAPI = args => { return typeof args[0] === 'object' || typeof args[0] === 'undefined'; }; export const DdRum = new DdRumWrapper(); //# sourceMappingURL=DdRum.js.map