UNPKG

@dynatrace/opentelemetry-core

Version:

Dynatrace OpenTelemetry core package

269 lines (266 loc) 12.9 kB
"use strict"; /* Copyright 2022 Dynatrace LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DtTextMapPropagator = exports.X_DTC_HEADER = exports.X_DYNTATRACE_HEADER = void 0; const api_1 = require("@opentelemetry/api"); const core_1 = require("@opentelemetry/core"); const StartupConfig_1 = require("../configuration/StartupConfig"); const SpanEmbedder_1 = require("../export/SpanEmbedder"); const SpanUpdater_1 = require("../export/SpanUpdater"); const Logger_1 = require("../Logger"); const Fw4Tag_1 = require("./Fw4Tag"); const LoggingUtils_1 = require("../utils/LoggingUtils"); const RumLite_1 = require("../utils/context/RumLite"); const XDtcHeader = require("./XDtcHeader"); const MobileTag = require("./MobileTag"); // ============================================================================ exports.X_DYNTATRACE_HEADER = "x-dynatrace"; exports.X_DTC_HEADER = "x-dtc"; // some shortcuts const { getSpanContext, getSpan, isSpanContextValid, setSpanContext } = api_1.trace; // ---------------------------------------------------------------------------- class DtTextMapPropagator { constructor(config) { this._w3cTraceContext = new core_1.W3CTraceContextPropagator(); this._logger = new Logger_1.ComponentLogger("Propagator"); config = Object.assign(Object.assign({}, (0, StartupConfig_1.getConfiguration)()), config); if (config == null || typeof (config) !== "object") { throw new TypeError("DtTextMapPropagator: config invalid"); } if (typeof (config.clusterId) !== "number") { throw new TypeError("DtTextMapPropagator: clusterId must be a number"); } if (typeof (config.tenantId) !== "number") { throw new TypeError("DtTextMapPropagator: tenantId must be a number"); } this._ids = { clusterId: config.clusterId, tenantId: config.tenantId }; this._traceStateKey = Fw4Tag_1.Fw4Tag.toTraceStateKey(this._ids); } // ------------------------------------------------------------------------ inject(context, carrier, setter) { const span = getSpan(context); if (span == null) { this._logger.warn("inject: sensor attempted to inject context missing a Span"); return; } if (!isSpanContextValid(span.spanContext())) { this._logger.info("inject: sensor attempted to inject an invalid span context"); return; } // Set last-propagation-time (0, SpanUpdater_1.markSpanPropagatedNow)(span); // update TraceState by either replacing our @dt entry or add a new one const spanContext = span.spanContext(); let parentTag = (0, SpanEmbedder_1.getFw4Tag)(spanContext.traceState); if (parentTag == null) { if (this._logger.debugEnabled) { this._logger.debug("inject: create FW4 tag"); } // Usually we should have a tag on TraceState, if there is none create one parentTag = new Fw4Tag_1.Fw4Tag(this._ids).setRootPathRandom(); } const tag = parentTag.propagate(spanContext); const xDt = tag.toXDt(); setter.set(carrier, exports.X_DYNTATRACE_HEADER, xDt); // Update TraceState in a new SpanContext to avoid side effects const injectSpanContext = Object.assign({}, spanContext); if (injectSpanContext.traceState == null) { injectSpanContext.traceState = new core_1.TraceState(); } injectSpanContext.traceState = injectSpanContext.traceState.set(this._traceStateKey, tag.toTraceStateValue()); this._w3cTraceContext.inject(setSpanContext(context, injectSpanContext), carrier, setter); if (this._logger.debugEnabled) { this._logger.debug(`inject: x-dt: ${xDt} - traceId: ${injectSpanContext.traceId}, spanId: ${injectSpanContext.spanId}, traceState: ${injectSpanContext.traceState.serialize()}`); } } // ------------------------------------------------------------------------ extract(parentContext, carrier, getter) { // first extract W3C headers let context = this._w3cTraceContext.extract(parentContext, carrier, getter); if (this._logger.debugEnabled) { const spanContext = getSpanContext(context); this._logger.debug(`extract: W3C traceId: ${spanContext === null || spanContext === void 0 ? void 0 : spanContext.traceId}, spanId: ${spanContext === null || spanContext === void 0 ? void 0 : spanContext.spanId}`); } // check x-dynatrace header for FW4 tag, if present use it const xDynaTrace = this._getXdynaTrace(carrier, getter); let fw4Tag = this._getFw4FromXdynaTrace(xDynaTrace); if (fw4Tag != null) { context = this._updateContextFromXDt(fw4Tag, context); } else if (context !== parentContext) { // parse FW4 tag from TraceState if present and attach it for later use const spanContext = getSpanContext(context); if ((spanContext === null || spanContext === void 0 ? void 0 : spanContext.traceState) != null) { fw4Tag = this._tagFromTraceState(spanContext.traceState); if (fw4Tag != null) { (0, SpanEmbedder_1.setFw4Tag)(spanContext.traceState, fw4Tag); } } } if (fw4Tag == null) { let serverId; // check for Mobile tag which may hold serverId if (xDynaTrace != null) { serverId = MobileTag.getServerId(xDynaTrace); // only non zero serverIds are valid if (serverId) { if (this._logger.debugEnabled) { this._logger.debug(`extract: use MobileTag: ${xDynaTrace}, serverId: ${serverId}`); } context = (0, RumLite_1.setServerId)(context, serverId); context = (0, RumLite_1.setMobileTag)(context, xDynaTrace); } else { this._logger.warn(`extract: Failed to parse serverId from mobile tag ${xDynaTrace}`); } } if (!serverId) { // with no Fw4 nor MT tag, server id may come from x-dtc const xDtc = this._getXdtc(carrier, getter); if (xDtc != null) { serverId = XDtcHeader.getServerId(xDtc); // only non zero serverIds are valid if (serverId) { if (this._logger.debugEnabled) { this._logger.debug(`extract: use x-dtc: ${xDtc}, serverId: ${serverId}`); } context = (0, RumLite_1.setServerId)(context, serverId); context = (0, RumLite_1.setXDtc)(context, xDtc); } else { this._logger.warn(`extract: Failed to parse serverId from x-dtc header ${xDtc}`); } } } } else { this._updateTraceFlags(fw4Tag, context); } if (this._logger.debugEnabled) { const spanContext = getSpanContext(context); if (spanContext == null) { this._logger.debug("extract: <no parent>"); } else { this._logger.debug(`extract: traceId: ${spanContext.traceId}, spanId: ${spanContext.spanId}, traceState: ${spanContext.traceState != null ? spanContext.traceState.serialize() : "<>"}`); } } return context; } // ------------------------------------------------------------------------ fields() { return [exports.X_DYNTATRACE_HEADER, ...this._w3cTraceContext.fields()]; } // ------------------------------------------------------------------------ _tagFromTraceState(traceState) { const traceStateVal = traceState.get(this._traceStateKey); if (traceStateVal != null) { try { if (this._logger.debugEnabled) { this._logger.debug(`extract: use tracestate: ${traceStateVal}`); } return Fw4Tag_1.Fw4Tag.fromTraceState(this._ids, traceStateVal); } catch (e) { if (this._logger.debugEnabled) { this._logger.debug(`extract: failed to decode tracestate ${(0, LoggingUtils_1.verboseFormatException)(e)}`); } } } return undefined; } // ------------------------------------------------------------------------ _getFw4FromXdynaTrace(xDynatrace) { if (xDynatrace != null) { try { if (this._logger.debugEnabled) { this._logger.debug(`extract: found x-dynatrace: ${xDynatrace}`); } const fw4Tag = Fw4Tag_1.Fw4Tag.fromXDt(this._ids, xDynatrace); if (fw4Tag.extensions.spanId && fw4Tag.extensions.traceId) { // only use x-dt tags with traceId and SpanId return fw4Tag; } } catch (e) { if (this._logger.debugEnabled) { this._logger.debug(`extract: failed to decode x-dynatrace ${(0, LoggingUtils_1.verboseFormatException)(e)}`); } } } return undefined; } // ------------------------------------------------------------------------ _getXdynaTrace(carrier, getter) { const xDynaTrace = getter.get(carrier, exports.X_DYNTATRACE_HEADER); return typeof (xDynaTrace) === "string" ? xDynaTrace : undefined; } // ------------------------------------------------------------------------ _getXdtc(carrier, getter) { const xDtc = getter.get(carrier, exports.X_DTC_HEADER); return typeof (xDtc) === "string" ? xDtc : undefined; } // ------------------------------------------------------------------------ _updateContextFromXDt(fw4Tag, context) { let spanContext = getSpanContext(context); // if traceId in traceparent differs from FW4 tag we have to handle this // like a new root and prune all tracestate entries if ((spanContext == null) || (spanContext.traceId !== fw4Tag.extensions.traceId)) { if ((spanContext != null) && this._logger.debugEnabled) { this._logger.debug(`extract: traceId mismatch, spanContext: ${spanContext.traceId}, fw4Tag: ${fw4Tag.extensions.traceId}`); } // create a remote span context using the parsed values from fw4 tag spanContext = { traceId: fw4Tag.extensions.traceId, spanId: fw4Tag.extensions.spanId, traceFlags: fw4Tag.isIgnored ? api_1.TraceFlags.NONE : api_1.TraceFlags.SAMPLED, isRemote: true, traceState: new core_1.TraceState() }; context = setSpanContext(context, spanContext); } else { if (spanContext.traceState == null) { spanContext.traceState = new core_1.TraceState(); } } // add/overwrite our entry in traceState spanContext.traceState = spanContext.traceState.set(this._traceStateKey, fw4Tag.toTraceStateValue()); // attach FW4 tag for later use (0, SpanEmbedder_1.setFw4Tag)(spanContext.traceState, fw4Tag); return context; } // ------------------------------------------------------------------------ _updateTraceFlags(fw4Tag, context) { const spanContext = getSpanContext(context); if (spanContext == null) { return; } let traceFlags = spanContext.traceFlags; if (fw4Tag.isIgnored) { // eslint-disable-next-line no-bitwise traceFlags &= ~api_1.TraceFlags.SAMPLED; } else { // eslint-disable-next-line no-bitwise traceFlags |= api_1.TraceFlags.SAMPLED; } spanContext.traceFlags = traceFlags; } } exports.DtTextMapPropagator = DtTextMapPropagator; //# sourceMappingURL=DtTextMapPropagator.js.map