Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/dd-trace/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module.exports = {
SPAN_SAMPLING_RULE_RATE: '_dd.span_sampling.rule_rate',
SPAN_SAMPLING_MAX_PER_SECOND: '_dd.span_sampling.max_per_second',
SVC_SRC_KEY: '_dd.svc_src',
SDK_OTLP_EXPORT_KEY: '_dd.sdk.otlp_export',
SDK_SEMANTICS_KEY: 'datadog.sdk.semantics',
DATADOG_LAMBDA_EXTENSION_PATH: '/opt/extensions/datadog-agent',
DATADOG_MINI_AGENT_PATH: '/tmp/datadog/mini_agent_ready',
DECISION_MAKER_KEY: '_dd.p.dm',
Expand Down
5 changes: 5 additions & 0 deletions packages/dd-trace/src/opentelemetry/trace/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const { VERSION } = require('../../../../../version')
const { SDK_OTLP_EXPORT_KEY, SDK_SEMANTICS_KEY } = require('../../constants')
const { registerResourceAttributeRefresh } = require('../resource-attributes')
const OtlpHttpTraceExporter = require('./otlp_http_trace_exporter')

Expand Down Expand Up @@ -46,6 +47,10 @@ function buildResourceAttributes (config) {
const { service, version, env, ...filteredTags } = config.tags
Object.assign(resourceAttributes, filteredTags)

// Tracer-owned adoption markers; set after the global tags so a user tag can't contradict them.
resourceAttributes[SDK_OTLP_EXPORT_KEY] = 'true'
resourceAttributes[SDK_SEMANTICS_KEY] = config.DD_TRACE_OTEL_SEMANTICS_ENABLED ? 'otel' : 'datadog'

if (config.OTEL_TRACES_SPAN_METRICS_ENABLED) {
resourceAttributes['_dd.stats_computed'] = 'true'
}
Expand Down
7 changes: 6 additions & 1 deletion packages/dd-trace/src/span_processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const SpanSampler = require('./span_sampler')
const GitMetadataTagger = require('./git_metadata_tagger')
const processTags = require('./process-tags')
const { applyHttpOtelSemantics } = require('./plugins/util/http-otel-semantics')
const { APM_TRACING_ENABLED_KEY } = require('./constants')
const { APM_TRACING_ENABLED_KEY, SDK_OTLP_EXPORT_KEY } = require('./constants')

const startedSpans = new WeakSet()
const finishedSpans = new WeakSet()
Expand All @@ -31,6 +31,7 @@ class SpanProcessor {
this._processTags = config.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED
? processTags.serialized
: false
this._nativeExport = config.OTEL_TRACES_EXPORTER !== 'otlp'
}

sample (span) {
Expand Down Expand Up @@ -80,6 +81,10 @@ class SpanProcessor {
if (stampApmDisabled) {
formattedSpan.metrics[APM_TRACING_ENABLED_KEY] = 0
}
// Stamped after formatting so a span tag with the same key can't override it.
if (isFirstSpanInChunk && this._nativeExport) {
formattedSpan.meta[SDK_OTLP_EXPORT_KEY] = 'false'
}
isFirstSpanInChunk = false
// Span stats read Datadog HTTP tag names from the formatted span, so
// record them before the OTel rename — an export-only transform.
Expand Down
44 changes: 44 additions & 0 deletions packages/dd-trace/test/opentelemetry/traces.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,50 @@ describe('OpenTelemetry Traces', () => {

exporter.export([createMockSpan()])
})

describe('SDK adoption markers', () => {
/**
* @param {object} extraEnv
* @returns {{ resource: Record<string, string> }}
*/
function exportAndCapture (extraEnv) {
let captured
const verify = mockOtlpExport((decoded) => {
const { resource } = decoded.resourceSpans[0]
captured = {
resource: Object.fromEntries(resource.attributes.map(attr => [attr.key, attr.value.stringValue])),
}
})

buildExporter({ OTEL_TRACES_EXPORTER: 'otlp', ...extraEnv }).export([createMockSpan()])
verify()

return captured
}

it('declares OTLP export and Datadog semantics on the resource by default', () => {
const { resource } = exportAndCapture({})

assert.strictEqual(resource['_dd.sdk.otlp_export'], 'true')
assert.strictEqual(resource['datadog.sdk.semantics'], 'datadog')
})

it('declares OTel semantics on the resource when DD_TRACE_OTEL_SEMANTICS_ENABLED is set', () => {
const { resource } = exportAndCapture({ DD_TRACE_OTEL_SEMANTICS_ENABLED: 'true' })

assert.strictEqual(resource['_dd.sdk.otlp_export'], 'true')
assert.strictEqual(resource['datadog.sdk.semantics'], 'otel')
})

it('does not let global tags with the same keys override the resource', () => {
const { resource } = exportAndCapture({
DD_TAGS: '_dd.sdk.otlp_export:false,datadog.sdk.semantics:otel',
})
Comment thread
mhlidd marked this conversation as resolved.

assert.strictEqual(resource['_dd.sdk.otlp_export'], 'true')
assert.strictEqual(resource['datadog.sdk.semantics'], 'datadog')
})
})
})

describe('Telemetry Metrics', () => {
Expand Down
64 changes: 53 additions & 11 deletions packages/dd-trace/test/span_processor.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const proxyquire = require('proxyquire')

require('./setup/core')

const { APM_TRACING_ENABLED_KEY } = require('../src/constants')
const { APM_TRACING_ENABLED_KEY, SDK_OTLP_EXPORT_KEY } = require('../src/constants')
const { AUTO_REJECT, USER_KEEP } = require('../../../ext/priority')

describe('SpanProcessor', () => {
Expand Down Expand Up @@ -68,7 +68,7 @@ describe('SpanProcessor', () => {
},
appsec: {},
}
spanFormat = sinon.stub().returns({ formatted: true })
spanFormat = sinon.stub().callsFake(() => ({ formatted: true, meta: {} }))

sample = sinon.stub()
SpanSampler = sinon.stub().returns({
Expand Down Expand Up @@ -189,9 +189,9 @@ describe('SpanProcessor', () => {
processor.process(finishedSpan)

sinon.assert.calledWith(exporter.export, [
{ formatted: true },
{ formatted: true },
{ formatted: true },
{ formatted: true, meta: { [SDK_OTLP_EXPORT_KEY]: 'false' } },
{ formatted: true, meta: {} },
{ formatted: true, meta: {} },
])

assert.ok('started' in trace)
Expand Down Expand Up @@ -247,7 +247,7 @@ describe('SpanProcessor', () => {

processor.process(finishedSpan)

sinon.assert.calledWith(exporter.export, [{ formatted: true }])
sinon.assert.calledWith(exporter.export, [{ formatted: true, meta: { [SDK_OTLP_EXPORT_KEY]: 'false' } }])
})

it('should configure span sampler correctly', () => {
Expand Down Expand Up @@ -342,12 +342,54 @@ describe('SpanProcessor', () => {
sinon.assert.calledWith(spanFormat.getCall(3), finishedSpan, false, processor._processTags)
})

it('should add the native export marker to the first span of each chunk', () => {
config.flushMinSpans = 2
const processor = new SpanProcessor(exporter, prioritySampler, config)
trace.started = [activeSpan, finishedSpan, finishedSpan]
trace.finished = [finishedSpan, finishedSpan]
processor.process(finishedSpan)

trace.started = [finishedSpan]
trace.finished = [finishedSpan]
processor.process(finishedSpan)

const [firstChunk] = exporter.export.firstCall.args
const [secondChunk] = exporter.export.secondCall.args
assert.strictEqual(firstChunk[0].meta[SDK_OTLP_EXPORT_KEY], 'false')
assert.ok(!Object.hasOwn(firstChunk[1].meta, SDK_OTLP_EXPORT_KEY))
assert.strictEqual(secondChunk[0].meta[SDK_OTLP_EXPORT_KEY], 'false')
})

it('should not let a span tag override the native export marker', () => {
config.flushMinSpans = 1
const processor = new SpanProcessor(exporter, prioritySampler, config)
const formattedSpan = { meta: { [SDK_OTLP_EXPORT_KEY]: 'true' } }
spanFormat.returns(formattedSpan)
trace.started = [finishedSpan]
trace.finished = [finishedSpan]
processor.process(finishedSpan)

assert.strictEqual(formattedSpan.meta[SDK_OTLP_EXPORT_KEY], 'false')
})

it('should not add the native export marker when traces are exported over OTLP', () => {
config.flushMinSpans = 1
config.OTEL_TRACES_EXPORTER = 'otlp'
const processor = new SpanProcessor(exporter, prioritySampler, config)
trace.started = [finishedSpan]
trace.finished = [finishedSpan]
processor.process(finishedSpan)

const [chunk] = exporter.export.firstCall.args
assert.ok(!Object.hasOwn(chunk[0].meta, SDK_OTLP_EXPORT_KEY))
})

it('should add APM disabled marker to every span in a chunk when APM tracing is disabled', () => {
config.apmTracingEnabled = false
config.flushMinSpans = 2
const processor = new SpanProcessor(exporter, prioritySampler, config)
const firstFormatted = { metrics: {} }
const secondFormatted = { metrics: {} }
const firstFormatted = { meta: {}, metrics: {} }
const secondFormatted = { meta: {}, metrics: {} }
spanFormat.onFirstCall().returns(firstFormatted)
spanFormat.onSecondCall().returns(secondFormatted)
trace.started = [activeSpan, finishedSpan, finishedSpan]
Expand All @@ -366,8 +408,8 @@ describe('SpanProcessor', () => {
// later in its own chunk. Both chunks must carry _dd.apm.enabled:0.
config.apmTracingEnabled = false
const processor = new SpanProcessor(exporter, prioritySampler, config)
const parentFormatted = { metrics: {} }
const childFormatted = { metrics: {} }
const parentFormatted = { meta: {}, metrics: {} }
const childFormatted = { meta: {}, metrics: {} }
spanFormat.onFirstCall().returns(parentFormatted)
spanFormat.onSecondCall().returns(childFormatted)

Expand All @@ -393,7 +435,7 @@ describe('SpanProcessor', () => {
it('should not add APM disabled marker when APM tracing is enabled', () => {
config.apmTracingEnabled = true
const processor = new SpanProcessor(exporter, prioritySampler, config)
const formattedSpan = { metrics: {} }
const formattedSpan = { meta: {}, metrics: {} }
spanFormat.returns(formattedSpan)
trace.started = [finishedSpan]
trace.finished = [finishedSpan]
Expand Down
Loading