-
Notifications
You must be signed in to change notification settings - Fork 536
feat(ddtrace/tracer): promote span fields out of meta map into a typed SpanAttributes struct #4538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gh-worker-dd-mergequeue-cf854d
merged 73 commits into
main
from
dario.castane/apmlp-856/promote-redundant-span-fields
Apr 17, 2026
Merged
Changes from all commits
Commits
Show all changes
73 commits
Select commit
Hold shift + click to select a range
220ad1c
feat(ddtrace/tracer): promote span fields from meta
darccio 4d323b6
feat(ddtrace/tracer): use span.env
darccio f0107ae
feat(ddtrace/tracer): use span.spanKind
darccio a59baa9
feat(ddtrace/tracer): evolve promoted fields to state-aware values wi…
darccio 5b2d9e0
feat(ddtrace/tracer): encode promoted fields instead of looking up th…
darccio 2ab1efd
feat(ddtrace/tracer): introduce API to tagValue to avoid leaking impl…
darccio ef61c9d
chore(ddtrace/tracer): preparing refactor to internalize
darccio 49cf5f3
chore(ddtrace/tracer): internalize SpanAttributes
darccio b021620
feat(ddtrace/tracer): enhancing API by using presence bitmap and acce…
darccio 9bc84d9
chore: make lint/go/fix
darccio 5f64898
chore(ddtrace/tracer): remove unexported alias
darccio 21dfaa2
feat(ddtrace/tracer): add language to SpanAttributes
darccio d1664fc
test(ddtrace/tracer): add tests and benchmarks for SpanAttributes
darccio ab6f412
feat(ddtrace/tracer): avoid double-encoding for promoted fields
darccio 55c7693
feat(ddtrace/tracer): COW-aware SpanAttributes; revert ext.Language
darccio 2cbde38
chore(ddtrace/ext): drop Language
darccio 9db4e60
feat(ddtrace/tracer): introduce spanMeta to group map and SpanAttribu…
darccio 4179372
fix(ddtrace/tracer): regenerate msgp code
darccio 6eb24be
fix(ddtrace/mocktracer): update spanStart linkname signature with uns…
darccio 823d13b
fix(ddtrace/tracer): reduce introduced allocations
darccio 63a0bdf
fix(ddtrace/tracer): avoid nil dereference on tests
darccio aff9637
fix(ddtrace/tracer): optimize code layout for favour improved inlining
darccio f5e90d3
fix(ddtrace/tracer): ensure checklocks is ok
darccio 581e57f
fix(ddtrace/tracer): revert DecodeMsg to a simpler state because fide…
darccio 2973d1c
feat(ddtrace/tracer): hide spanMeta's implementation details behind a…
darccio 2106eee
refactor(ddtrace/tracer): move spanMeta to ddtrace/tracer/internal
darccio b6fcf9e
fix(ddtrace/tracer): detected mishaps
darccio a720748
chore(ddtrace/tracer): make lint/go/fix
darccio 8246258
feat(ddtrace/tracer): reduce allocations on hot paths; reduce CPU cyc…
darccio 40dbd10
fix(ddtrace/tracer): modernize using maps.Copy on spanMeta.Merge
darccio d31f246
fix(ddtrace/tracer): clean up; ensure new API is inlinable
darccio e809732
fix(ddtrace/tracer): try to reduce allocations, again (meta init hint…
darccio aad2032
fix(ddtrace/tracer): fixing SpanMeta.Merge to avoid bad allocations
darccio 851fe39
fix(ddtrace/tracer): pool StatSpanConfig; ensure SpanMeta.Merge doesn…
darccio a373301
chore(ddtrace/tracer): encapsulate pooling logic around the statSpanC…
darccio d4c7ec4
fix(ddtrace/tracer): avoid v1 payload corruption by exposing attrs pr…
darccio 9fd77aa
chore(ddtrace/tracer): revert to simpler design
darccio b15fe08
fix(ddtrace/tracer): inline promoted attributes into meta map to redu…
darccio 3c9e3fa
chore(ddtrace/tracer): break down big conditional into individual guards
darccio 80a9c92
chore(ddtrace/tracer): happy path must be left aligned
darccio 35dd1f0
chore(ddtrace/tracer): final touches
darccio d4ad3e8
fix(ddtrace/tracer): run SpanMeta.Inline before SpanContext.finish to…
darccio 1bd2a4a
fix(ddtrace/tracer): hold span lock on CIVis when reading inner span
darccio 091e0f4
fix(ddtrace/tracer): adapt TestStatsIncludeServiceSource to new SpanMeta
darccio e6b737f
fix(ddtrace/tracer): remove redundant assert.RWMutextLocked
darccio 5b45c5c
fix(ddtrace/tracer): add inlined bool, RangeInlined, and Attr to opti…
darccio ef0e563
refactor(ddtrace/tracer): use `tracerinternal` instead of `tinternal`
darccio 5fe4407
refactor(ddtrace/tracer): remove `SpanMeta.Inline()`
darccio b3d0f7f
feat(ddtrace/tracer): simplify API and don't expose internals
darccio 3f89622
refactor(ddtrace/tracer): drop `*Map` methods on SpanMeta
darccio 1b510d8
fix(ddtrace/tracer): adapt merged code
darccio aa55e43
perf(ddtrace/tracer): reduce SpanMeta overhead on hot paths
darccio 35ddce1
fix(ddtrace/tracer): fix leftovers for supportsLinks
darccio 4bab6e1
feat(ddtrace/tracer): another attempt
darccio 7921b27
chore(ddtrace/tracer): move dangling comment to its right place
darccio b52bcf0
feat(ddtrace/tracer): ensure that Delete is inlineable; simplify API
darccio d50dd48
chore(ddtrace/tracer): adapt TestServiceSource
darccio c70a7bb
chore(ddtrace/tracer): improve samplingPriorityPtr docs
darccio aeead6f
feat(ddtrace/tracer): promote language to attribute
darccio d80aad0
feat(ddtrace/tracer): implement locking on SpanMeta
darccio c1a3543
fix(ddtrace/tracer): reduce mutex contantion leveraging inline as ato…
darccio eece436
revert: go back to lock-free implementation; add SpanMeta.Finish to a…
darccio 78586fe
fix(ddtrace/tracer): demote component and span.kind; adopt read-only …
darccio 9020723
fix(ddtrace/tracer): use atomic inline; remove misleading comment
darccio 423355d
fix(ddtrace/tracer): avoid race condition moving meta.Finish before c…
darccio 525b29a
chore(ddtrace/tracer): structural and safety improvements
darccio 8fcdf46
fix(ddtrace/tracer): remove map merging code based on wrong assumptio…
darccio 50afbf1
Merge remote-tracking branch 'origin' into dario.castane/apmlp-856/pr…
darccio 57a2f21
feat(ddtrace/tracer): simplify after merging main
darccio 7b626d1
chore(ddtrace/tracer): go fmt
darccio 4ae9555
fix(ddtrace/tracer): allow to adjust which `SpanMeta.Map` a consumer …
darccio ef024b0
Merge remote-tracking branch 'origin' into dario.castane/apmlp-856/pr…
darccio 21cbe1d
fix(ddtrace/tracer): update OTLP exporter code to new promoted fields…
darccio File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| // Unless explicitly stated otherwise all files in this repository are licensed | ||
| // under the Apache License Version 2.0. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| // Copyright 2016 Datadog, Inc. | ||
|
|
||
| package internal | ||
|
|
||
| import ( | ||
| "iter" | ||
| "math/bits" | ||
| "unsafe" | ||
| ) | ||
|
|
||
| // AttrKey is an integer index into a SpanAttributes value array. | ||
| // Use the pre-declared constants; do not construct AttrKey from arbitrary integers. | ||
| type AttrKey uint8 | ||
|
|
||
| const ( | ||
| AttrEnv AttrKey = 0 | ||
| AttrVersion AttrKey = 1 | ||
| AttrLanguage AttrKey = 2 | ||
| numAttrs AttrKey = 3 | ||
|
|
||
| // AttrUnknown is returned by AttrKeyForTag when no promoted tag matches. | ||
| // Its value is intentionally out of range for vals[] so misuse panics immediately. | ||
| AttrUnknown AttrKey = 0xFF | ||
| ) | ||
|
|
||
| // Compile-time guard: the numeric values of AttrKey constants are load-bearing — | ||
| // they index both vals[] and setMask bit positions. If any value drifts (e.g. via | ||
| // iota + reorder), the expression below produces a compile error. | ||
| var ( | ||
| _ = [1]byte{}[AttrEnv] // AttrEnv must be 0 | ||
| _ = [1]byte{}[AttrVersion-1] // AttrVersion must be 1 | ||
| _ = [1]byte{}[AttrLanguage-2] // AttrLanguage must be 2 | ||
| _ = [1]byte{}[numAttrs-3] // numAttrs must be 3 (update when adding attributes) | ||
| ) | ||
|
|
||
| // SpanAttributes holds the V1-protocol promoted span fields. | ||
| // Zero value = all fields absent. | ||
| // Set(key, "") is distinct from never-Set: the bit is set, the string is "". | ||
| // | ||
| // Layout: 1-byte setMask + 1-byte readOnly + 6B padding + [3]string (48B) = 56 bytes. | ||
| // | ||
| // When readOnly is true, the instance is owned by the tracer and must not be | ||
| // mutated. Callers must Clone before writing (copy-on-write). | ||
| type SpanAttributes struct { | ||
| setMask uint8 | ||
| readOnly bool | ||
| vals [numAttrs]string | ||
| } | ||
|
|
||
| // Compile-time layout check: SpanAttributes must be exactly 56 bytes. | ||
| // 1B setMask + 1B readOnly + 6B padding + [3]string (48B) = 56B. | ||
| var _ = [1]byte{}[56-unsafe.Sizeof(SpanAttributes{})] | ||
| var _ = [1]byte{}[unsafe.Sizeof(SpanAttributes{})-56] | ||
|
|
||
| // All read methods are nil-safe so callers holding a *SpanAttributes don't | ||
| // need nil guards. | ||
|
|
||
| func (a *SpanAttributes) Set(key AttrKey, v string) { | ||
| if a == nil { | ||
| return | ||
| } | ||
| a.vals[key] = v | ||
| a.setMask |= 1 << key | ||
| } | ||
|
|
||
| // Unset clears the attribute for key, making it absent (as if never set). nil-safe. | ||
| func (a *SpanAttributes) Unset(key AttrKey) { | ||
| if a == nil { | ||
| return | ||
| } | ||
| a.vals[key] = "" | ||
| a.setMask &^= 1 << key | ||
| } | ||
|
|
||
| func (a *SpanAttributes) Val(key AttrKey) string { | ||
| if a == nil { | ||
| return "" | ||
| } | ||
| return a.vals[key] | ||
| } | ||
|
|
||
| func (a *SpanAttributes) Has(key AttrKey) bool { | ||
| return a != nil && a.setMask>>key&1 != 0 | ||
| } | ||
|
|
||
| func (a *SpanAttributes) Get(key AttrKey) (string, bool) { | ||
| return a.Val(key), a.Has(key) | ||
| } | ||
|
|
||
| // Count returns the number of promoted fields that have been set. | ||
| func (a *SpanAttributes) Count() int { | ||
| if a == nil { | ||
| return 0 | ||
| } | ||
| return bits.OnesCount8(a.setMask) | ||
| } | ||
|
|
||
| // MarkReadOnly marks this instance as readOnly (read-only). Clone before mutating. | ||
| // The receiver must be non-nil; marking a nil SpanAttributes read-only is a | ||
| // programming error and panics. | ||
| func (a *SpanAttributes) MarkReadOnly() { a.readOnly = true } | ||
|
|
||
| // IsReadOnly reports whether this is a readOnly instance requiring COW. | ||
| func (a *SpanAttributes) IsReadOnly() bool { return a != nil && a.readOnly } | ||
|
|
||
| // Reset clears all set attributes, returning the instance to its zero state. | ||
| // It is nil-safe and does not free the underlying memory, making it suitable | ||
| // for reuse (e.g. in a decode loop that reuses Span objects). | ||
| func (a *SpanAttributes) Reset() { | ||
| if a == nil { | ||
| return | ||
| } | ||
| *a = SpanAttributes{} | ||
| } | ||
|
|
||
| // Clone returns a mutable (non-readOnly) shallow copy. | ||
| func (a *SpanAttributes) Clone() *SpanAttributes { | ||
| if a == nil { | ||
| return &SpanAttributes{} | ||
| } | ||
| cp := *a | ||
| cp.readOnly = false | ||
| return &cp | ||
| } | ||
|
|
||
| // AttrDef maps an AttrKey to its canonical tag name. | ||
| type AttrDef struct { | ||
| Key AttrKey | ||
| Name string | ||
| } | ||
|
|
||
| // Defs enumerates all promoted attribute definitions. | ||
| var Defs = [numAttrs]AttrDef{ | ||
| {AttrEnv, "env"}, | ||
| {AttrVersion, "version"}, | ||
| {AttrLanguage, "language"}, | ||
| } | ||
|
|
||
| // IsPromotedKeyLen reports whether n matches the length of any promoted attribute name. | ||
| // Promoted keys: "env"(3), "version"(7), "language"(8). | ||
| // This must stay in sync with the Defs table; the init check below enforces | ||
| // this at program start. | ||
| func IsPromotedKeyLen(n int) bool { | ||
| switch n { | ||
| case 3, 7, 8: | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| func init() { | ||
| for _, d := range Defs { | ||
| if !IsPromotedKeyLen(len(d.Name)) { | ||
| panic("IsPromotedKeyLen out of sync with Defs: missing length " + d.Name) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // all returns an iterator over the set attributes (name, value) pairs. | ||
| func (a *SpanAttributes) all() iter.Seq2[string, string] { | ||
| return func(yield func(string, string) bool) { | ||
| if a == nil { | ||
| return | ||
| } | ||
| for _, d := range Defs { | ||
| if a.Has(d.Key) { | ||
| if !yield(d.Name, a.vals[d.Key]) { | ||
| return | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // AttrKeyForTag returns the AttrKey for a promoted tag name, if any. | ||
| // Returns (AttrUnknown, false) when the tag is not a promoted attribute. | ||
| func AttrKeyForTag(tag string) (AttrKey, bool) { | ||
| if !IsPromotedKeyLen(len(tag)) { | ||
| return AttrUnknown, false | ||
| } | ||
| switch tag { | ||
| case "env": | ||
| return AttrEnv, true | ||
| case "version": | ||
| return AttrVersion, true | ||
| case "language": | ||
| return AttrLanguage, true | ||
| } | ||
| return AttrUnknown, false | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should go level higher here if it is possible.
sharedAttrsshould be an implementation detail.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kakkoyun
mocktracerdoesn't have visibility of the unexportedspanMetatype. How should this be done?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😭
mocktracerstrikes again. We should find a better solution for it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kakkoyun It's called inspectable tracer 😁 #4512
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Kemal; would #4512 unblock this for us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since #4512 is very close. Maybe we should merge it first and then this one.