From 3868ae59ee4d8778cb6fe0b1942c46937f8beeba Mon Sep 17 00:00:00 2001 From: "Ida.Liu" Date: Mon, 16 Dec 2024 10:10:52 -0500 Subject: [PATCH 1/4] preliminary otel baggage --- .../src/opentelemetry/context_manager.js | 29 +++++++- .../opentelemetry/context_manager.spec.js | 68 ++++++++++++++++++- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/packages/dd-trace/src/opentelemetry/context_manager.js b/packages/dd-trace/src/opentelemetry/context_manager.js index 430626bbd7e..800a309dc47 100644 --- a/packages/dd-trace/src/opentelemetry/context_manager.js +++ b/packages/dd-trace/src/opentelemetry/context_manager.js @@ -1,7 +1,7 @@ 'use strict' const { storage } = require('../../../datadog-core') -const { trace, ROOT_CONTEXT } = require('@opentelemetry/api') +const { trace, ROOT_CONTEXT, propagation } = require('@opentelemetry/api') const DataDogSpanContext = require('../opentracing/span_context') const SpanContext = require('./span_context') @@ -21,14 +21,25 @@ class ContextManager { return context } + let otelBaggages + const baggages = JSON.parse(activeSpan.getAllBaggageItems()) + if (Object.keys(baggages).length) { + const entries = {} + for (const [key, value] of Object.entries(baggages)) { + entries[key] = { value } + } + otelBaggages = propagation.createBaggage(entries) + } + if (!context._otelSpanContext) { const newSpanContext = new SpanContext(context) context._otelSpanContext = newSpanContext } if (store && trace.getSpanContext(store) === context._otelSpanContext) { - return store + return otelBaggages ? propagation.setBaggage(store, otelBaggages) : store } - return trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext) + const wrappedContext = trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext) + return otelBaggages ? propagation.setBaggage(wrappedContext, otelBaggages) : wrappedContext } with (context, fn, thisArg, ...args) { @@ -39,6 +50,18 @@ class ContextManager { return this._store.run(context, cb, ...args) } if (span && span._ddSpan) { + // remove all baggage items? + const baggages = propagation.getBaggage(context) + if (baggages) { + const baggageItems = baggages.getAllEntries() + if (baggageItems.length) { + for (const baggage of baggageItems) { + const key = baggage[0] + const value = baggage[1].value + span._ddSpan.setBaggageItem(key, value) + } + } + } return ddScope.activate(span._ddSpan, run) } return run() diff --git a/packages/dd-trace/test/opentelemetry/context_manager.spec.js b/packages/dd-trace/test/opentelemetry/context_manager.spec.js index ebf8f122d87..3121ca1df54 100644 --- a/packages/dd-trace/test/opentelemetry/context_manager.spec.js +++ b/packages/dd-trace/test/opentelemetry/context_manager.spec.js @@ -4,8 +4,17 @@ require('../setup/tap') const { expect } = require('chai') const ContextManager = require('../../src/opentelemetry/context_manager') -const { ROOT_CONTEXT } = require('@opentelemetry/api') +const TracerProvider = require('../../src/opentelemetry/tracer_provider') +const { context, propagation, trace, ROOT_CONTEXT } = require('@opentelemetry/api') const api = require('@opentelemetry/api') +const tracer = require('../../').init() + +function makeSpan (...args) { + const tracerProvider = new TracerProvider() + tracerProvider.register() + const tracer = tracerProvider.getTracer() + return tracer.startSpan(...args) +} describe('OTel Context Manager', () => { let contextManager @@ -114,4 +123,61 @@ describe('OTel Context Manager', () => { }) expect(ret).to.equal('return value') }) + + it('should propagate baggage from an otel span to a datadog span', () => { + const entries = { + foo : { value: 'bar' } + } + const baggage = propagation.createBaggage(entries) + const contextWithBaggage = propagation.setBaggage(context.active(), baggage) + const span = makeSpan('otel-to-dd') + const contextWithSpan = trace.setSpan(contextWithBaggage, span) + api.context.with(contextWithSpan, () => { + expect(span._ddSpan.getBaggageItem('foo')).to.be.equal('bar') + }) + }) + + it('should propagate baggage from a datadog span to an otel span', () => { + const baggageKey = 'raccoon' + const baggageVal = 'chunky' + const ddSpan = tracer.startSpan('dd-to-otel') + ddSpan.setBaggageItem(baggageKey, baggageVal) + tracer.scope().activate(ddSpan, () => { + const baggages = propagation.getBaggage(api.context.active()).getAllEntries() + expect(baggages.length).to.equal(1) + const baggage = baggages[0] + expect(baggage[0]).to.equal(baggageKey) + expect(baggage[1].value).to.equal(baggageVal) + }) + }) + + // it('should handle dd-otel baggage conflict on a datadog span', () => { + // // const ddSpan = tracer.startSpan('dd') + // // ddSpan.setBaggageItem('key', 'dd') + // // let baggageContext + // // tracer.scope().activate(ddSpan, () => { + // // const entries = { + // // key : { value: 'otel' }, + // // } + // // const baggage = propagation.createBaggage(entries) + // // baggageContext = propagation.setBaggage(api.context.active(), baggage) + // // }) + // // const otelSpan = makeSpan('otel') + // // const contextWithSpan = trace.setSpan(baggageContext, otelSpan) + // // api.context.with(contextWithSpan, () => { + // // expect(span._ddSpan.getBaggageItem('foo')).to.be.equal('bar') + // // }) + // tracer.trace('ddSpan', (ddSpan) => { + // ddSpan.setBaggageItem('key', 'dd') + // const otelSpan = makeSpan('otelSpan') + // const entries = { + // key : { value: 'otel' }, + // } + // const baggage = propagation.createBaggage(entries) + // baggageContext = propagation.setBaggage(otelSpan.spanContext(), baggage) + // // api.context.with(baggageContext, () => { + // // console.log(ddSpan.context()) + // // }) + // }) + // }) }) From 3524dbfcbc75f4c84f3ff5d154b64876629ee8ce Mon Sep 17 00:00:00 2001 From: "Ida.Liu" Date: Sun, 5 Jan 2025 18:10:24 -0500 Subject: [PATCH 2/4] updated otel drop in support for baggage --- .../src/opentelemetry/context_manager.js | 54 +++++++++----- .../opentelemetry/context_manager.spec.js | 74 +++++++++++-------- 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/packages/dd-trace/src/opentelemetry/context_manager.js b/packages/dd-trace/src/opentelemetry/context_manager.js index 800a309dc47..e190d1367fc 100644 --- a/packages/dd-trace/src/opentelemetry/context_manager.js +++ b/packages/dd-trace/src/opentelemetry/context_manager.js @@ -18,28 +18,39 @@ class ContextManager { const context = (activeSpan && activeSpan.context()) || store || ROOT_CONTEXT if (!(context instanceof DataDogSpanContext)) { + const span = trace.getSpan(context) + // span instanceof NonRecordingSpan + if (span && span._spanContext && span._spanContext._ddContext && span._spanContext._ddContext._baggageItems) { + const baggages = span._spanContext._ddContext._baggageItems + const entries = {} + for (const [key, value] of Object.entries(baggages)) { + entries[key] = { value } + } + const otelBaggages = propagation.createBaggage(entries) + return propagation.setBaggage(context, otelBaggages) + } return context } let otelBaggages const baggages = JSON.parse(activeSpan.getAllBaggageItems()) - if (Object.keys(baggages).length) { - const entries = {} - for (const [key, value] of Object.entries(baggages)) { - entries[key] = { value } - } - otelBaggages = propagation.createBaggage(entries) + const entries = {} + for (const [key, value] of Object.entries(baggages)) { + entries[key] = { value } } + otelBaggages = propagation.createBaggage(entries) if (!context._otelSpanContext) { const newSpanContext = new SpanContext(context) context._otelSpanContext = newSpanContext } if (store && trace.getSpanContext(store) === context._otelSpanContext) { - return otelBaggages ? propagation.setBaggage(store, otelBaggages) : store + return otelBaggages ? propagation.setBaggage(store, otelBaggages) + : store } const wrappedContext = trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext) - return otelBaggages ? propagation.setBaggage(wrappedContext, otelBaggages) : wrappedContext + return otelBaggages ? propagation.setBaggage(wrappedContext, otelBaggages) + : wrappedContext } with (context, fn, thisArg, ...args) { @@ -49,21 +60,26 @@ class ContextManager { const cb = thisArg == null ? fn : fn.bind(thisArg) return this._store.run(context, cb, ...args) } + const baggages = propagation.getBaggage(context) + let baggageItems = [] + if (baggages) { + baggageItems = baggages.getAllEntries() + } if (span && span._ddSpan) { - // remove all baggage items? - const baggages = propagation.getBaggage(context) - if (baggages) { - const baggageItems = baggages.getAllEntries() - if (baggageItems.length) { - for (const baggage of baggageItems) { - const key = baggage[0] - const value = baggage[1].value - span._ddSpan.setBaggageItem(key, value) - } - } + // does otel always override datadog? + span._ddSpan.removeAllBaggageItems() + for (const baggage of baggageItems) { + span._ddSpan.setBaggageItem(baggage[0], baggage[1].value) } return ddScope.activate(span._ddSpan, run) } + // span instanceof NonRecordingSpan + if (span && span._spanContext && span._spanContext._ddContext && span._spanContext._ddContext._baggageItems) { + span._spanContext._ddContext._baggageItems = {} + for (const baggage of baggageItems) { + span._spanContext._ddContext._baggageItems[baggage[0]] = baggage[1].value + } + } return run() } diff --git a/packages/dd-trace/test/opentelemetry/context_manager.spec.js b/packages/dd-trace/test/opentelemetry/context_manager.spec.js index 3121ca1df54..61b33750eb7 100644 --- a/packages/dd-trace/test/opentelemetry/context_manager.spec.js +++ b/packages/dd-trace/test/opentelemetry/context_manager.spec.js @@ -126,7 +126,7 @@ describe('OTel Context Manager', () => { it('should propagate baggage from an otel span to a datadog span', () => { const entries = { - foo : { value: 'bar' } + foo: { value: 'bar' } } const baggage = propagation.createBaggage(entries) const contextWithBaggage = propagation.setBaggage(context.active(), baggage) @@ -151,33 +151,47 @@ describe('OTel Context Manager', () => { }) }) - // it('should handle dd-otel baggage conflict on a datadog span', () => { - // // const ddSpan = tracer.startSpan('dd') - // // ddSpan.setBaggageItem('key', 'dd') - // // let baggageContext - // // tracer.scope().activate(ddSpan, () => { - // // const entries = { - // // key : { value: 'otel' }, - // // } - // // const baggage = propagation.createBaggage(entries) - // // baggageContext = propagation.setBaggage(api.context.active(), baggage) - // // }) - // // const otelSpan = makeSpan('otel') - // // const contextWithSpan = trace.setSpan(baggageContext, otelSpan) - // // api.context.with(contextWithSpan, () => { - // // expect(span._ddSpan.getBaggageItem('foo')).to.be.equal('bar') - // // }) - // tracer.trace('ddSpan', (ddSpan) => { - // ddSpan.setBaggageItem('key', 'dd') - // const otelSpan = makeSpan('otelSpan') - // const entries = { - // key : { value: 'otel' }, - // } - // const baggage = propagation.createBaggage(entries) - // baggageContext = propagation.setBaggage(otelSpan.spanContext(), baggage) - // // api.context.with(baggageContext, () => { - // // console.log(ddSpan.context()) - // // }) - // }) - // }) + it('should handle dd-otel baggage conflict', () => { + const ddSpan = tracer.startSpan('dd') + ddSpan.setBaggageItem('key1', 'dd1') + let contextWithUpdatedBaggages + tracer.scope().activate(ddSpan, () => { + let baggages = propagation.getBaggage(api.context.active()) + baggages = baggages.setEntry('key1', { value: 'otel1' }) + baggages = baggages.setEntry('key2', { value: 'otel2' }) + contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages) + }) + expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal({ 'key1': 'dd1' }) + api.context.with(contextWithUpdatedBaggages, () => { + expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( + { 'key1': 'otel1', 'key2': 'otel2' } + ) + ddSpan.setBaggageItem('key2', 'dd2') + expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal( + [ [ 'key1', { value: 'otel1' } ], [ 'key2', { value: 'dd2' } ] ] + ) + }) + }) + + it('should handle dd-otel baggage removal', () => { + const ddSpan = tracer.startSpan('dd') + ddSpan.setBaggageItem('key1', 'dd1') + ddSpan.setBaggageItem('key2', 'dd2') + let contextWithUpdatedBaggages + tracer.scope().activate(ddSpan, () => { + let baggages = propagation.getBaggage(api.context.active()) + baggages = baggages.removeEntry('key1') + contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages) + }) + expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( + { 'key1': 'dd1', 'key2': 'dd2' } + ) + api.context.with(contextWithUpdatedBaggages, () => { + expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( + { 'key2': 'dd2' } + ) + ddSpan.removeBaggageItem('key2') + expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal([]) + }) + }) }) From 8f743c8998f2dce46c458529adcd838f9b1c825d Mon Sep 17 00:00:00 2001 From: "Ida.Liu" Date: Sun, 5 Jan 2025 18:41:48 -0500 Subject: [PATCH 3/4] fix lint errors --- .../dd-trace/src/opentelemetry/context_manager.js | 13 +++++++------ .../test/opentelemetry/context_manager.spec.js | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/dd-trace/src/opentelemetry/context_manager.js b/packages/dd-trace/src/opentelemetry/context_manager.js index e190d1367fc..99df0b13054 100644 --- a/packages/dd-trace/src/opentelemetry/context_manager.js +++ b/packages/dd-trace/src/opentelemetry/context_manager.js @@ -32,25 +32,26 @@ class ContextManager { return context } - let otelBaggages const baggages = JSON.parse(activeSpan.getAllBaggageItems()) const entries = {} for (const [key, value] of Object.entries(baggages)) { entries[key] = { value } } - otelBaggages = propagation.createBaggage(entries) + const otelBaggages = propagation.createBaggage(entries) if (!context._otelSpanContext) { const newSpanContext = new SpanContext(context) context._otelSpanContext = newSpanContext } if (store && trace.getSpanContext(store) === context._otelSpanContext) { - return otelBaggages ? propagation.setBaggage(store, otelBaggages) - : store + return otelBaggages + ? propagation.setBaggage(store, otelBaggages) + : store } const wrappedContext = trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext) - return otelBaggages ? propagation.setBaggage(wrappedContext, otelBaggages) - : wrappedContext + return otelBaggages + ? propagation.setBaggage(wrappedContext, otelBaggages) + : wrappedContext } with (context, fn, thisArg, ...args) { diff --git a/packages/dd-trace/test/opentelemetry/context_manager.spec.js b/packages/dd-trace/test/opentelemetry/context_manager.spec.js index 61b33750eb7..517690ab118 100644 --- a/packages/dd-trace/test/opentelemetry/context_manager.spec.js +++ b/packages/dd-trace/test/opentelemetry/context_manager.spec.js @@ -161,14 +161,14 @@ describe('OTel Context Manager', () => { baggages = baggages.setEntry('key2', { value: 'otel2' }) contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages) }) - expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal({ 'key1': 'dd1' }) + expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal({ key1: 'dd1' }) api.context.with(contextWithUpdatedBaggages, () => { expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( - { 'key1': 'otel1', 'key2': 'otel2' } + { key1: 'otel1', key2: 'otel2' } ) ddSpan.setBaggageItem('key2', 'dd2') expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal( - [ [ 'key1', { value: 'otel1' } ], [ 'key2', { value: 'dd2' } ] ] + [['key1', { value: 'otel1' }], ['key2', { value: 'dd2' }]] ) }) }) @@ -184,11 +184,11 @@ describe('OTel Context Manager', () => { contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages) }) expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( - { 'key1': 'dd1', 'key2': 'dd2' } + { key1: 'dd1', key2: 'dd2' } ) api.context.with(contextWithUpdatedBaggages, () => { expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal( - { 'key2': 'dd2' } + { key2: 'dd2' } ) ddSpan.removeBaggageItem('key2') expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal([]) From e191372db5091cf258139fcc2e46fe50279a8450 Mon Sep 17 00:00:00 2001 From: "Ida.Liu" Date: Tue, 7 Jan 2025 14:24:49 -0500 Subject: [PATCH 4/4] improve readability --- packages/dd-trace/test/opentelemetry/context_manager.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dd-trace/test/opentelemetry/context_manager.spec.js b/packages/dd-trace/test/opentelemetry/context_manager.spec.js index 517690ab118..aefd0ec6f54 100644 --- a/packages/dd-trace/test/opentelemetry/context_manager.spec.js +++ b/packages/dd-trace/test/opentelemetry/context_manager.spec.js @@ -133,7 +133,7 @@ describe('OTel Context Manager', () => { const span = makeSpan('otel-to-dd') const contextWithSpan = trace.setSpan(contextWithBaggage, span) api.context.with(contextWithSpan, () => { - expect(span._ddSpan.getBaggageItem('foo')).to.be.equal('bar') + expect(tracer.scope().active().getBaggageItem('foo')).to.be.equal('bar') }) }) @@ -143,7 +143,7 @@ describe('OTel Context Manager', () => { const ddSpan = tracer.startSpan('dd-to-otel') ddSpan.setBaggageItem(baggageKey, baggageVal) tracer.scope().activate(ddSpan, () => { - const baggages = propagation.getBaggage(api.context.active()).getAllEntries() + const baggages = propagation.getActiveBaggage().getAllEntries() expect(baggages.length).to.equal(1) const baggage = baggages[0] expect(baggage[0]).to.equal(baggageKey)