Skip to content

Commit 78201a7

Browse files
committed
fix(native-spans): normalize core span fields before native export
The v0.4 encoder runs `normalizeSpan` on every span as it encodes (`encode/0.4.js` selects it as the per-span formatter), so the JS pipeline never ships a span missing the intake defaults or exceeding the 100-character caps on service, name and type. The native path wrote `formatted.name` / `.service` / `.type` straight into WASM, making it the only pipeline that could emit un-normalized core fields — so a high-cardinality route name went out at full length. Apply the same pass at the native write, after the stats snapshot, which matches the legacy ordering where normalization happens at encode time rather than at finish. Reported by Codex review as P2. Note the report also mentions the 5,000 character resource cap; that is `truncateSpan`, which the v0.4 agent path does not apply either (only the electron and agentless encoders do), so it is deliberately left alone.
1 parent e6dfb9f commit 78201a7

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

packages/dd-trace/src/span_processor.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const SpanSampler = require('./span_sampler')
77
const GitMetadataTagger = require('./git_metadata_tagger')
88
const native = require('./native')
99
const processTags = require('./process-tags')
10-
const { MAX_META_VALUE_LENGTH } = require('./encode/tags-processors')
10+
const { MAX_META_VALUE_LENGTH, normalizeSpan } = require('./encode/tags-processors')
1111
const {
1212
APM_TRACING_ENABLED_KEY,
1313
SAMPLING_MECHANISM_MANUAL,
@@ -313,7 +313,15 @@ class SpanProcessor {
313313

314314
if (typeof context.syncFinalTagsToNative === 'function') {
315315
formattedSpan ??= spanFormat(span, isFirstSpanInChunk, this._processTags)
316-
context.syncFinalTagsToNative(formattedSpan)
316+
// The v0.4 encoder runs `normalizeSpan` on every span as it encodes
317+
// (encode/0.4.js picks it as the per-span formatter), so the JS
318+
// pipeline never ships a span without the intake defaults and the
319+
// 100-char caps on service/name/type. The native path writes these
320+
// fields straight into WASM, so apply the same pass here or it
321+
// becomes the only pipeline sending un-normalized core fields.
322+
// Applied after the stats snapshot, matching the legacy ordering
323+
// where normalization happens at encode time rather than at finish.
324+
context.syncFinalTagsToNative(normalizeSpan(formattedSpan))
317325
}
318326

319327
// Remap Datadog HTTP tags to OpenTelemetry names on the native span

packages/dd-trace/test/span_processor.spec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,31 @@ describe('SpanProcessor', () => {
136136
assert.deepStrictEqual(syncOrder, ['sync', 'export'])
137137
})
138138

139+
it('normalizes core fields before syncing them to native storage', () => {
140+
// The v0.4 encoder runs `normalizeSpan` per span as it encodes, so the JS
141+
// pipeline never ships an over-long service/name or a missing resource. The
142+
// native path writes these straight into WASM, so without the same pass it
143+
// would be the only pipeline sending un-normalized core fields.
144+
spanFormat.returns({
145+
name: 'n'.repeat(150),
146+
service: 's'.repeat(150),
147+
type: 't'.repeat(150),
148+
metrics: {},
149+
meta: {},
150+
})
151+
trace.started = [finishedSpan]
152+
trace.finished = [finishedSpan]
153+
154+
processor.process(finishedSpan)
155+
156+
const synced = finishedSpan.context().syncFinalTagsToNative.getCall(0).args[0]
157+
assert.strictEqual(synced.name.length, 100)
158+
assert.strictEqual(synced.service.length, 100)
159+
assert.strictEqual(synced.type.length, 100)
160+
// A missing resource falls back to the (already truncated) name.
161+
assert.strictEqual(synced.resource, synced.name)
162+
})
163+
139164
it('should generate sampling priority when sampling manually', () => {
140165
trace.started = [finishedSpan]
141166
processor.sample(finishedSpan)

0 commit comments

Comments
 (0)