UNPKG

@uoa/lambda-tracing

Version:

Library for logging & distributed tracing in UoA Lambda projects

155 lines (154 loc) 6.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UoaB3Propagator = exports.B3_PARENT_SPAN_ID_KEY = exports.B3_TRACE_ID_LENGTH_KEY = exports.B3_INFO_KEY = exports.B3_DEBUG_FLAG_KEY = exports.X_B3_INFO = exports.X_B3_FLAGS = exports.X_B3_PARENT_SPAN_ID = exports.X_B3_SAMPLED = exports.X_B3_SPAN_ID = exports.X_B3_TRACE_ID = void 0; /** * Class extended from OpenTelemetry B3MultiPropagator so that we can: * 1. Propagate & log the X-B3-Info header * 2. Propagate the correct X-B3-ParentSpanId * * See {@link https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-propagator-b3/src/B3MultiPropagator.ts} */ const api_1 = require("@opentelemetry/api"); const core_1 = require("@opentelemetry/core"); const tracing_1 = require("./tracing"); const crypto = require('crypto'); exports.X_B3_TRACE_ID = 'x-b3-traceid'; exports.X_B3_SPAN_ID = 'x-b3-spanid'; exports.X_B3_SAMPLED = 'x-b3-sampled'; exports.X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid'; exports.X_B3_FLAGS = 'x-b3-flags'; exports.X_B3_INFO = 'x-b3-info'; exports.B3_DEBUG_FLAG_KEY = (0, api_1.createContextKey)('B3 Debug Flag'); exports.B3_INFO_KEY = (0, api_1.createContextKey)('B3 Info Header'); exports.B3_TRACE_ID_LENGTH_KEY = (0, api_1.createContextKey)('B3 TraceId Header Length'); exports.B3_PARENT_SPAN_ID_KEY = (0, api_1.createContextKey)('B3 Parent Span Id'); const VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]); const VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]); function parseHeader(header) { return Array.isArray(header) ? header[0] : header; } function getHeaderValue(carrier, getter, key) { const header = getter.get(carrier, key); return parseHeader(header); } function getTraceIdLength(carrier, getter) { const traceId = getHeaderValue(carrier, getter, exports.X_B3_TRACE_ID); if (typeof traceId === 'string') { if (traceId.length <= 16 || traceId.length > 32) { return 16; } else { return 32; } } return 16; } function getTraceId(carrier, getter) { const traceId = getHeaderValue(carrier, getter, exports.X_B3_TRACE_ID); if (typeof traceId === 'string') { return traceId.padStart(32, '0'); } return ''; } function getSpanId(carrier, getter) { const spanId = getHeaderValue(carrier, getter, exports.X_B3_SPAN_ID); if (typeof spanId === 'string') { return spanId; } return ''; } function getParentSpanId(carrier, getter) { const parentSpanId = getHeaderValue(carrier, getter, exports.X_B3_PARENT_SPAN_ID); if (typeof parentSpanId === 'string') { return parentSpanId; } return ''; } function getDebug(carrier, getter) { const debug = getHeaderValue(carrier, getter, exports.X_B3_FLAGS); return debug === '1' ? '1' : undefined; } function getTraceFlags(carrier, getter) { const traceFlags = getHeaderValue(carrier, getter, exports.X_B3_SAMPLED); const debug = getDebug(carrier, getter); if (debug === '1' || VALID_SAMPLED_VALUES.has(traceFlags)) { return api_1.TraceFlags.SAMPLED; } if (traceFlags === undefined || VALID_UNSAMPLED_VALUES.has(traceFlags)) { return api_1.TraceFlags.NONE; } // This indicates to isValidSampledValue that this is not valid return; } function getInfo(carrier, getter) { const info = getHeaderValue(carrier, getter, exports.X_B3_INFO); if (typeof info === 'string') { return info; } return undefined; } class UoaB3Propagator { inject(context, carrier, setter) { const spanContext = api_1.trace.getSpanContext(context); if (!spanContext || !(0, api_1.isSpanContextValid)(spanContext) || (0, core_1.isTracingSuppressed)(context)) return; const debug = context.getValue(exports.B3_DEBUG_FLAG_KEY); const traceIdLength = context.getValue(exports.B3_TRACE_ID_LENGTH_KEY); setter.set(carrier, exports.X_B3_TRACE_ID, spanContext.traceId.slice(spanContext.traceId.length - traceIdLength)); setter.set(carrier, exports.X_B3_SPAN_ID, crypto.randomBytes(8).toString('hex')); setter.set(carrier, exports.X_B3_PARENT_SPAN_ID, spanContext.spanId); const info = context.getValue(exports.B3_INFO_KEY); if ((0, tracing_1.getTraceInfoHeader)()) { setter.set(carrier, exports.X_B3_INFO, (0, tracing_1.getTraceInfoHeader)()); } else if (info && typeof info === 'string') { setter.set(carrier, exports.X_B3_INFO, info.toString()); } // According to the B3 spec, if the debug flag is set, // the sampled flag shouldn't be propagated as well. if (debug === '1') { setter.set(carrier, exports.X_B3_FLAGS, debug); } else if (spanContext.traceFlags !== undefined) { // We set the header only if there is an existing sampling decision. // Otherwise we will omit it => Absent. setter.set(carrier, exports.X_B3_SAMPLED, (api_1.TraceFlags.SAMPLED & spanContext.traceFlags) === api_1.TraceFlags.SAMPLED ? '1' : '0'); } } extract(context, carrier, getter) { const traceIdLength = getTraceIdLength(carrier, getter); context = context.setValue(exports.B3_TRACE_ID_LENGTH_KEY, traceIdLength); const traceId = getTraceId(carrier, getter); const spanId = getSpanId(carrier, getter); const parentSpanId = getParentSpanId(carrier, getter); context = context.setValue(exports.B3_PARENT_SPAN_ID_KEY, parentSpanId); const traceFlags = getTraceFlags(carrier, getter); const debug = getDebug(carrier, getter); const info = getInfo(carrier, getter); context = context.setValue(exports.B3_DEBUG_FLAG_KEY, debug); if (info) { context = context.setValue(exports.B3_INFO_KEY, info); } return api_1.trace.setSpanContext(context, { traceId, spanId, isRemote: true, traceFlags, }); } fields() { return [ exports.X_B3_TRACE_ID, exports.X_B3_SPAN_ID, exports.X_B3_FLAGS, exports.X_B3_SAMPLED, exports.X_B3_PARENT_SPAN_ID, exports.X_B3_INFO ]; } } exports.UoaB3Propagator = UoaB3Propagator;