UNPKG

@embrace-io/react-native-tracer-provider

Version:

A React Native for the Embrace SDK that conforms to the OpenTelemetry TracerProvider interface

173 lines (172 loc) 7.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmbraceNativeSpan = void 0; const api_1 = require("@opentelemetry/api"); const util_1 = require("./util"); const TracerProviderModule_1 = require("./TracerProviderModule"); /** * EmbraceNativeSpan implements a Span over the native Embrace Android and iOS SDKs. * * A handful of simple attributes are maintained on the JS side for each span including a unique ID based on the * tracer's name and version which is used in calls over the bridge to the native modules to perform the actual operations * on the span * * The JS side of this implementation is modelled after [opentelemetry-sdk-trace-base](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base) */ // Need an ID that spans from JS can use to communicate with their stored instances on the native side. A uuid would // work but did not want to introduce extra work or another dependency here. Instead, it should be enough to keep a // global count of the spans created by any tracer or tracer provider and use a given span's createdIndex as their ID let spansCreated = 0; class EmbraceNativeSpan { constructor(tracerName, tracerVersion, tracerSchemaUrl, spanContextSyncBehaviour) { this.recording = true; this.savedSpanContext = null; this.creating = null; this.tracerName = tracerName; this.tracerVersion = tracerVersion; this.tracerSchemaUrl = tracerSchemaUrl; this.spanContextSyncBehaviour = spanContextSyncBehaviour; this.createdIndex = spansCreated++; } nativeID() { // Only `this.createdIndex` is strictly needed for uniqueness, keeping the other values for debugging purposes return `${this.tracerName}_${this.tracerVersion}_${this.tracerSchemaUrl}_${this.createdIndex}`; } creatingNativeSide(creating) { this.creating = creating; this.creating .then((spanContext) => { this.savedSpanContext = spanContext; }) .catch(reason => { (0, util_1.logWarning)(`Failed to create span: ${reason}`); }); } isReadonly() { return !this.recording; } /** * The fact that native module calls are asynchronous and the @opentelemetry/api is synchronous doesn't matter for * most methods as they are generally fire and forget. Getting spanContext is an exception since this could be asked * for immediately after having started a span and if the value is not yet ready on the native side we will either: * - return a blank span context * - throw an error * * Behaviour can be configured on the EmbraceNativeTracerProvider by supplying spanContextSyncBehaviour */ spanContext() { if (this.savedSpanContext) { return this.savedSpanContext; } const msg = "Span context is not yet available on the native side." + " You can wait on it with: `await (span as EmbraceNativeSpan).spanContextAsync()`."; switch (this.spanContextSyncBehaviour) { case "return_empty": (0, util_1.logWarning)(msg + " Returning a blank SpanContext"); return { traceId: "", spanId: "", traceFlags: 0, }; case "throw": throw new Error(msg); } } /** * spanContextAsync provides an async alternative to spanContext by wrapping the response in a promise */ spanContextAsync() { return this.creating || Promise.reject("failed to retrieve span context"); } setAttribute(key, value) { return this.setAttributes({ [key]: value }); } setAttributes(attributes) { if (this.isReadonly()) { return this; } TracerProviderModule_1.TracerProviderModule.setAttributes(this.nativeID(), (0, util_1.normalizeAttributes)(attributes)); return this; } addEvent(name, attributesOrStartTime, timeStamp) { if (this.isReadonly()) { return this; } if ((0, util_1.isAttributes)(attributesOrStartTime)) { TracerProviderModule_1.TracerProviderModule.addEvent(this.nativeID(), name, (0, util_1.normalizeAttributes)(attributesOrStartTime), (0, util_1.normalizeTime)(timeStamp)); } else if (timeStamp) { TracerProviderModule_1.TracerProviderModule.addEvent(this.nativeID(), name, (0, util_1.normalizeAttributes)({}), (0, util_1.normalizeTime)(timeStamp)); } else { TracerProviderModule_1.TracerProviderModule.addEvent(this.nativeID(), name, (0, util_1.normalizeAttributes)({}), (0, util_1.normalizeTime)(attributesOrStartTime)); } return this; } addLink(link) { return this.addLinks([link]); } addLinks(links) { if (this.isReadonly()) { return this; } (0, util_1.logWarning)("Adding span links is not currently supported by the Embrace SDK"); TracerProviderModule_1.TracerProviderModule.addLinks(this.nativeID(), (0, util_1.normalizeLinks)(links)); return this; } setStatus(status) { if (this.isReadonly()) { return this; } TracerProviderModule_1.TracerProviderModule.setStatus(this.nativeID(), { code: api_1.SpanStatusCode[status.code], message: status.message, }); return this; } updateName(name) { if (this.isReadonly()) { return this; } TracerProviderModule_1.TracerProviderModule.updateName(this.nativeID(), name); return this; } end(endTime) { if (this.isReadonly()) { return; } TracerProviderModule_1.TracerProviderModule.endSpan(this.nativeID(), (0, util_1.normalizeTime)(endTime)); this.recording = false; } isRecording() { return this.recording; } recordException(exception, time) { if (this.isReadonly()) { return; } // See: // https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/ // https://github.com/open-telemetry/opentelemetry-js/blob/4fa7c1358e84287079a5cba95313d42b50cfcb91/packages/opentelemetry-sdk-trace-base/src/Span.ts#L303 const attributes = {}; if (typeof exception === "string") { attributes["exception.message"] = exception; } else if (exception) { if (exception.code) { attributes["exception.type"] = exception.code.toString(); } else if (exception.name) { attributes["exception.type"] = exception.name; } if (exception.message) { attributes["exception.message"] = exception.message; } if (exception.stack) { attributes["exception.stacktrace"] = exception.stack; } } TracerProviderModule_1.TracerProviderModule.addEvent(this.nativeID(), "exception", (0, util_1.normalizeAttributes)(attributes), (0, util_1.normalizeTime)(time)); } } exports.EmbraceNativeSpan = EmbraceNativeSpan;