UNPKG

applicationinsights

Version:

Microsoft Application Insights module for Node.js

259 lines 13.4 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. Object.defineProperty(exports, "__esModule", { value: true }); exports.CorrelationContextManager = void 0; const api_1 = require("@opentelemetry/api"); const core_1 = require("@opentelemetry/core"); const util_1 = require("../shared/util"); const CONTEXT_NAME = "ApplicationInsights-Context"; class CorrelationContextManager { /** * Converts an OpenTelemetry SpanContext object to an ICorrelationContext object for backwards compatibility with ApplicationInsights * @param spanContext OpenTelmetry SpanContext object * @param parentId spanId of the parent span * @param name OpenTelemetry human readable name of the span * @param traceState String of key value pairs for additional trace context * @returns ICorrelationContext object */ static spanToContextObject(spanContext, parentId, name, traceState) { var _a; // Generate a basic ITraceparent to satisfy the ICorrelationContext interface const traceContext = { legacyRootId: "", traceId: spanContext === null || spanContext === void 0 ? void 0 : spanContext.traceId, spanId: spanContext === null || spanContext === void 0 ? void 0 : spanContext.spanId, traceFlag: (_a = spanContext === null || spanContext === void 0 ? void 0 : spanContext.traceFlags) === null || _a === void 0 ? void 0 : _a.toString(), parentId: parentId, version: "00" }; return this.generateContextObject(traceContext.traceId, traceContext.parentId, name, traceContext, traceState); } /** * Provides the current Context. * The context is the most recent one entered into for the current * logical chain of execution, including across asynchronous calls. * @returns ICorrelationContext object */ static getCurrentContext() { var _a, _b; if (!this._isDisabled) { // Gets the active span and extracts the context to populate and return the ICorrelationContext object let activeSpan = api_1.trace.getSpan(api_1.context.active()); // If no active span exists, create a new one. This is needed if runWithContext() is executed without an active span if (!activeSpan) { activeSpan = api_1.trace.getTracer(CONTEXT_NAME).startSpan(CONTEXT_NAME); } const traceStateObj = new core_1.TraceState((_b = (_a = activeSpan === null || activeSpan === void 0 ? void 0 : activeSpan.spanContext()) === null || _a === void 0 ? void 0 : _a.traceState) === null || _b === void 0 ? void 0 : _b.serialize()); return this.spanToContextObject(activeSpan === null || activeSpan === void 0 ? void 0 : activeSpan.spanContext(), activeSpan === null || activeSpan === void 0 ? void 0 : activeSpan.parentSpanId, activeSpan === null || activeSpan === void 0 ? void 0 : activeSpan.name, traceStateObj); } return null; } /** * Helper to generate objects conforming to the CorrelationContext interface * @param operationId String assigned to a series of related telemetry items - equivalent to OpenTelemetry traceId * @param parentId spanId of the parent span * @param operationName Human readable name of the span * @param traceparent Context conveying string in the format version-traceId-spanId-traceFlag * @param tracestate String of key value pairs for additional trace context * @returns ICorrelationContext object */ static generateContextObject(operationId, parentId, operationName, traceparent, tracestate) { var _a; // Cast OpenTelemetry TraceState object to ITracestate object const ITraceState = { fieldmap: (_a = tracestate === null || tracestate === void 0 ? void 0 : tracestate.serialize()) === null || _a === void 0 ? void 0 : _a.split(",") }; return { operation: { name: operationName, id: operationId, parentId: parentId, traceparent: traceparent, tracestate: ITraceState, }, // Headers are not being used so custom properties will always be stubbed out customProperties: { getProperty(prop) { return ""; }, setProperty(prop) { return ""; }, }, }; } /** * Runs a function inside a given Context. * All logical children of the execution path that entered this Context * will receive this Context object on calls to GetCurrentContext. * @param ctx Context to run the function within * @param fn Function to run within the stated context * @returns any */ static runWithContext(ctx, fn) { // Creates a new Context object containing the values from the ICorrelationContext object, then sets the active context to the new Context try { const newContext = api_1.trace.setSpanContext(api_1.context.active(), this._contextObjectToSpanContext(ctx)); return api_1.context.with(newContext, fn); } catch (error) { api_1.diag.warn("Error binding to session context", util_1.Util.getInstance().dumpObj(error)); } return fn(); } /** * Wrapper for cls-hooked bindEmitter method * @param emitter emitter to bind to the current context */ static wrapEmitter(emitter) { try { api_1.context.bind(api_1.context.active(), emitter); } catch (error) { api_1.diag.warn("Error binding to session context", util_1.Util.getInstance().dumpObj(error)); } } /** * Patches a callback to restore the correct Context when getCurrentContext * is run within it. This is necessary if automatic correlation fails to work * with user-included libraries. * The supplied callback will be given the same context that was present for * the call to wrapCallback * @param fn Function to be wrapped in the provided context * @param ctx Context to bind the function to * @returns Generic type T */ static wrapCallback(fn, ctx) { try { if (ctx) { // Create the new context and bind it if context is passed const newContext = api_1.trace.setSpanContext(api_1.context.active(), this._contextObjectToSpanContext(ctx)); return api_1.context.bind(newContext, fn); } // If no context is passed, bind to the current context return api_1.context.bind(api_1.context.active(), fn); } catch (error) { api_1.diag.error("Error binding to session context", util_1.Util.getInstance().dumpObj(error)); return fn; } } /** * Enables the CorrelationContextManager * @param forceClsHooked unused parameter used to satisfy backward compatibility */ // eslint-disable-next-line @typescript-eslint/no-unused-vars static enable(forceClsHooked) { api_1.diag.info("Enabling the context manager is no longer necessary and this method is a no-op."); } /** * Creates a new correlation context * @param input Any kind of object we can extract context information from * @param request HTTP request we can pull context information from in the form of the request's headers * @returns IcorrelationContext object */ static startOperation(input, request) { const traceContext = (input && input.traceContext || null); const span = input && input.spanContext ? input : null; const spanContext = input && input.traceId ? input : null; const headers = input && input.headers; if (span) { api_1.trace.setSpanContext(api_1.context.active(), span.spanContext()); return this.spanToContextObject(span.spanContext(), span.parentSpanId); } if (spanContext) { api_1.trace.setSpanContext(api_1.context.active(), spanContext); return this.spanToContextObject(spanContext); } if (traceContext || headers) { let traceparent = null; let tracestate = null; if (traceContext) { // Use the headers on the request from Azure Functions to set the active context const azureFnRequest = request; // New programming model if ((azureFnRequest === null || azureFnRequest === void 0 ? void 0 : azureFnRequest.headers) && (azureFnRequest === null || azureFnRequest === void 0 ? void 0 : azureFnRequest.headers.get)) { traceparent = azureFnRequest.headers.get("traceparent") || azureFnRequest.headers.get("request-id"); tracestate = azureFnRequest.headers.get("tracestate"); } // Old programming model else if (azureFnRequest === null || azureFnRequest === void 0 ? void 0 : azureFnRequest.headers) { // request-id is a GUID-based unique identifier for the request traceparent = azureFnRequest.headers.traceparent ? azureFnRequest.headers.traceparent : azureFnRequest.headers["request-id"]; tracestate = azureFnRequest.headers.tracestate; } if (!traceparent && traceContext.traceparent) { traceparent = traceContext.traceparent; } else if (!traceparent && traceContext.traceParent) { traceparent = traceContext.traceParent; } if (!tracestate && traceContext.tracestate) { tracestate = traceContext.tracestate; } else if (!tracestate && traceContext.traceState) { tracestate = traceContext.traceState; } } // If headers is defined instead of traceContext, use the headers to set the traceparent and tracestate // If headers is not an instance of Headers, we use the old programming model, otherwise use the old v3 values if (headers && headers.traceparent) { traceparent = headers.traceparent ? headers.traceparent.toString() : null; tracestate = headers.tracestate ? headers.tracestate.toString() : tracestate; } else if (headers && headers.get) { traceparent = headers.get("traceparent") || headers.get("request-id"); tracestate = headers.get("tracestate"); } const traceArray = traceparent === null || traceparent === void 0 ? void 0 : traceparent.split("-"); const tracestateObj = new core_1.TraceState(); tracestate === null || tracestate === void 0 ? void 0 : tracestate.split(",").forEach((pair) => { const kv = pair.split("="); tracestateObj.set(kv[0], kv[1]); }); try { return this.generateContextObject(traceArray[1], traceArray[2], null, { legacyRootId: "", parentId: "", spanId: traceArray[2], traceFlag: "", traceId: traceArray[1], version: "00", }, tracestateObj); } catch (error) { api_1.diag.warn("Error creating context object", util_1.Util.getInstance().dumpObj(error)); } } api_1.diag.warn("startOperation was called with invalid arguments"); return null; } /** * Disables the CorrelationContextManager */ static disable() { api_1.diag.warn("It will not be possible to re-enable the current context manager after disabling it!"); this._isDisabled = true; api_1.context.disable(); } /** * Resets the namespace */ static reset() { api_1.diag.info("This is a no-op and exists only for compatibility reasons."); } /** * Converts ApplicationInsights' ICorrelationContext to an OpenTelemetry SpanContext * @param ctx ICorrelationContext object to convert to a SpanContext * @returns OpenTelemetry SpanContext */ static _contextObjectToSpanContext(ctx) { var _a, _b, _c, _d; return { traceId: ctx.operation.id, spanId: (_b = (_a = ctx.operation.traceparent) === null || _a === void 0 ? void 0 : _a.spanId) !== null && _b !== void 0 ? _b : "", traceFlags: ((_c = ctx.operation.traceparent) === null || _c === void 0 ? void 0 : _c.traceFlag) ? Number((_d = ctx.operation.traceparent) === null || _d === void 0 ? void 0 : _d.traceFlag) : undefined, }; } } exports.CorrelationContextManager = CorrelationContextManager; // Context is taken from the trace API and not context API so we need a flag to disable this functionality CorrelationContextManager._isDisabled = false; //# sourceMappingURL=correlationContextManager.js.map