Skip to content

Commit 6d5eb31

Browse files
dougqhclaude
andcommitted
Cover the tag registry: name resolution, namespaces, and id partitioning
Parity test for the generated KnownTags registry and the resolver it installs: keyOf/nameOf round-tripping across the tag groups, the many->one inbound mapping (a Datadog name and an OpenTelemetry name both resolve to the one id) with the per-namespace accessors taking each back out, and the intercepted / reserved / stored / trace-level partitioning of the id. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8dc8970 commit 6d5eb31

1 file changed

Lines changed: 274 additions & 0 deletions

File tree

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
package datadog.trace.api;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNull;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import datadog.trace.bootstrap.instrumentation.api.Tags;
9+
import java.util.ArrayList;
10+
import java.util.HashSet;
11+
import java.util.List;
12+
import java.util.Set;
13+
import java.util.stream.Stream;
14+
import org.junit.jupiter.api.BeforeAll;
15+
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.params.ParameterizedTest;
17+
import org.junit.jupiter.params.provider.Arguments;
18+
import org.junit.jupiter.params.provider.MethodSource;
19+
20+
/**
21+
* Parity test for the keyOf substrate: the generated {@link KnownTags} registry + the {@link
22+
* KnownTagCodec.Resolver} it registers. Verifies name &harr; id resolution and the intercepted /
23+
* reserved / stored partitioning. {@code keyOf} is many&rarr;one (a Datadog or an OpenTelemetry
24+
* name both land on the one id) and the per-namespace accessors take it back out. A tag id is
25+
* identity only, so nothing here depends on how a tag is stored.
26+
*/
27+
class KnownTagsTest {
28+
29+
/** (name, id) pairs across the groups — keyOf returns the id verbatim (incl. INTERCEPTED). */
30+
static Stream<Arguments> knownTags() {
31+
return Stream.of(
32+
Arguments.of(Tags.ERROR, KnownTags.ERROR_ID),
33+
Arguments.of(DDTags.PARENT_ID, KnownTags.DD_PARENT_ID),
34+
Arguments.of(DDTags.BASE_SERVICE, KnownTags.DD_BASE_SERVICE_ID),
35+
Arguments.of(Tags.VERSION, KnownTags.VERSION_ID),
36+
Arguments.of("env", KnownTags.ENV_ID),
37+
Arguments.of(DDTags.DJM_ENABLED, KnownTags.DD_DJM_ENABLED_ID),
38+
Arguments.of(DDTags.DSM_ENABLED, KnownTags.DD_DSM_ENABLED_ID),
39+
Arguments.of(DDTags.TRACER_HOST, KnownTags.DD_TRACER_HOST_ID),
40+
Arguments.of(DDTags.DD_INTEGRATION, KnownTags.DD_INTEGRATION_ID),
41+
Arguments.of(DDTags.DD_SVC_SRC, KnownTags.DD_SVC_SRC_ID),
42+
Arguments.of(Tags.PEER_SERVICE, KnownTags.PEER_SERVICE_ID),
43+
Arguments.of(DDTags.PEER_SERVICE_REMAPPED_FROM, KnownTags.DD_PEER_SERVICE_REMAPPED_FROM_ID),
44+
Arguments.of(Tags.HTTP_METHOD, KnownTags.HTTP_METHOD_ID),
45+
Arguments.of(Tags.HTTP_ROUTE, KnownTags.HTTP_ROUTE_ID),
46+
Arguments.of(Tags.HTTP_URL, KnownTags.HTTP_URL_ID),
47+
Arguments.of(Tags.PEER_HOSTNAME, KnownTags.PEER_HOSTNAME_ID),
48+
Arguments.of(Tags.PEER_HOST_IPV4, KnownTags.PEER_IPV4_ID),
49+
Arguments.of(Tags.PEER_HOST_IPV6, KnownTags.PEER_IPV6_ID),
50+
Arguments.of(Tags.PEER_PORT, KnownTags.PEER_PORT_ID),
51+
Arguments.of(Tags.COMPONENT, KnownTags.COMPONENT_ID),
52+
Arguments.of(Tags.SPAN_KIND, KnownTags.SPAN_KIND_ID),
53+
Arguments.of(DDTags.LANGUAGE_TAG_KEY, KnownTags.LANGUAGE_ID),
54+
Arguments.of(Tags.DB_TYPE, KnownTags.DB_TYPE_ID),
55+
Arguments.of(Tags.DB_INSTANCE, KnownTags.DB_INSTANCE_ID),
56+
Arguments.of(Tags.DB_USER, KnownTags.DB_USER_ID),
57+
Arguments.of(Tags.DB_OPERATION, KnownTags.DB_OPERATION_ID),
58+
Arguments.of(Tags.DB_POOL_NAME, KnownTags.DB_POOL_NAME_ID));
59+
}
60+
61+
/**
62+
* (otelName, canonicalId, datadogName) — the OpenTelemetry name resolves (keyOf) to the canonical
63+
* tag's id; datadogNameOf returns the Datadog name and openTelemetryNameOf returns the OTel name.
64+
*/
65+
static Stream<Arguments> otelNamedTags() {
66+
return Stream.of(
67+
Arguments.of("http.request.method", KnownTags.HTTP_METHOD_ID, "http.method"),
68+
Arguments.of(
69+
"http.response.status_code", KnownTags.HTTP_STATUS_CODE_ID, "http.status_code"),
70+
Arguments.of("url.full", KnownTags.HTTP_URL_ID, "http.url"),
71+
Arguments.of("server.address", KnownTags.HTTP_HOSTNAME_ID, "http.hostname"),
72+
Arguments.of("user_agent.original", KnownTags.HTTP_USERAGENT_ID, "http.useragent"),
73+
Arguments.of("url.query", KnownTags.HTTP_QUERY_STRING_ID, "http.query.string"),
74+
Arguments.of("db.system", KnownTags.DB_TYPE_ID, "db.type"),
75+
Arguments.of("db.operation.name", KnownTags.DB_OPERATION_ID, "db.operation"),
76+
Arguments.of("db.query.text", KnownTags.DB_STATEMENT_ID, "db.statement"),
77+
Arguments.of("service.name", KnownTags.SERVICE_ID, "service"));
78+
}
79+
80+
/**
81+
* The subset flagged INTERCEPTED (sign bit) — must agree with the interceptor's needsIntercept.
82+
*/
83+
static Stream<Arguments> interceptedTags() {
84+
return Stream.of(
85+
Arguments.of(KnownTags.ERROR_ID),
86+
Arguments.of(KnownTags.PEER_SERVICE_ID),
87+
Arguments.of(KnownTags.HTTP_METHOD_ID),
88+
Arguments.of(KnownTags.HTTP_URL_ID),
89+
Arguments.of(KnownTags.SPAN_KIND_ID));
90+
}
91+
92+
/**
93+
* Trace-level tags (live on the TraceSegment's TagMap) — their id carries the LEVEL_TRACE bit.
94+
*/
95+
static Stream<Arguments> traceLevelTags() {
96+
return Stream.of(
97+
Arguments.of(KnownTags.DD_BASE_SERVICE_ID),
98+
Arguments.of(KnownTags.VERSION_ID),
99+
Arguments.of(KnownTags.ENV_ID),
100+
Arguments.of(KnownTags.LANGUAGE_ID),
101+
Arguments.of(KnownTags.RUNTIME_ID),
102+
Arguments.of(KnownTags.DD_TRACER_HOST_ID),
103+
Arguments.of(KnownTags.DD_DJM_ENABLED_ID));
104+
}
105+
106+
/** Span-level tags — their id leaves the LEVEL_TRACE bit clear. */
107+
static Stream<Arguments> spanLevelTags() {
108+
return Stream.of(
109+
Arguments.of(KnownTags.HTTP_METHOD_ID),
110+
Arguments.of(KnownTags.HTTP_URL_ID),
111+
Arguments.of(KnownTags.DB_TYPE_ID),
112+
Arguments.of(KnownTags.COMPONENT_ID),
113+
Arguments.of(KnownTags.SPAN_KIND_ID),
114+
Arguments.of(KnownTags.PEER_SERVICE_ID));
115+
}
116+
117+
@BeforeAll
118+
static void registerResolver() {
119+
// Generated ids are compile-time constants (literal), so a constant reference is inlined and
120+
// never triggers KnownTags.<clinit>. init() forces class-load -> KnownTagCodec.register.
121+
KnownTags.init();
122+
}
123+
124+
@Test
125+
void resolverIsActiveAfterInit() {
126+
assertTrue(KnownTagCodec.isActive());
127+
}
128+
129+
@ParameterizedTest
130+
@MethodSource("knownTags")
131+
void keyOfResolvesNameToId(String name, long id) {
132+
assertEquals(id, KnownTagCodec.keyOf(name), "keyOf(" + name + ")");
133+
}
134+
135+
@ParameterizedTest
136+
@MethodSource("knownTags")
137+
void nameOfResolvesIdToName(String name, long id) {
138+
assertEquals(name, KnownTagCodec.nameOf(id), "nameOf(" + name + ")");
139+
}
140+
141+
@ParameterizedTest
142+
@MethodSource("otelNamedTags")
143+
void otelNameResolvesToCanonicalId(String otelName, long id, String datadogName) {
144+
// Inbound (keyOf) is many->one: both names land on the same canonical id.
145+
assertEquals(id, KnownTagCodec.keyOf(otelName), "keyOf(" + otelName + ")");
146+
assertEquals(id, KnownTagCodec.keyOf(datadogName), "keyOf(" + datadogName + ")");
147+
}
148+
149+
@ParameterizedTest
150+
@MethodSource("otelNamedTags")
151+
void namespaceAccessorsReturnPerNamespaceName(String otelName, long id, String datadogName) {
152+
assertEquals(datadogName, KnownTagCodec.datadogNameOf(id), "datadogNameOf");
153+
assertEquals(otelName, KnownTagCodec.openTelemetryNameOf(id), "openTelemetryNameOf");
154+
// nameOf stays the Datadog name -- outbound is namespace-specific, not normalized to OTel.
155+
assertEquals(datadogName, KnownTagCodec.nameOf(id), "nameOf stays Datadog");
156+
}
157+
158+
@ParameterizedTest
159+
@MethodSource("otelNamedTags")
160+
void openTelemetryTagOfReturnsTheRename(String otelName, long id, String datadogName) {
161+
assertEquals(otelName, KnownTagCodec.openTelemetryTagOf(id), "openTelemetryTagOf");
162+
}
163+
164+
@Test
165+
void openTelemetryTagOfPassesThroughWhenThereIsNoRename() {
166+
// http.route declares no otel-name, so the OpenTelemetry namespace emits the Datadog name.
167+
assertNull(KnownTagCodec.openTelemetryNameOf(KnownTags.HTTP_ROUTE_ID), "no declared rename");
168+
assertEquals(
169+
KnownTagCodec.nameOf(KnownTags.HTTP_ROUTE_ID),
170+
KnownTagCodec.openTelemetryTagOf(KnownTags.HTTP_ROUTE_ID),
171+
"pass-through falls back to the Datadog name");
172+
}
173+
174+
@Test
175+
void openTelemetryTagOfIsNullForAnUnknownId() {
176+
// A custom tag has no registry name in any namespace; only its holder knows its key.
177+
assertNull(KnownTagCodec.openTelemetryTagOf(0L));
178+
}
179+
180+
@Test
181+
void tagsWithoutOtelNameReturnNull() {
182+
assertNull(KnownTagCodec.openTelemetryNameOf(KnownTags.HTTP_ROUTE_ID)); // no OTel name declared
183+
assertNull(KnownTagCodec.openTelemetryNameOf(0L)); // unknown id
184+
}
185+
186+
@ParameterizedTest
187+
@MethodSource("interceptedTags")
188+
void interceptedTagsCarryFlag(long id) {
189+
assertTrue(KnownTagCodec.isIntercepted(id), "isIntercepted");
190+
}
191+
192+
@Test
193+
void nonInterceptedTagsDoNotCarryFlag() {
194+
Set<Long> intercepted = new HashSet<>();
195+
interceptedTags().forEach(a -> intercepted.add((Long) a.get()[0]));
196+
knownTags()
197+
.forEach(
198+
a -> {
199+
long id = (Long) a.get()[1];
200+
if (!intercepted.contains(id)) {
201+
assertFalse(KnownTagCodec.isIntercepted(id), "not intercepted: " + a.get()[0]);
202+
}
203+
});
204+
}
205+
206+
@Test
207+
void unknownNamesResolveToZero() {
208+
assertEquals(0L, KnownTagCodec.keyOf("definitely.not.a.known.tag"));
209+
assertEquals(0L, KnownTagCodec.keyOf("http.statuscode")); // close-but-not-listed
210+
assertEquals(0L, KnownTagCodec.keyOf(""));
211+
}
212+
213+
@Test
214+
void unknownIdsResolveToNullName() {
215+
assertNull(KnownTagCodec.nameOf(0L));
216+
assertNull(KnownTagCodec.nameOf(KnownTagCodec.makeTagId(9999))); // serial with no assigned tag
217+
}
218+
219+
@Test
220+
void errorIsReservedTheRestAreStored() {
221+
assertTrue(KnownTagCodec.isReserved(KnownTags.ERROR_ID), "ERROR reserved");
222+
assertFalse(KnownTagCodec.isStored(KnownTags.ERROR_ID), "ERROR not stored");
223+
knownTags()
224+
.forEach(
225+
a -> {
226+
long id = (Long) a.get()[1];
227+
if (id != KnownTags.ERROR_ID) {
228+
assertTrue(KnownTagCodec.isStored(id), "stored: " + a.get()[0]);
229+
assertFalse(KnownTagCodec.isReserved(id), "not reserved: " + a.get()[0]);
230+
}
231+
});
232+
}
233+
234+
@Test
235+
void globalSerialsAreUnique() {
236+
List<Long> serials = new ArrayList<>();
237+
knownTags().forEach(a -> serials.add((long) KnownTagCodec.serialNum((Long) a.get()[1])));
238+
assertEquals(serials.size(), new HashSet<>(serials).size(), "globalSerials must be unique");
239+
}
240+
241+
@ParameterizedTest
242+
@MethodSource("traceLevelTags")
243+
void traceLevelTagsCarryLevelBit(long id) {
244+
assertTrue(KnownTagCodec.isTraceLevel(id), "isTraceLevel");
245+
}
246+
247+
@ParameterizedTest
248+
@MethodSource("spanLevelTags")
249+
void spanLevelTagsClearLevelBit(long id) {
250+
assertFalse(KnownTagCodec.isTraceLevel(id), "not trace-level");
251+
}
252+
253+
@Test
254+
void levelBitCompositionRoundTrips() {
255+
long spanId = KnownTagCodec.makeTagId(300); // no level bit
256+
assertFalse(KnownTagCodec.isTraceLevel(spanId));
257+
long traceId = KnownTagCodec.traceLevel(spanId);
258+
assertTrue(KnownTagCodec.isTraceLevel(traceId));
259+
// level bit is orthogonal to the serial — it survives setting the bit
260+
assertEquals(KnownTagCodec.serialNum(spanId), KnownTagCodec.serialNum(traceId));
261+
assertEquals(traceId, KnownTagCodec.traceLevel(traceId), "traceLevel is idempotent");
262+
}
263+
264+
@Test
265+
void serialEncodingRoundTrips() {
266+
long id = KnownTagCodec.makeTagId(FIRST_STORED_SERIAL_PLUS_7);
267+
assertEquals(FIRST_STORED_SERIAL_PLUS_7, KnownTagCodec.serialNum(id));
268+
assertTrue(KnownTagCodec.isStored(id));
269+
assertFalse(KnownTagCodec.isIntercepted(id));
270+
assertFalse(KnownTagCodec.isTraceLevel(id));
271+
}
272+
273+
private static final int FIRST_STORED_SERIAL_PLUS_7 = KnownTagCodec.FIRST_STORED_SERIAL + 7;
274+
}

0 commit comments

Comments
 (0)