applicationinsights
Version:
Microsoft Application Insights module for Node.js
259 lines • 13.4 kB
JavaScript
// 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
;