@opentelemetry/propagator-instana
Version:
The OpenTelemetry Instana Propagator implements the propagation format used by IBM Observability by Instana
98 lines • 4.38 kB
JavaScript
;
/*
* Copyright The OpenTelemetry Authors
*
* 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
*
* https://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.InstanaPropagator = exports.INSTANA_LEVEL_HEADER = exports.INSTANA_SPAN_ID_HEADER = exports.INSTANA_TRACE_ID_HEADER = void 0;
const api_1 = require("@opentelemetry/api");
/** Instana header names */
exports.INSTANA_TRACE_ID_HEADER = 'X-INSTANA-T';
exports.INSTANA_SPAN_ID_HEADER = 'X-INSTANA-S';
exports.INSTANA_LEVEL_HEADER = 'X-INSTANA-L';
const FIELDS = [
exports.INSTANA_TRACE_ID_HEADER,
exports.INSTANA_SPAN_ID_HEADER,
exports.INSTANA_LEVEL_HEADER,
];
function readHeader(carrier, getter, key) {
// By convention, X-INSTANA-* headers are sent all-upper-case. For http and http/2, Node.js normalizes all headers to
// all-lower-case, that's why we first read via the lower case variant of the header name. For other protocols, no
// such normalization happens, so we also need to read via the original all-upper-case variant.
let header = getter.get(carrier, key.toLowerCase()) || getter.get(carrier, key);
if (Array.isArray(header))
[header] = header;
return header || '';
}
/**
* Propagator for the Instana HTTP headers.
*/
class InstanaPropagator {
/**
* Injects the current span context into Instana's vendor specific trace correlation headers (X-INSTANA-T, X-INSTANA-S
* and X-INSTANA-L).
*/
inject(context, carrier, setter) {
const spanContext = api_1.trace.getSpan(context)?.spanContext();
if (!spanContext || !(0, api_1.isSpanContextValid)(spanContext)) {
return;
}
setter.set(carrier, exports.INSTANA_TRACE_ID_HEADER, spanContext.traceId);
setter.set(carrier, exports.INSTANA_SPAN_ID_HEADER, spanContext.spanId);
const sampled = (spanContext.traceFlags & api_1.TraceFlags.SAMPLED) === api_1.TraceFlags.SAMPLED;
setter.set(carrier, exports.INSTANA_LEVEL_HEADER, sampled ? '1' : '0');
}
/**
* Extracts the span context from Instana's vendor specific trace correlation headers (X-INSTANA-T, X-INSTANA-S
* and X-INSTANA-L).
*/
extract(context, carrier, getter) {
let traceId = readHeader(carrier, getter, exports.INSTANA_TRACE_ID_HEADER);
if (traceId && traceId.length < 32) {
traceId = traceId.padStart(32, '0');
}
let spanId = readHeader(carrier, getter, exports.INSTANA_SPAN_ID_HEADER);
if (spanId && spanId.length < 16) {
spanId = spanId.padStart(16, '0');
}
const level = readHeader(carrier, getter, exports.INSTANA_LEVEL_HEADER) || '1';
const sampled = level !== '0';
const traceFlags = sampled ? api_1.TraceFlags.SAMPLED : api_1.TraceFlags.NONE;
if ((0, api_1.isValidTraceId)(traceId) && (0, api_1.isValidSpanId)(spanId)) {
context = api_1.trace.setSpan(context, api_1.trace.wrapSpanContext({
traceId,
spanId,
isRemote: true,
traceFlags,
}));
}
else if (!sampled) {
// Even if we do not receive a trace ID/span ID via X-INSTANA-T/X-INSTANA-S, if there is an explicit incoming
// X-INSTANA-L=0, we want to capture that via trace flags. For Instana tracers, it is perfectly valid to send
// X-INSTANA-L=0 without X-INSTANA-T/-S.
context = api_1.trace.setSpan(context, api_1.trace.wrapSpanContext({
traceId: api_1.INVALID_TRACEID,
spanId: api_1.INVALID_SPANID,
isRemote: true,
traceFlags,
}));
}
return context;
}
fields() {
return FIELDS.slice();
}
}
exports.InstanaPropagator = InstanaPropagator;
//# sourceMappingURL=InstanaPropagator.js.map