UNPKG

@datadog/mobile-react-native

Version:

A client-side React Native module to interact with Datadog

216 lines (215 loc) 10.1 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 { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { validateContext } from '../utils/argsUtils'; import { getErrorContext } from '../utils/errorUtils'; 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 { 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: validateContext(context), timestampMs, actionContext }); if (!mappedEvent) { return generateEmptyPromise(); } InternalLog.log(`Adding RUM Action “${name}” (${type})`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.addAction(mappedEvent.type, mappedEvent.name, mappedEvent.context, 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: validateContext(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)); } InternalLog.log(`Stopping RUM Resource #${key} status:${statusCode}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => this.nativeRum.stopResource(mappedEvent.key, mappedEvent.statusCode, mappedEvent.kind, mappedEvent.size, mappedEvent.context, mappedEvent.timestampMs)); }; addError = (message, source, stacktrace, context = {}, timestampMs = this.timeProvider.now(), fingerprint) => { const mappedEvent = this.errorEventMapper.applyEventMapper({ message, source, stacktrace, context: getErrorContext(validateContext(context)), timestampMs, fingerprint: fingerprint ?? '' }); if (!mappedEvent) { return generateEmptyPromise(); } InternalLog.log(`Adding RUM Error “${message}”`, SdkVerbosity.DEBUG); const updatedContext = mappedEvent.context; updatedContext[DdAttributes.errorSourceType] = 'react-native'; 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); 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: validateContext(context), timestampMs }); if (!mappedEvent) { return bufferVoidNativeCall(() => this.nativeRum.stopAction(type, name, { '_dd.action.drop_action': true }, timestampMs)); } return bufferVoidNativeCall(() => this.nativeRum.stopAction(mappedEvent.type, mappedEvent.name, mappedEvent.context, mappedEvent.timestampMs)); }; getStopActionNativeCallArgs = args => { if (isNewStopActionAPI(args)) { return [args[0], args[1], validateContext(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, validateContext(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