Repository navigation
Expand file tree
/
Copy pathtag-conventions.yaml
More file actions
213 lines (199 loc) · 14.1 KB
/
Copy pathtag-conventions.yaml
File metadata and controls
213 lines (199 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# Tag conventions — LANGUAGE-AGNOSTIC domain spec (structure + semantics only)
# ---------------------------------------------------------------------------
# The code generator consumes THIS file to emit each language's tag-id constants and its
# id<->name resolver. A tag id is IDENTITY (a globally unique serial + the trace-level bit);
# storage layout (the dense store's slot assignment) and set-path routing (which keys the tracer
# intercepts into span fields or sampling directives) are per-language concerns that arrive with
# the code that consumes them, via a per-language overlay alongside this file.
#
# TRACE-LEVEL is its own thing (its own TagMap "type" on the TraceSegment) — the process/trace
# constants + product flags that are set once per trace, NOT per span. Declared explicitly in the
# `trace_level` section below (a distinct tier), never inferred from `source`.
#
# SPAN TYPES compose three ways:
# extends — structural is-a inheritance (http.server is-a http is-a base). `base` is implicitly
# in every span; abstract layers exist only to be extended.
# include — a span type PULLS in a mixin it intrinsically has (has-a; core-owned).
# applies — a mixin PUSHES itself onto span types, gated by `enabled_by`.
# resolved_tags(type) = own + extends-chain (incl base) + included mixins + applied mixins (de-duped).
# A span type may declare `span-kind` (server|client|producer|consumer|internal), inherited through
# `extends`. It sets the type's DIRECTION: server/consumer spans are inbound, client/producer spans are
# outbound, internal spans have none. A mixin may declare one too: a directional mixin may only reach
# span types of its direction.
#
# tag fields (DOMAIN only): dd-name | type (string|int|long|boolean|double)
# | required (required|conditional|recommended|optional|opt_in) | otel-name.
# dd-name is the canonical Datadog-namespace name AND the tag's identity. otel-name is OPTIONAL and
# tri-state:
# - absent => the OpenTelemetry name is IMPLICITLY the dd-name (the tag passes through under
# its Datadog name; this is the RFC "retain" default for tags with no rename).
# - a name => rename: the tag is emitted under that OpenTelemetry-namespace name instead.
# - the literal none => Datadog-only: the tag has NO OpenTelemetry name (suppressed from OTel). This
# value is reserved — no tag uses it today (the RFC renames or retains, never
# suppresses), and real suppression is a follow-on; it currently behaves as
# pass-through.
# A tag is one identity across span types/mixins, so it is DECLARED exactly once — a second declaration
# fails the build. Put a tag shared by several span types on their common parent or a mixin; any other
# span type that carries it uses `{ ref: <dd-name>, required: <level> }`, which may override only
# `required` (type and otel-name come from the one declaration).
# OpenTelemetry names can depend on direction: `server.address` is the local host on an inbound span
# but the remote one on an outbound span. So an OpenTelemetry name is unique PER DIRECTION, not
# globally, and where a rename is declared scopes it: in a scope without a span-kind (trace_level, an
# abstract type, a plain mixin) it applies in every direction; in a directional scope, only in that
# direction.
# A tag whose MEANING flips with direction, like peer.port (the server's port on an outbound span, the
# client's on an inbound one), is declared once per direction, each in a directional mixin. Each
# declaration becomes its own tag, with fixed names in both namespaces; the Datadog name is shared.
# (The generator labels these tags `<dd-name>@<direction>` in its reports; that label is not YAML
# syntax.) Any other repeated declaration is still an error.
# Until name resolution knows a span's direction, only renames that apply in every direction are used.
# A directional rename, including one under `span-kind: internal` (spans with no direction), is
# listed in tag-assignment.txt but not yet applied.
# `span-kind-neutral: true` widens a directional rename to every direction -- the author's assertion
# that OTel only uses that name for this tag (db.system, say, only ever names a database). It is
# interim, and goes away once resolution is direction-aware. A rename on a concrete type with no
# span-kind still requires it.
# The id coordinate (group-decl / field-decl) is NOT authored here — the generator assigns it: each
# declaration source (the trace-level tier, each span type, each mixin) is a group, and within a
# group `field-decl` numbers the dense (required/conditional/recommended) tags; the rest are
# bucketed. See the design doc.
# ---------------------------------------------------------------------------
# Trace-level tier: its own TagMap on the TraceSegment. Set once per trace, not per span.
# (Their OTel mapping is a resource-attribute follow-on; they pass through under dd-name for now.)
trace_level:
tags:
- { dd-name: _dd.base_service, type: string, required: required }
- { dd-name: version, type: string, required: recommended }
- { dd-name: env, type: string, required: recommended }
- { dd-name: language, type: string, required: required }
- { dd-name: runtime-id, type: string, required: required }
- { dd-name: _dd.tracer_host, type: string, required: recommended }
- { dd-name: _dd.git.commit.sha, type: string, required: recommended }
- { dd-name: _dd.git.repository_url, type: string, required: recommended }
# product .enabled flags — process-constant; present on the trace segment regardless of whether
# the product is enabled (the flag carries the state), so always-present => recommended.
- { dd-name: _dd.profiling.enabled, type: boolean, required: recommended }
- { dd-name: _dd.dsm.enabled, type: boolean, required: recommended }
- { dd-name: _dd.appsec.enabled, type: boolean, required: recommended }
- { dd-name: _dd.djm.enabled, type: boolean, required: recommended }
- { dd-name: _dd.civisibility.enabled, type: boolean, required: recommended }
span_types:
# root: per-span tags every span has (incl. the per-span core tags parent_id / integration / svc_src
# — core-set but per-span, so NOT trace-level).
base:
abstract: true
tags:
- { dd-name: _dd.parent_id, type: string, required: required }
- { dd-name: service, type: string, required: required, otel-name: service.name }
- { dd-name: component, type: string, required: required }
- { dd-name: span.kind, type: string, required: required } # OTel span kind is a first-class field, not an attribute
- { dd-name: _dd.integration, type: string, required: recommended }
- { dd-name: _dd.svc_src, type: string, required: optional }
- { dd-name: error.type, type: string, required: recommended } # TODO(otel): map error.* to exception.* semconv
- { dd-name: error.message, type: string, required: recommended }
- { dd-name: error.stack, type: string, required: recommended }
http:
abstract: true
extends: base
tags:
- { dd-name: http.method, type: string, required: required, otel-name: http.request.method }
- { dd-name: http.status_code, type: int, required: conditional, otel-name: http.response.status_code }
- { dd-name: network.protocol.version, type: string, required: recommended } # passes through: dd-name already is the OTel name
- { dd-name: http.url, type: string, required: required, otel-name: url.full } # shared by http.server and http.client => one otel-name. url.full is the client-correct rename; server's spec mapping (url.path + url.scheme + url.query) is a one-to-many split reserved for the derivation layer (needs span.kind). TODO(otel): server split.
http.server:
extends: http
span-kind: server
include: [ peer_address, inbound_peer ]
tags:
- { dd-name: http.route, type: string, required: conditional } # passes through: dd-name already is the OTel name
- { dd-name: http.hostname, type: string, required: required, otel-name: server.address } # inbound only; on outbound spans server.address is peer.hostname
- { dd-name: http.useragent, type: string, required: recommended, otel-name: user_agent.original, span-kind-neutral: true }
- { dd-name: http.query.string, type: string, required: recommended } # not a rename: url.query is also carried inside url.full, while http.url excludes the query (QueryObfuscator re-appends it)
- { dd-name: servlet.path, type: string, required: optional }
- { dd-name: servlet.context, type: string, required: optional }
- { dd-name: http.client_ip, type: string, required: recommended, otel-name: client.address, span-kind-neutral: true }
# Inbound only: the client's socket address. Server spans also carry that address in peer.ipv4 /
# peer.ipv6, so do not map those inbound too, or the attribute is exported twice.
- { dd-name: network.client.ip, type: string, required: recommended, otel-name: network.peer.address }
http.client:
extends: http
span-kind: client
include: [ peer, peer_address, outbound_peer ]
tags:
- { dd-name: http.resend_count, type: int, required: recommended }
db.client:
extends: base
span-kind: client
include: [ peer, peer_address, outbound_peer ]
tags:
- { dd-name: db.type, type: string, required: required, otel-name: db.system, span-kind-neutral: true }
- { dd-name: db.instance, type: string, required: recommended } # TODO(otel): db.namespace
- { dd-name: db.operation, type: string, required: recommended, otel-name: db.operation.name, span-kind-neutral: true }
- { dd-name: db.user, type: string, required: recommended }
- { dd-name: db.pool.name, type: string, required: optional }
- { dd-name: db.statement, type: string, required: recommended, otel-name: db.query.text, span-kind-neutral: true }
view.render:
extends: base
span-kind: internal
tags:
- { dd-name: view.name, type: string, required: recommended }
mixins:
# peer — the downstream service a client span calls, PULLED via `include` by client span types.
peer:
tags:
- { dd-name: peer.service, type: string, required: recommended }
- { dd-name: _dd.peer.service.source, type: string, required: recommended }
- { dd-name: _dd.peer.service.remapped_from, type: string, required: recommended }
# peer_address — the other end's IP address, on both client and server spans.
peer_address:
tags:
# TODO(otel): both are network.peer.address, split by address family -- input needs the value to
# pick one, which the registry cannot model. Planned: a `resolved-by: <layer>` marker that names
# the layer responsible (e.g. otel-api) and allows the mutually exclusive shared output name.
- { dd-name: peer.ipv4, type: string }
- { dd-name: peer.ipv6, type: string }
# outbound_peer / inbound_peer declare peer.port once per direction. `peer.port` is then a SHARED
# Datadog name for two tags (reported as peer.port@outbound and peer.port@inbound): emitting it needs no context,
# but resolving the bare name needs the span's direction.
# outbound_peer — the other end of an outbound connection: the server.
outbound_peer:
span-kind: client
tags:
- { dd-name: peer.hostname, type: string, required: recommended, otel-name: server.address }
- { dd-name: peer.port, type: int, otel-name: server.port }
# inbound_peer — the other end of an inbound connection: the client.
inbound_peer:
span-kind: server
tags:
- { dd-name: peer.port, type: int, otel-name: client.port }
# ci_visibility — per-span test tags. Its capability flag (_dd.civisibility.enabled) lives in
# trace_level, outside this mixin (general rule: capability flags are trace-level, mixins hold the
# per-span tags). Applies to the `test` span type, which is not modeled here yet — so these tags
# get ids (identity does not depend on layout) but contribute to no type's resolved set until it
# is. The generator reports that gap in resolved-tags.txt.
ci_visibility:
enabled_by: dd.civisibility.enabled
applies: [ test ]
tags:
- { dd-name: test.name, type: string, required: recommended }
- { dd-name: test.suite, type: string, required: recommended }
- { dd-name: test.status, type: string, required: recommended }
- { dd-name: test.framework, type: string, required: recommended }
# ---------------------------------------------------------------------------
# Notes
# - Product .enabled flags moved to `trace_level` (process-constant) — the old product mixins held
# only those flags, so they dissolved. `enabled_by`/attachment gating is a runtime concern.
# - span.kind enumerates: server | client | producer | consumer | internal | broker.
# - Some keys (resource.name, error, sampling.priority, ...) are accepted by setTag but routed to a
# span field or a trace directive instead of tag storage. That routing is a per-language tracer
# concern, so it is NOT modelled here; such a key appears above only when it also needs an id and
# a name (service does, for OpenTelemetry's service.name).
# - Tags with no otel-name pass through under their Datadog name (RFC "retain"). A `# TODO(otel)` note
# marks a pending OpenTelemetry-team review of a mapping that is not yet a settled rename.
# - db.statement/db.query.text is deliberately never re-exposed as a stored tag once consumed into
# resource.name, regardless of which alias set it -- the two names are equivalent for identity/id
# purposes, but the raw value (often unobfuscated SQL) is intentionally not retained. This is an
# open question (not yet resolved one way or the other): revisit only as a language-agnostic policy
# change (i.e. decide to start storing/exporting it in every tracer), not as a per-language or
# per-spelling special case.
# ---------------------------------------------------------------------------