@datadog/mobile-react-native
Version:
A client-side React Native module to interact with Datadog
216 lines (215 loc) • 10.1 kB
JavaScript
/*
* 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