From 768fd0514e8291c2d44f07a70684a8e168b1e555 Mon Sep 17 00:00:00 2001 From: Ruben Vargas Date: Tue, 3 Mar 2020 15:34:54 -0600 Subject: [PATCH] feat: implement W3C Correlation Context propagator Signed-off-by: Ruben Vargas --- .../distribute-context/distribute-context.ts | 37 ++++++++++ .../propagation/HttpTraceContext.ts | 74 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 packages/opentelemetry-core/src/distribute-context/distribute-context.ts create mode 100644 packages/opentelemetry-core/src/distribute-context/propagation/HttpTraceContext.ts diff --git a/packages/opentelemetry-core/src/distribute-context/distribute-context.ts b/packages/opentelemetry-core/src/distribute-context/distribute-context.ts new file mode 100644 index 00000000000..7078807b77e --- /dev/null +++ b/packages/opentelemetry-core/src/distribute-context/distribute-context.ts @@ -0,0 +1,37 @@ +/*! + * Copyright 2020, 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. + */ + +import { DistributedContext } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/scope-base'; + +const DISTRIBUTE_CONTEXTS = Context.createKey( + 'OpenTelemetry Distributed Contexts Key' +); + +export function getDistributedContext( + context: Context +): DistributedContext | undefined { + return ( + (context.getValue(DISTRIBUTE_CONTEXTS) as DistributedContext) || undefined + ); +} + +export function setDistributedContext( + context: Context, + distributedContext: DistributedContext +): Context { + return context.setValue(DISTRIBUTE_CONTEXTS, distributedContext); +} diff --git a/packages/opentelemetry-core/src/distribute-context/propagation/HttpTraceContext.ts b/packages/opentelemetry-core/src/distribute-context/propagation/HttpTraceContext.ts new file mode 100644 index 00000000000..30a7cf2f6de --- /dev/null +++ b/packages/opentelemetry-core/src/distribute-context/propagation/HttpTraceContext.ts @@ -0,0 +1,74 @@ +/*! + * Copyright 2020, 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. + */ + +import { + Carrier, + Context, + HttpTextFormat, + DistributedContext, +} from '@opentelemetry/api'; + +import { + getDistributedContext, + setDistributedContext, +} from '../distribute-context'; + +export const CORRELATION_CONTEXT_HEADER = 'Correlation-Context'; + +/** + * Propagates {@link DistributedContext} through Context format propagation. + * + * Based on the Correlation Context specification: + * https://w3c.github.io/correlation-context/ + */ +export class HttpTraceContext implements HttpTextFormat { + inject(context: Context, carrier: Carrier) { + const distContext = getDistributedContext(context); + if (distContext) { + const headerValue = Object.keys(distContext) + .map( + (key: string) => + `${encodeURIComponent(key)}=${encodeURIComponent( + distContext[key].value + )}` + ) + .join(','); + if (headerValue.length > 0) { + carrier[CORRELATION_CONTEXT_HEADER] = headerValue; + } + } + } + + extract(context: Context, carrier: Carrier): Context { + const headerValue: string = carrier[CORRELATION_CONTEXT_HEADER] as string; + const distributedContext: DistributedContext = {}; + if (headerValue.length > 0) { + headerValue.split(',').forEach(entry => { + const valueProps = entry.split(';'); + if (valueProps.length > 0) { + const keyPair = valueProps[0].split('='); + const key = decodeURIComponent(keyPair[0].trim()); + let value = decodeURIComponent(keyPair[1].trim()); + if (valueProps.length > 1) { + value = value + ';' + valueProps.join(';'); + } + distributedContext[key] = { value }; + } + }); + } + return setDistributedContext(context, distributedContext); + } +}