Skip to content

Commit 149742b

Browse files
authored
otel drop in support for baggage (#5019)
1 parent 708b627 commit 149742b

2 files changed

Lines changed: 124 additions & 4 deletions

File tree

packages/dd-trace/src/opentelemetry/context_manager.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const { storage } = require('../../../datadog-core')
4-
const { trace, ROOT_CONTEXT } = require('@opentelemetry/api')
4+
const { trace, ROOT_CONTEXT, propagation } = require('@opentelemetry/api')
55
const DataDogSpanContext = require('../opentracing/span_context')
66

77
const SpanContext = require('./span_context')
@@ -18,17 +18,40 @@ class ContextManager {
1818
const context = (activeSpan && activeSpan.context()) || store || ROOT_CONTEXT
1919

2020
if (!(context instanceof DataDogSpanContext)) {
21+
const span = trace.getSpan(context)
22+
// span instanceof NonRecordingSpan
23+
if (span && span._spanContext && span._spanContext._ddContext && span._spanContext._ddContext._baggageItems) {
24+
const baggages = span._spanContext._ddContext._baggageItems
25+
const entries = {}
26+
for (const [key, value] of Object.entries(baggages)) {
27+
entries[key] = { value }
28+
}
29+
const otelBaggages = propagation.createBaggage(entries)
30+
return propagation.setBaggage(context, otelBaggages)
31+
}
2132
return context
2233
}
2334

35+
const baggages = JSON.parse(activeSpan.getAllBaggageItems())
36+
const entries = {}
37+
for (const [key, value] of Object.entries(baggages)) {
38+
entries[key] = { value }
39+
}
40+
const otelBaggages = propagation.createBaggage(entries)
41+
2442
if (!context._otelSpanContext) {
2543
const newSpanContext = new SpanContext(context)
2644
context._otelSpanContext = newSpanContext
2745
}
2846
if (store && trace.getSpanContext(store) === context._otelSpanContext) {
29-
return store
47+
return otelBaggages
48+
? propagation.setBaggage(store, otelBaggages)
49+
: store
3050
}
31-
return trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext)
51+
const wrappedContext = trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext)
52+
return otelBaggages
53+
? propagation.setBaggage(wrappedContext, otelBaggages)
54+
: wrappedContext
3255
}
3356

3457
with (context, fn, thisArg, ...args) {
@@ -38,9 +61,26 @@ class ContextManager {
3861
const cb = thisArg == null ? fn : fn.bind(thisArg)
3962
return this._store.run(context, cb, ...args)
4063
}
64+
const baggages = propagation.getBaggage(context)
65+
let baggageItems = []
66+
if (baggages) {
67+
baggageItems = baggages.getAllEntries()
68+
}
4169
if (span && span._ddSpan) {
70+
// does otel always override datadog?
71+
span._ddSpan.removeAllBaggageItems()
72+
for (const baggage of baggageItems) {
73+
span._ddSpan.setBaggageItem(baggage[0], baggage[1].value)
74+
}
4275
return ddScope.activate(span._ddSpan, run)
4376
}
77+
// span instanceof NonRecordingSpan
78+
if (span && span._spanContext && span._spanContext._ddContext && span._spanContext._ddContext._baggageItems) {
79+
span._spanContext._ddContext._baggageItems = {}
80+
for (const baggage of baggageItems) {
81+
span._spanContext._ddContext._baggageItems[baggage[0]] = baggage[1].value
82+
}
83+
}
4484
return run()
4585
}
4686

packages/dd-trace/test/opentelemetry/context_manager.spec.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@ require('../setup/tap')
44

55
const { expect } = require('chai')
66
const ContextManager = require('../../src/opentelemetry/context_manager')
7-
const { ROOT_CONTEXT } = require('@opentelemetry/api')
7+
const TracerProvider = require('../../src/opentelemetry/tracer_provider')
8+
const { context, propagation, trace, ROOT_CONTEXT } = require('@opentelemetry/api')
89
const api = require('@opentelemetry/api')
10+
const tracer = require('../../').init()
11+
12+
function makeSpan (...args) {
13+
const tracerProvider = new TracerProvider()
14+
tracerProvider.register()
15+
const tracer = tracerProvider.getTracer()
16+
return tracer.startSpan(...args)
17+
}
918

1019
describe('OTel Context Manager', () => {
1120
let contextManager
@@ -114,4 +123,75 @@ describe('OTel Context Manager', () => {
114123
})
115124
expect(ret).to.equal('return value')
116125
})
126+
127+
it('should propagate baggage from an otel span to a datadog span', () => {
128+
const entries = {
129+
foo: { value: 'bar' }
130+
}
131+
const baggage = propagation.createBaggage(entries)
132+
const contextWithBaggage = propagation.setBaggage(context.active(), baggage)
133+
const span = makeSpan('otel-to-dd')
134+
const contextWithSpan = trace.setSpan(contextWithBaggage, span)
135+
api.context.with(contextWithSpan, () => {
136+
expect(tracer.scope().active().getBaggageItem('foo')).to.be.equal('bar')
137+
})
138+
})
139+
140+
it('should propagate baggage from a datadog span to an otel span', () => {
141+
const baggageKey = 'raccoon'
142+
const baggageVal = 'chunky'
143+
const ddSpan = tracer.startSpan('dd-to-otel')
144+
ddSpan.setBaggageItem(baggageKey, baggageVal)
145+
tracer.scope().activate(ddSpan, () => {
146+
const baggages = propagation.getActiveBaggage().getAllEntries()
147+
expect(baggages.length).to.equal(1)
148+
const baggage = baggages[0]
149+
expect(baggage[0]).to.equal(baggageKey)
150+
expect(baggage[1].value).to.equal(baggageVal)
151+
})
152+
})
153+
154+
it('should handle dd-otel baggage conflict', () => {
155+
const ddSpan = tracer.startSpan('dd')
156+
ddSpan.setBaggageItem('key1', 'dd1')
157+
let contextWithUpdatedBaggages
158+
tracer.scope().activate(ddSpan, () => {
159+
let baggages = propagation.getBaggage(api.context.active())
160+
baggages = baggages.setEntry('key1', { value: 'otel1' })
161+
baggages = baggages.setEntry('key2', { value: 'otel2' })
162+
contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages)
163+
})
164+
expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal({ key1: 'dd1' })
165+
api.context.with(contextWithUpdatedBaggages, () => {
166+
expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal(
167+
{ key1: 'otel1', key2: 'otel2' }
168+
)
169+
ddSpan.setBaggageItem('key2', 'dd2')
170+
expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal(
171+
[['key1', { value: 'otel1' }], ['key2', { value: 'dd2' }]]
172+
)
173+
})
174+
})
175+
176+
it('should handle dd-otel baggage removal', () => {
177+
const ddSpan = tracer.startSpan('dd')
178+
ddSpan.setBaggageItem('key1', 'dd1')
179+
ddSpan.setBaggageItem('key2', 'dd2')
180+
let contextWithUpdatedBaggages
181+
tracer.scope().activate(ddSpan, () => {
182+
let baggages = propagation.getBaggage(api.context.active())
183+
baggages = baggages.removeEntry('key1')
184+
contextWithUpdatedBaggages = propagation.setBaggage(api.context.active(), baggages)
185+
})
186+
expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal(
187+
{ key1: 'dd1', key2: 'dd2' }
188+
)
189+
api.context.with(contextWithUpdatedBaggages, () => {
190+
expect(JSON.parse(ddSpan.getAllBaggageItems())).to.deep.equal(
191+
{ key2: 'dd2' }
192+
)
193+
ddSpan.removeBaggageItem('key2')
194+
expect(propagation.getActiveBaggage().getAllEntries()).to.deep.equal([])
195+
})
196+
})
117197
})

0 commit comments

Comments
 (0)