packages/opentelemetry-cloud-trace-propagator/src/CloudPropagator.ts (61 lines of code) (raw):

// Copyright 2020 Google 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. import { TextMapPropagator, Context, TraceFlags, trace, TextMapSetter, TextMapGetter, } from '@opentelemetry/api'; import {decToHex, hexToDec} from 'hex2dec'; /** * This file implements propagation for the Stackdriver Trace v1 Trace Context * format. * * The header specification is: * "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE" * Where: * {TRACE_ID} is a 32-character hexadecimal value representing a 128-bit * number. It should be unique between your requests, unless you * intentionally want to bundle the requests together. * {SPAN_ID} is the decimal representation of the (unsigned) span ID. * It should be randomly generated and unique in your trace. For subsequent requests, * set SPAN_ID to the span ID of the parent request. * {TRACE_TRUE} must be 1 to trace request. Specify 0 to not trace the request. */ /** Header that carries span context across Google infrastructure. */ export const X_CLOUD_TRACE_HEADER = 'x-cloud-trace-context'; const FIELDS = [X_CLOUD_TRACE_HEADER]; export class CloudPropagator implements TextMapPropagator { inject( context: Context, carrier: unknown, setter: TextMapSetter<unknown> ): void { const spanContext = trace.getSpanContext(context); if (!spanContext) return; const header = `${spanContext.traceId}/${hexToDec(spanContext.spanId)};o=${ spanContext.traceFlags & TraceFlags.SAMPLED }`; setter.set(carrier, X_CLOUD_TRACE_HEADER, header); } extract( context: Context, carrier: unknown, getter: TextMapGetter<unknown> ): Context { const traceContextHeader = getter.get(carrier, X_CLOUD_TRACE_HEADER); const traceContextHeaderValue = Array.isArray(traceContextHeader) ? traceContextHeader[0] : traceContextHeader; if (typeof traceContextHeaderValue !== 'string') { return context; } const matches = traceContextHeaderValue.match( /^([0-9a-fA-F]{32})(?:\/([0-9]+))(?:;o=(.*))?/ ); if ( !matches || matches[1] === '00000000000000000000000000000000' || matches[2] === '0000000000000000' ) { return context; } const spanContext = { traceId: matches[1], spanId: decToHex(matches[2], {prefix: false}).padStart(16, '0'), traceFlags: isNaN(Number(matches[3])) || matches[3] === '0' ? TraceFlags.NONE : TraceFlags.SAMPLED, isRemote: true, }; return trace.setSpanContext(context, spanContext); } fields(): string[] { return FIELDS; } }