diff --git a/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java b/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java
index 7ed9dff633a..e85294170e7 100644
--- a/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java
+++ b/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java
@@ -22,23 +22,27 @@ public static boolean isActive() {
}
/*
- * tagId bit layout: [63 intercepted] [62-48 globalSerial (15 bits)] [47-32 fieldPos]
- * [31-0 nameHash]. Bit 63 (the sign bit) marks a tag the tag interceptor must see, so the check
- * is a single {@code tagId < 0}. globalSerial is globally unique per known tag; fieldPos is its
- * slot in the global positional layout (TagMap.knownEntries index); nameHash is
- * TagMap.Entry#_hash(name) and is layout-independent. Unknown (string-only) tags have the upper
- * 32 bits zero. NOTE: TagMap.Entry decodes nameHash inline as (int) tagId on its hot path, so the
- * low-32 encoding here must stay in sync with that.
+ * tagId bit layout: [63 intercepted] [62-48 globalSerial (15 bits)] [47-42 group-decl (6 bits)]
+ * [41-32 field-decl (10 bits)] [31-0 reserved, zero]. Bit 63 (the sign bit) marks a tag the tag
+ * interceptor must see, so the check is a single {@code tagId < 0}. globalSerial is globally
+ * unique per known tag. The middle 16 bits carry the tag's DECLARATION coordinate: group-decl is
+ * the declaration group (the trace-level tier / each span type / each mixin is a group),
+ * field-decl is the tag's ordinal within that group (restarts per group). Together they drive the dense
+ * store's two-tier presence fast path (per-map group mask, then field bloom) — see {@link
+ * TagMap}. The low 32 bits are unused for known ids (the whole id is fully determined by
+ * serial + coordinate, so the generator can emit a literal); they are free for a wider
+ * group-decl/field-decl split later. Unknown (string-only) custom tags are NOT known ids — they
+ * key off {@code TagMap.Entry#_hash(name)} in their own bucket path and never enter here.
*/
public static int globalSerial(long tagId) {
return (int) ((tagId >>> 48) & 0x7FFF);
}
/**
- * Flag bit (the sign bit) marking a tag the tag interceptor must process — reserved/"virtual"
- * tags AND intercepted-but-stored tags (e.g. http.method, which the interceptor side-effects and
- * also stores). Encoded in the id so {@code DDSpanContext.setTag(long)} can route with a single
- * sign test ({@link #isIntercepted}) instead of resolving the name. Non-intercepted tags (peer.*,
+ * Flag bit (the sign bit) marking a tag the tag interceptor must process — reserved tags AND
+ * intercepted-but-stored tags (e.g. http.method, which the interceptor side-effects and also
+ * stores). Encoded in the id so {@code DDSpanContext.setTag(long)} can route with a single sign
+ * test ({@link #isIntercepted}) instead of resolving the name. Non-intercepted tags (peer.*,
* base.service, …) leave it clear and take the fast store path. Must agree with the interceptor's
* name-based {@code needsIntercept} for every assigned id.
*/
@@ -54,25 +58,54 @@ public static long intercepted(long tagId) {
return tagId | INTERCEPTED;
}
- public static int fieldPos(long tagId) {
- return (int) ((tagId >>> 32) & 0xFFFF);
+ // The middle 16 bits [47-32] hold the declaration coordinate, split so the two-tier presence
+ // fast path can read each half cheaply: group-decl (high 6 bits, [47-42]) selects the tag's
+ // declaration group (<=64 groups -> one long group mask, where a clear bit proves the whole
+ // group absent and enables a bulk fast insert); field-decl (low 10 bits, [41-32]) is the tag's
+ // ordinal within that group (<=1024 tags/group), which the field bloom hashes (field-decl & 63)
+ // once a group is present. See TagMap's dense-store fast path.
+ static final int GROUP_DECL_SHIFT = 42;
+ static final int GROUP_DECL_MASK = 0x3F; // 6 bits
+ static final int FIELD_DECL_SHIFT = 32;
+ static final int FIELD_DECL_MASK = 0x3FF; // 10 bits
+
+ /**
+ * The tag's declaration group: the trace-level tier, a span type, or a mixin. Assigned by the tag
+ * registry; drives the two-tier presence fast path's group mask.
+ */
+ public static int groupDecl(long tagId) {
+ return (int) ((tagId >>> GROUP_DECL_SHIFT) & GROUP_DECL_MASK);
}
- public static int nameHash(long tagId) {
- return (int) tagId;
+ /** The tag's ordinal within its declaration group. */
+ public static int fieldDecl(long tagId) {
+ return (int) ((tagId >>> FIELD_DECL_SHIFT) & FIELD_DECL_MASK);
}
/**
- * globalSerial partition. {@code [1, FIRST_STORED_SERIAL)} is reserved for "virtual" tags that
- * are specially handled (redirected to span fields or processed by the tag interceptor) and are
- * NOT stored in the TagMap — these are hand-assigned in tracer core. {@code [FIRST_STORED_SERIAL,
- * ..]} is for generated convention tags that ARE stored (slotted/bucketed). {@code globalSerial
- * == 0} means unknown / string-only. Both core and the code generator must agree on this
- * boundary.
+ * globalSerial partition. {@code [1, FIRST_STORED_SERIAL)} is the RESERVED tier and {@code
+ * [FIRST_STORED_SERIAL, ..]} is the STORED tier; {@code globalSerial == 0} means unknown /
+ * string-only. Both core and the code generator must agree on this boundary.
+ *
+ *
Reserved is the shared mechanism: the tracer reserves the key and handles it itself
+ * instead of putting it in the TagMap. It says nothing about whether a value exists — that splits
+ * into two kinds (the {@code kind:} in the overlay):
+ *
+ *
+ * - structural — the value does exist, it just lives in a first-class
+ * span/trace field (service, resource.name, error, span.type, origin), not the tag map.
+ *
- directive — there is no stored value; the key is a command that triggers
+ * trace behavior (sampling.priority, manual.keep, measured).
+ *
+ *
+ * "virtual" over-claims non-existence (wrong for structural) and "built-in" over-claims existence
+ * (wrong for directive), so the tier is named for the mechanism they share: reserved. These are
+ * hand-assigned in the overlay. Stored tags are the generated convention tags that ARE put
+ * in the map (slotted/bucketed).
*/
public static final int FIRST_STORED_SERIAL = 256;
- /** True if the tagId names a reserved "virtual"/specially-handled tag (not stored in the map). */
+ /** True if the tagId names a reserved (structural/directive) tag — handled, not stored. */
public static boolean isReserved(long tagId) {
int globalSerial = globalSerial(tagId);
return globalSerial > 0 && globalSerial < FIRST_STORED_SERIAL;
@@ -84,48 +117,53 @@ public static boolean isStored(long tagId) {
}
/**
- * Sentinel {@code fieldPos} meaning "no positional slot". It is the maximum value the 16-bit
- * fieldPos field can hold, so it always compares {@code >= slotCount()} and routes to the hash
+ * Sentinel {@code field-decl} meaning "no positional slot". It is the maximum value the 10-bit
+ * field-decl field can hold, so it always compares {@code >= slotCount()} and routes to the hash
* buckets rather than the fast positional array. Two kinds of tagId use it:
*
*
- * - Reserved/virtual tags ({@code globalSerial < FIRST_STORED_SERIAL}) — not stored at all;
- * the sentinel just guarantees an incidental store never lands in a slot.
+ *
- Reserved tags ({@code globalSerial < FIRST_STORED_SERIAL}) — not stored at all; the
+ * sentinel just guarantees an incidental store never lands in a slot.
*
- Unslotted stored tags ({@code globalSerial >= FIRST_STORED_SERIAL}) — "low-priority" tags
* that get a stable id (and so {@code keyOf}/{@code nameOf} unification with their string
- * form) but are deliberately not given a slot, so they live in the buckets and don't widen
- * {@code knownEntries[]} for every span. {@code getEntry(long)} for these resolves the name
- * and rehashes — the cost of not owning a slot.
+ * form) but are deliberately not given a slot, so they live in the buckets. {@code
+ * getEntry(long)} for these resolves the name and rehashes — the cost of not owning a slot.
*
*/
- public static final int NO_SLOT = 0xFFFF;
+ public static final int NO_SLOT = FIELD_DECL_MASK; // field-decl all-ones sentinel
/**
* True if the tagId names a stored tag that deliberately has no positional slot (bucket-only).
*/
public static boolean isUnslotted(long tagId) {
- return isStored(tagId) && fieldPos(tagId) == NO_SLOT;
+ return isStored(tagId) && fieldDecl(tagId) == NO_SLOT;
}
/**
- * Builds a tagId from its parts: {@code globalSerial} (globally unique per known tag), {@code
- * fieldPos} (the tag's slot within its span type's positional table), and the tag {@code name}
- * (whose hash is computed via the same function the runtime uses, so the low 32 bits match {@link
- * TagMap.Entry#hash()}). Inverse of {@link #globalSerial}/{@link #fieldPos}/{@link #nameHash}.
+ * Builds a tagId from all three parts: {@code globalSerial} (globally unique per known tag),
+ * {@code groupDecl} (its declaration group), and {@code fieldDecl} (its ordinal within that
+ * group). The low 32 bits are zero, so the id is fully determined by these parts — the generator
+ * emits it as a literal. Inverse of {@link #globalSerial}/{@link #groupDecl}/{@link #fieldDecl}.
* Intended for the code generator and tests.
*/
- public static long tagId(int globalSerial, int fieldPos, String name) {
- long nameHash = TagMap.Entry._hash(name) & 0xFFFFFFFFL;
- return ((long) globalSerial << 48) | ((long) (fieldPos & 0xFFFF) << 32) | nameHash;
+ public static long tagId(int globalSerial, int groupDecl, int fieldDecl) {
+ return ((long) globalSerial << 48)
+ | ((long) (groupDecl & GROUP_DECL_MASK) << GROUP_DECL_SHIFT)
+ | ((long) (fieldDecl & FIELD_DECL_MASK) << FIELD_DECL_SHIFT);
+ }
+
+ /** Builds a tagId in group 0 — shorthand for {@link #tagId(int, int, int)} with group-decl 0. */
+ public static long tagId(int globalSerial, int fieldDecl) {
+ return tagId(globalSerial, 0, fieldDecl);
}
/**
- * Builds a tagId with no positional slot ({@code fieldPos == }{@link #NO_SLOT}). Use for reserved
- * "virtual" tags and for "low-priority" stored tags that get a stable id but are intentionally
+ * Builds a tagId with no positional slot ({@code field-decl == }{@link #NO_SLOT}). Use for
+ * reserved tags and for "low-priority" stored tags that get a stable id but are intentionally
* kept out of the fast slot array (they route to the hash buckets). See {@link #NO_SLOT}.
*/
- public static long tagId(int globalSerial, String name) {
- return tagId(globalSerial, NO_SLOT, name);
+ public static long tagId(int globalSerial) {
+ return tagId(globalSerial, NO_SLOT);
}
// Number of positional slots in the global layout = (max stored fieldPos) + 1, declared by the
diff --git a/internal-api/src/main/java/datadog/trace/api/KnownTags.java b/internal-api/src/main/java/datadog/trace/api/KnownTags.java
index 391b000b844..c1e6ccd4a49 100644
--- a/internal-api/src/main/java/datadog/trace/api/KnownTags.java
+++ b/internal-api/src/main/java/datadog/trace/api/KnownTags.java
@@ -1,225 +1,336 @@
package datadog.trace.api;
-import datadog.trace.bootstrap.instrumentation.api.Tags;
import datadog.trace.util.StringIndex;
-/**
- * Hand-assigned tag-id constants for well-known tags, plus the {@link KnownTagCodec.Resolver} that
- * resolves them. This is the single registry shared by the tracer core and by instrumentation
- * (decorators) — it lives in {@code internal-api} so both layers can reference the ids; the
- * eventual code generator will replace the hand assignment here.
- *
- * Reserved serials {@code [1, KnownTagCodec.FIRST_STORED_SERIAL)} name "virtual" tags handled by
- * the tag interceptor / span fields and are NOT stored in the {@code TagMap}; their {@code
- * fieldPos} is the {@link KnownTagCodec#NO_SLOT} sentinel that is out of slot range, so any
- * incidental store routes to the hash buckets rather than a positional slot. Serials {@code >=
- * FIRST_STORED_SERIAL} name stored tags that slot/bucket normally (or, with {@code NO_SLOT}, are
- * stored bucket-only).
- *
- *
The resolver registers on class initialization, so simply referencing any constant here makes
- * tag-id resolution live before the first span is built.
- *
- *
Slice-1 note (keyOf substrate): the {@code fieldPos} assignments below (and {@link
- * #SLOT_COUNT}) describe a single universal positional layout (slots 0..25). That layout is
- * currently dormant — no dense store consumes {@code fieldPos} yet — and is provisional: the
- * dense-store slice replaces the universal layout with per-role / per-type sizing (see the
- * over-provision finding in {@code dense-tagmap-design.md}). {@code keyOf}/{@code nameOf} depend
- * only on {@code globalSerial} + name, not {@code fieldPos}, so the ids themselves are stable
- * across any layout scheme.
- */
+// Interim hand-maintained known-tag table with coordinate-assigned ids (globalSerial + group-decl
+// + field-decl). The tag-registry code generator replaces this with a generated equivalent
+// (byte-for-byte-equal ids) in a follow-up change; kept hand-written here so the two-tier presence
+// bloom has a coordinate substrate to build on independently of the generator.
public final class KnownTags {
- // slot count = (max stored fieldPos) + 1. Stored tags use fieldPos 0..25. PROVISIONAL universal
- // layout — see the slice-1 note above; the dense-store slice supersedes this with role/type
- // sizing.
- static final int SLOT_COUNT = 26;
+ static final int SLOT_COUNT = 13;
+
+ // ---- reserved (routed to span fields or directives; not stored) ----
+ static final int ERROR_SERIAL = 1;
+ // tagId(serial=1, intercepted=true, group=0, field=NO_SLOT) [structural -> error] "error"
+ public static final long ERROR = 0x800103FF00000000L;
+ static final int SERVICE_SERIAL = 2;
+ // tagId(serial=2, intercepted=true, group=0, field=NO_SLOT) [structural -> service] "service"
+ public static final long SERVICE = 0x800203FF00000000L;
+ static final int RESOURCE_NAME_SERIAL = 3;
+ // tagId(serial=3, intercepted=true, group=0, field=NO_SLOT) [structural -> resource]
+ // "resource.name"
+ public static final long RESOURCE_NAME = 0x800303FF00000000L;
+ static final int SPAN_TYPE_SERIAL = 4;
+ // tagId(serial=4, intercepted=true, group=0, field=NO_SLOT) [structural -> type] "span.type"
+ public static final long SPAN_TYPE = 0x800403FF00000000L;
+ static final int ORIGIN_SERIAL = 5;
+ // tagId(serial=5, intercepted=true, group=0, field=NO_SLOT) [structural -> origin] "origin"
+ public static final long ORIGIN = 0x800503FF00000000L;
+ static final int SAMPLING_PRIORITY_SERIAL = 6;
+ // tagId(serial=6, intercepted=true, group=0, field=NO_SLOT) [directive] "sampling.priority"
+ public static final long SAMPLING_PRIORITY = 0x800603FF00000000L;
+ static final int MANUAL_KEEP_SERIAL = 7;
+ // tagId(serial=7, intercepted=true, group=0, field=NO_SLOT) [directive] "manual.keep"
+ public static final long MANUAL_KEEP = 0x800703FF00000000L;
+ static final int MANUAL_DROP_SERIAL = 8;
+ // tagId(serial=8, intercepted=true, group=0, field=NO_SLOT) [directive] "manual.drop"
+ public static final long MANUAL_DROP = 0x800803FF00000000L;
+ static final int MEASURED_SERIAL = 9;
+ // tagId(serial=9, intercepted=true, group=0, field=NO_SLOT) [directive] "measured"
+ public static final long MEASURED = 0x800903FF00000000L;
+ static final int ANALYTICS_SAMPLE_RATE_SERIAL = 10;
+ // tagId(serial=10, intercepted=true, group=0, field=NO_SLOT) [directive]
+ // "analytics.sample_rate"
+ public static final long ANALYTICS_SAMPLE_RATE = 0x800A03FF00000000L;
+
+ // ---- stored (dense field-decl, or bucketed when field=NO_SLOT) ----
+ static final int DD_BASE_SERVICE_SERIAL = 256;
+ // tagId(serial=256, intercepted=false, group=0, field=0)
+ // "_dd.base_service"
+ public static final long DD_BASE_SERVICE = 0x0100000000000000L;
+ static final int VERSION_SERIAL = 257;
+ // tagId(serial=257, intercepted=false, group=0, field=1) "version"
+ public static final long VERSION = 0x0101000100000000L;
+ static final int ENV_SERIAL = 258;
+ // tagId(serial=258, intercepted=false, group=0, field=2) "env"
+ public static final long ENV = 0x0102000200000000L;
+ static final int LANGUAGE_SERIAL = 259;
+ // tagId(serial=259, intercepted=false, group=0, field=3) "language"
+ public static final long LANGUAGE = 0x0103000300000000L;
+ static final int RUNTIME_ID_SERIAL = 260;
+ // tagId(serial=260, intercepted=false, group=0, field=4) "runtime-id"
+ public static final long RUNTIME_ID = 0x0104000400000000L;
+ static final int DD_TRACER_HOST_SERIAL = 261;
+ // tagId(serial=261, intercepted=false, group=0, field=5)
+ // "_dd.tracer_host"
+ public static final long DD_TRACER_HOST = 0x0105000500000000L;
+ static final int DD_GIT_COMMIT_SHA_SERIAL = 262;
+ // tagId(serial=262, intercepted=false, group=0, field=6)
+ // "_dd.git.commit.sha"
+ public static final long DD_GIT_COMMIT_SHA = 0x0106000600000000L;
+ static final int DD_GIT_REPOSITORY_URL_SERIAL = 263;
+ // tagId(serial=263, intercepted=false, group=0, field=7)
+ // "_dd.git.repository_url"
+ public static final long DD_GIT_REPOSITORY_URL = 0x0107000700000000L;
+ static final int DD_PROFILING_ENABLED_SERIAL = 264;
+ // tagId(serial=264, intercepted=false, group=0, field=8)
+ // "_dd.profiling.enabled"
+ public static final long DD_PROFILING_ENABLED = 0x0108000800000000L;
+ static final int DD_DSM_ENABLED_SERIAL = 265;
+ // tagId(serial=265, intercepted=false, group=0, field=9)
+ // "_dd.dsm.enabled"
+ public static final long DD_DSM_ENABLED = 0x0109000900000000L;
+ static final int DD_APPSEC_ENABLED_SERIAL = 266;
+ // tagId(serial=266, intercepted=false, group=0, field=10)
+ // "_dd.appsec.enabled"
+ public static final long DD_APPSEC_ENABLED = 0x010A000A00000000L;
+ static final int DD_DJM_ENABLED_SERIAL = 267;
+ // tagId(serial=267, intercepted=false, group=0, field=11)
+ // "_dd.djm.enabled"
+ public static final long DD_DJM_ENABLED = 0x010B000B00000000L;
+ static final int DD_CIVISIBILITY_ENABLED_SERIAL = 268;
+ // tagId(serial=268, intercepted=false, group=0, field=12)
+ // "_dd.civisibility.enabled"
+ public static final long DD_CIVISIBILITY_ENABLED = 0x010C000C00000000L;
+ static final int DD_PARENT_ID_SERIAL = 269;
+ // tagId(serial=269, intercepted=false, group=1, field=0) "_dd.parent_id"
+ public static final long DD_PARENT_ID = 0x010D040000000000L;
+ static final int COMPONENT_SERIAL = 270;
+ // tagId(serial=270, intercepted=false, group=1, field=1) "component"
+ public static final long COMPONENT = 0x010E040100000000L;
+ static final int SPAN_KIND_SERIAL = 271;
+ // tagId(serial=271, intercepted=true, group=1, field=2) "span.kind"
+ public static final long SPAN_KIND = 0x810F040200000000L;
+ static final int DD_INTEGRATION_SERIAL = 272;
+ // tagId(serial=272, intercepted=false, group=1, field=3) "_dd.integration"
+ public static final long DD_INTEGRATION = 0x0110040300000000L;
+ static final int DD_SVC_SRC_SERIAL = 273;
+ // tagId(serial=273, intercepted=false, group=1, field=NO_SLOT) "_dd.svc_src"
+ public static final long DD_SVC_SRC = 0x011107FF00000000L;
+ static final int ERROR_TYPE_SERIAL = 274;
+ // tagId(serial=274, intercepted=false, group=1, field=4) "error.type"
+ public static final long ERROR_TYPE = 0x0112040400000000L;
+ static final int ERROR_MESSAGE_SERIAL = 275;
+ // tagId(serial=275, intercepted=false, group=1, field=5) "error.message"
+ public static final long ERROR_MESSAGE = 0x0113040500000000L;
+ static final int ERROR_STACK_SERIAL = 276;
+ // tagId(serial=276, intercepted=false, group=1, field=6) "error.stack"
+ public static final long ERROR_STACK = 0x0114040600000000L;
+ static final int DB_TYPE_SERIAL = 277;
+ // tagId(serial=277, intercepted=false, group=2, field=0) "db.type"
+ public static final long DB_TYPE = 0x0115080000000000L;
+ static final int DB_INSTANCE_SERIAL = 278;
+ // tagId(serial=278, intercepted=false, group=2, field=1) "db.instance"
+ public static final long DB_INSTANCE = 0x0116080100000000L;
+ static final int DB_OPERATION_SERIAL = 279;
+ // tagId(serial=279, intercepted=false, group=2, field=2) "db.operation"
+ public static final long DB_OPERATION = 0x0117080200000000L;
+ static final int DB_USER_SERIAL = 280;
+ // tagId(serial=280, intercepted=false, group=2, field=3) "db.user"
+ public static final long DB_USER = 0x0118080300000000L;
+ static final int DB_POOL_NAME_SERIAL = 281;
+ // tagId(serial=281, intercepted=false, group=2, field=NO_SLOT) "db.pool.name"
+ public static final long DB_POOL_NAME = 0x01190BFF00000000L;
+ static final int DB_STATEMENT_SERIAL = 282;
+ // tagId(serial=282, intercepted=true, group=2, field=4) "db.statement"
+ public static final long DB_STATEMENT = 0x811A080400000000L;
+ static final int HTTP_METHOD_SERIAL = 283;
+ // tagId(serial=283, intercepted=true, group=3, field=0) "http.method"
+ public static final long HTTP_METHOD = 0x811B0C0000000000L;
+ static final int HTTP_STATUS_CODE_SERIAL = 284;
+ // tagId(serial=284, intercepted=false, group=3, field=1) "http.status_code"
+ public static final long HTTP_STATUS_CODE = 0x011C0C0100000000L;
+ static final int NETWORK_PROTOCOL_VERSION_SERIAL = 285;
+ // tagId(serial=285, intercepted=false, group=3, field=2)
+ // "network.protocol.version"
+ public static final long NETWORK_PROTOCOL_VERSION = 0x011D0C0200000000L;
+ static final int HTTP_URL_SERIAL = 286;
+ // tagId(serial=286, intercepted=true, group=4, field=0) "http.url"
+ public static final long HTTP_URL = 0x811E100000000000L;
+ static final int HTTP_RESEND_COUNT_SERIAL = 287;
+ // tagId(serial=287, intercepted=false, group=4, field=1) "http.resend_count"
+ public static final long HTTP_RESEND_COUNT = 0x011F100100000000L;
+ static final int HTTP_ROUTE_SERIAL = 288;
+ // tagId(serial=288, intercepted=false, group=5, field=0) "http.route"
+ public static final long HTTP_ROUTE = 0x0120140000000000L;
+ static final int HTTP_HOSTNAME_SERIAL = 289;
+ // tagId(serial=289, intercepted=false, group=5, field=1) "http.hostname"
+ public static final long HTTP_HOSTNAME = 0x0121140100000000L;
+ static final int HTTP_USERAGENT_SERIAL = 290;
+ // tagId(serial=290, intercepted=false, group=5, field=2) "http.useragent"
+ public static final long HTTP_USERAGENT = 0x0122140200000000L;
+ static final int HTTP_QUERY_STRING_SERIAL = 291;
+ // tagId(serial=291, intercepted=false, group=5, field=3) "http.query.string"
+ public static final long HTTP_QUERY_STRING = 0x0123140300000000L;
+ static final int SERVLET_PATH_SERIAL = 292;
+ // tagId(serial=292, intercepted=false, group=5, field=NO_SLOT) "servlet.path"
+ public static final long SERVLET_PATH = 0x012417FF00000000L;
+ static final int SERVLET_CONTEXT_SERIAL = 293;
+ // tagId(serial=293, intercepted=true, group=5, field=NO_SLOT) "servlet.context"
+ public static final long SERVLET_CONTEXT = 0x812517FF00000000L;
+ static final int VIEW_NAME_SERIAL = 294;
+ // tagId(serial=294, intercepted=false, group=6, field=0) "view.name"
+ public static final long VIEW_NAME = 0x0126180000000000L;
+ static final int TEST_NAME_SERIAL = 295;
+ // tagId(serial=295, intercepted=false, group=7, field=0) "test.name"
+ public static final long TEST_NAME = 0x01271C0000000000L;
+ static final int TEST_SUITE_SERIAL = 296;
+ // tagId(serial=296, intercepted=false, group=7, field=1) "test.suite"
+ public static final long TEST_SUITE = 0x01281C0100000000L;
+ static final int TEST_STATUS_SERIAL = 297;
+ // tagId(serial=297, intercepted=false, group=7, field=2) "test.status"
+ public static final long TEST_STATUS = 0x01291C0200000000L;
+ static final int TEST_FRAMEWORK_SERIAL = 298;
+ // tagId(serial=298, intercepted=false, group=7, field=3) "test.framework"
+ public static final long TEST_FRAMEWORK = 0x012A1C0300000000L;
+ static final int PEER_SERVICE_SERIAL = 299;
+ // tagId(serial=299, intercepted=true, group=8, field=0) "peer.service"
+ public static final long PEER_SERVICE = 0x812B200000000000L;
+ static final int DD_PEER_SERVICE_SOURCE_SERIAL = 300;
+ // tagId(serial=300, intercepted=false, group=8, field=1)
+ // "_dd.peer.service.source"
+ public static final long DD_PEER_SERVICE_SOURCE = 0x012C200100000000L;
+ static final int DD_PEER_SERVICE_REMAPPED_FROM_SERIAL = 301;
+ // tagId(serial=301, intercepted=false, group=8, field=2)
+ // "_dd.peer.service.remapped_from"
+ public static final long DD_PEER_SERVICE_REMAPPED_FROM = 0x012D200200000000L;
+ static final int PEER_HOSTNAME_SERIAL = 302;
+ // tagId(serial=302, intercepted=false, group=8, field=3) "peer.hostname"
+ public static final long PEER_HOSTNAME = 0x012E200300000000L;
+ static final int PEER_IPV4_SERIAL = 303;
+ // tagId(serial=303, intercepted=false, group=8, field=NO_SLOT) "peer.ipv4"
+ public static final long PEER_IPV4 = 0x012F23FF00000000L;
+ static final int PEER_IPV6_SERIAL = 304;
+ // tagId(serial=304, intercepted=false, group=8, field=NO_SLOT) "peer.ipv6"
+ public static final long PEER_IPV6 = 0x013023FF00000000L;
+ static final int PEER_PORT_SERIAL = 305;
+ // tagId(serial=305, intercepted=false, group=8, field=NO_SLOT) "peer.port"
+ public static final long PEER_PORT = 0x013123FF00000000L;
- // ---- reserved / virtual (tag-interceptor handled, not stored) ----
- // Reserved tags are always intercepted -> set the INTERCEPTED flag.
- public static final int ERROR_SERIAL = 1;
- public static final long ERROR_ID =
- KnownTagCodec.intercepted(KnownTagCodec.tagId(ERROR_SERIAL, Tags.ERROR));
-
- // ---- stored (slotted / bucketed) ----
- public static final int PARENT_ID_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL;
- public static final long PARENT_ID = KnownTagCodec.tagId(PARENT_ID_SERIAL, 0, DDTags.PARENT_ID);
-
- // common (process-constant) tags added by InternalTagsAdder to ~every span
- public static final int BASE_SERVICE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 1;
- public static final long BASE_SERVICE_ID =
- KnownTagCodec.tagId(BASE_SERVICE_SERIAL, 1, DDTags.BASE_SERVICE);
-
- public static final int VERSION_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 2;
- public static final long VERSION_ID = KnownTagCodec.tagId(VERSION_SERIAL, 2, Tags.VERSION);
-
- // build-time-known constant tags merged into defaultSpanTags (see CoreTracer.withTracerTags).
- // "env" is a base-mixin tag; the *_ENABLED flags are product-mixin tags. Hand-assigned for now.
- public static final String ENV = "env";
- public static final int ENV_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 3;
- public static final long ENV_ID = KnownTagCodec.tagId(ENV_SERIAL, 3, ENV);
-
- public static final int DJM_ENABLED_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 4;
- public static final long DJM_ENABLED_ID =
- KnownTagCodec.tagId(DJM_ENABLED_SERIAL, 4, DDTags.DJM_ENABLED);
-
- public static final int DSM_ENABLED_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 5;
- public static final long DSM_ENABLED_ID =
- KnownTagCodec.tagId(DSM_ENABLED_SERIAL, 5, DDTags.DSM_ENABLED);
-
- // common tags added by the tag post-processors (RemoteHostnameAdder / IntegrationAdder /
- // ServiceNameSourceAdder). Not intercepted; stored.
- public static final int TRACER_HOST_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 6;
- public static final long TRACER_HOST_ID =
- KnownTagCodec.tagId(TRACER_HOST_SERIAL, 6, DDTags.TRACER_HOST);
-
- public static final int INTEGRATION_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 7;
- public static final long INTEGRATION_ID =
- KnownTagCodec.tagId(INTEGRATION_SERIAL, 7, DDTags.DD_INTEGRATION);
-
- public static final int SVC_SRC_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 8;
- public static final long SVC_SRC_ID = KnownTagCodec.tagId(SVC_SRC_SERIAL, 8, DDTags.DD_SVC_SRC);
-
- // peer.service tags, read/written by PeerServiceCalculator (post-processor; uses Map put/get that
- // bypass the interceptor). peer.service is intercepted on the set-path but STORED, so it slots.
- public static final int PEER_SERVICE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 9;
- public static final long PEER_SERVICE_ID =
- KnownTagCodec.intercepted(KnownTagCodec.tagId(PEER_SERVICE_SERIAL, 9, Tags.PEER_SERVICE));
-
- public static final int PEER_SERVICE_REMAPPED_FROM_SERIAL =
- KnownTagCodec.FIRST_STORED_SERIAL + 10;
- public static final long PEER_SERVICE_REMAPPED_FROM_ID =
- KnownTagCodec.tagId(PEER_SERVICE_REMAPPED_FROM_SERIAL, 10, DDTags.PEER_SERVICE_REMAPPED_FROM);
-
- // HTTP tags read by HttpEndpointPostProcessor. http.method/http.url are intercepted-but-stored
- // (interceptTag side-effects then returns false → stored); http.route is not intercepted. All
- // stored, so the string set-path slots them via keyOf and the id reads here find them.
- public static final int HTTP_METHOD_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 11;
- public static final long HTTP_METHOD_ID =
- KnownTagCodec.intercepted(KnownTagCodec.tagId(HTTP_METHOD_SERIAL, 11, Tags.HTTP_METHOD));
-
- public static final int HTTP_ROUTE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 12;
- public static final long HTTP_ROUTE_ID =
- KnownTagCodec.tagId(HTTP_ROUTE_SERIAL, 12, Tags.HTTP_ROUTE);
-
- public static final int HTTP_URL_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 13;
- public static final long HTTP_URL_ID =
- KnownTagCodec.intercepted(KnownTagCodec.tagId(HTTP_URL_SERIAL, 13, Tags.HTTP_URL));
-
- // peer connection tags set by BaseDecorator.onPeerConnection on ~every client/producer span.
- // Not intercepted; stored. Slotted (common across client instrumentations).
- public static final int PEER_HOSTNAME_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 14;
- public static final long PEER_HOSTNAME_ID =
- KnownTagCodec.tagId(PEER_HOSTNAME_SERIAL, 14, Tags.PEER_HOSTNAME);
-
- public static final int PEER_HOST_IPV4_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 15;
- public static final long PEER_HOST_IPV4_ID =
- KnownTagCodec.tagId(PEER_HOST_IPV4_SERIAL, 15, Tags.PEER_HOST_IPV4);
-
- public static final int PEER_HOST_IPV6_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 16;
- public static final long PEER_HOST_IPV6_ID =
- KnownTagCodec.tagId(PEER_HOST_IPV6_SERIAL, 16, Tags.PEER_HOST_IPV6);
-
- public static final int PEER_PORT_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 17;
- public static final long PEER_PORT_ID = KnownTagCodec.tagId(PEER_PORT_SERIAL, 17, Tags.PEER_PORT);
-
- // Universal decorator tags — set on ~every span (component/span.kind via Base/Server/Client
- // decorators, language via ServerDecorator). span.kind is intercepted (setSpanKindOrdinal).
- public static final int COMPONENT_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 18;
- public static final long COMPONENT_ID = KnownTagCodec.tagId(COMPONENT_SERIAL, 18, Tags.COMPONENT);
-
- public static final int SPAN_KIND_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 19;
- public static final long SPAN_KIND_ID =
- KnownTagCodec.intercepted(KnownTagCodec.tagId(SPAN_KIND_SERIAL, 19, Tags.SPAN_KIND));
-
- public static final int LANGUAGE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 20;
- public static final long LANGUAGE_ID =
- KnownTagCodec.tagId(LANGUAGE_SERIAL, 20, DDTags.LANGUAGE_TAG_KEY);
-
- // JDBC / database-client tags — set on every db span (58% of petclinic spans). Not intercepted
- // (only db.statement is, and that's handled separately).
- public static final int DB_TYPE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 21;
- public static final long DB_TYPE_ID = KnownTagCodec.tagId(DB_TYPE_SERIAL, 21, Tags.DB_TYPE);
-
- public static final int DB_INSTANCE_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 22;
- public static final long DB_INSTANCE_ID =
- KnownTagCodec.tagId(DB_INSTANCE_SERIAL, 22, Tags.DB_INSTANCE);
-
- public static final int DB_USER_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 23;
- public static final long DB_USER_ID = KnownTagCodec.tagId(DB_USER_SERIAL, 23, Tags.DB_USER);
-
- public static final int DB_OPERATION_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 24;
- public static final long DB_OPERATION_ID =
- KnownTagCodec.tagId(DB_OPERATION_SERIAL, 24, Tags.DB_OPERATION);
-
- public static final int DB_POOL_NAME_SERIAL = KnownTagCodec.FIRST_STORED_SERIAL + 25;
- public static final long DB_POOL_NAME_ID =
- KnownTagCodec.tagId(DB_POOL_NAME_SERIAL, 25, Tags.DB_POOL_NAME);
-
- // Open-addressed name -> id table backing keyOf (data, not a switch): scales flat as the known
- // set grows, where a generated switch eventually falls off the inline threshold. KEYOF_NAMES and
- // KEYOF_VALUES are parallel; the table places names by hash and a parallel ids[] by slot.
private static final String[] KEYOF_NAMES = {
- Tags.ERROR,
- DDTags.PARENT_ID,
- DDTags.BASE_SERVICE,
- Tags.VERSION,
- ENV,
- DDTags.DJM_ENABLED,
- DDTags.DSM_ENABLED,
- DDTags.TRACER_HOST,
- DDTags.DD_INTEGRATION,
- DDTags.DD_SVC_SRC,
- Tags.PEER_SERVICE,
- DDTags.PEER_SERVICE_REMAPPED_FROM,
- Tags.HTTP_METHOD,
- Tags.HTTP_ROUTE,
- Tags.HTTP_URL,
- Tags.PEER_HOSTNAME,
- Tags.PEER_HOST_IPV4,
- Tags.PEER_HOST_IPV6,
- Tags.PEER_PORT,
- Tags.COMPONENT,
- Tags.SPAN_KIND,
- DDTags.LANGUAGE_TAG_KEY,
- Tags.DB_TYPE,
- Tags.DB_INSTANCE,
- Tags.DB_USER,
- Tags.DB_OPERATION,
- Tags.DB_POOL_NAME,
+ "error",
+ "service",
+ "resource.name",
+ "span.type",
+ "origin",
+ "sampling.priority",
+ "manual.keep",
+ "manual.drop",
+ "measured",
+ "analytics.sample_rate",
+ "_dd.base_service",
+ "version",
+ "env",
+ "language",
+ "runtime-id",
+ "_dd.tracer_host",
+ "_dd.git.commit.sha",
+ "_dd.git.repository_url",
+ "_dd.profiling.enabled",
+ "_dd.dsm.enabled",
+ "_dd.appsec.enabled",
+ "_dd.djm.enabled",
+ "_dd.civisibility.enabled",
+ "_dd.parent_id",
+ "component",
+ "span.kind",
+ "_dd.integration",
+ "_dd.svc_src",
+ "error.type",
+ "error.message",
+ "error.stack",
+ "db.type",
+ "db.instance",
+ "db.operation",
+ "db.user",
+ "db.pool.name",
+ "db.statement",
+ "http.method",
+ "http.status_code",
+ "network.protocol.version",
+ "http.url",
+ "http.resend_count",
+ "http.route",
+ "http.hostname",
+ "http.useragent",
+ "http.query.string",
+ "servlet.path",
+ "servlet.context",
+ "view.name",
+ "test.name",
+ "test.suite",
+ "test.status",
+ "test.framework",
+ "peer.service",
+ "_dd.peer.service.source",
+ "_dd.peer.service.remapped_from",
+ "peer.hostname",
+ "peer.ipv4",
+ "peer.ipv6",
+ "peer.port",
};
-
private static final long[] KEYOF_VALUES = {
- ERROR_ID,
- PARENT_ID,
- BASE_SERVICE_ID,
- VERSION_ID,
- ENV_ID,
- DJM_ENABLED_ID,
- DSM_ENABLED_ID,
- TRACER_HOST_ID,
- INTEGRATION_ID,
- SVC_SRC_ID,
- PEER_SERVICE_ID,
- PEER_SERVICE_REMAPPED_FROM_ID,
- HTTP_METHOD_ID,
- HTTP_ROUTE_ID,
- HTTP_URL_ID,
- PEER_HOSTNAME_ID,
- PEER_HOST_IPV4_ID,
- PEER_HOST_IPV6_ID,
- PEER_PORT_ID,
- COMPONENT_ID,
- SPAN_KIND_ID,
- LANGUAGE_ID,
- DB_TYPE_ID,
- DB_INSTANCE_ID,
- DB_USER_ID,
- DB_OPERATION_ID,
- DB_POOL_NAME_ID,
+ ERROR,
+ SERVICE,
+ RESOURCE_NAME,
+ SPAN_TYPE,
+ ORIGIN,
+ SAMPLING_PRIORITY,
+ MANUAL_KEEP,
+ MANUAL_DROP,
+ MEASURED,
+ ANALYTICS_SAMPLE_RATE,
+ DD_BASE_SERVICE,
+ VERSION,
+ ENV,
+ LANGUAGE,
+ RUNTIME_ID,
+ DD_TRACER_HOST,
+ DD_GIT_COMMIT_SHA,
+ DD_GIT_REPOSITORY_URL,
+ DD_PROFILING_ENABLED,
+ DD_DSM_ENABLED,
+ DD_APPSEC_ENABLED,
+ DD_DJM_ENABLED,
+ DD_CIVISIBILITY_ENABLED,
+ DD_PARENT_ID,
+ COMPONENT,
+ SPAN_KIND,
+ DD_INTEGRATION,
+ DD_SVC_SRC,
+ ERROR_TYPE,
+ ERROR_MESSAGE,
+ ERROR_STACK,
+ DB_TYPE,
+ DB_INSTANCE,
+ DB_OPERATION,
+ DB_USER,
+ DB_POOL_NAME,
+ DB_STATEMENT,
+ HTTP_METHOD,
+ HTTP_STATUS_CODE,
+ NETWORK_PROTOCOL_VERSION,
+ HTTP_URL,
+ HTTP_RESEND_COUNT,
+ HTTP_ROUTE,
+ HTTP_HOSTNAME,
+ HTTP_USERAGENT,
+ HTTP_QUERY_STRING,
+ SERVLET_PATH,
+ SERVLET_CONTEXT,
+ VIEW_NAME,
+ TEST_NAME,
+ TEST_SUITE,
+ TEST_STATUS,
+ TEST_FRAMEWORK,
+ PEER_SERVICE,
+ DD_PEER_SERVICE_SOURCE,
+ DD_PEER_SERVICE_REMAPPED_FROM,
+ PEER_HOSTNAME,
+ PEER_IPV4,
+ PEER_IPV6,
+ PEER_PORT,
};
-
- // Static-final raw arrays placed by StringIndex.EmbeddingSupport: the JIT folds these refs to
- // constants on
- // the keyOf hot path (the fastest of StringIndex's three usage modes — no instance dereference).
private static final int[] KEYOF_HASHES;
private static final String[] KEYOF_KEYS;
private static final long[] KEYOF_IDS;
@@ -242,59 +353,125 @@ public final class KnownTags {
public String nameOf(long tagId) {
switch (KnownTagCodec.globalSerial(tagId)) {
case ERROR_SERIAL:
- return Tags.ERROR;
- case PARENT_ID_SERIAL:
- return DDTags.PARENT_ID;
- case BASE_SERVICE_SERIAL:
- return DDTags.BASE_SERVICE;
+ return "error";
+ case SERVICE_SERIAL:
+ return "service";
+ case RESOURCE_NAME_SERIAL:
+ return "resource.name";
+ case SPAN_TYPE_SERIAL:
+ return "span.type";
+ case ORIGIN_SERIAL:
+ return "origin";
+ case SAMPLING_PRIORITY_SERIAL:
+ return "sampling.priority";
+ case MANUAL_KEEP_SERIAL:
+ return "manual.keep";
+ case MANUAL_DROP_SERIAL:
+ return "manual.drop";
+ case MEASURED_SERIAL:
+ return "measured";
+ case ANALYTICS_SAMPLE_RATE_SERIAL:
+ return "analytics.sample_rate";
+ case DD_BASE_SERVICE_SERIAL:
+ return "_dd.base_service";
case VERSION_SERIAL:
- return Tags.VERSION;
+ return "version";
case ENV_SERIAL:
- return ENV;
- case DJM_ENABLED_SERIAL:
- return DDTags.DJM_ENABLED;
- case DSM_ENABLED_SERIAL:
- return DDTags.DSM_ENABLED;
- case TRACER_HOST_SERIAL:
- return DDTags.TRACER_HOST;
- case INTEGRATION_SERIAL:
- return DDTags.DD_INTEGRATION;
- case SVC_SRC_SERIAL:
- return DDTags.DD_SVC_SRC;
- case PEER_SERVICE_SERIAL:
- return Tags.PEER_SERVICE;
- case PEER_SERVICE_REMAPPED_FROM_SERIAL:
- return DDTags.PEER_SERVICE_REMAPPED_FROM;
- case HTTP_METHOD_SERIAL:
- return Tags.HTTP_METHOD;
- case HTTP_ROUTE_SERIAL:
- return Tags.HTTP_ROUTE;
- case HTTP_URL_SERIAL:
- return Tags.HTTP_URL;
- case PEER_HOSTNAME_SERIAL:
- return Tags.PEER_HOSTNAME;
- case PEER_HOST_IPV4_SERIAL:
- return Tags.PEER_HOST_IPV4;
- case PEER_HOST_IPV6_SERIAL:
- return Tags.PEER_HOST_IPV6;
- case PEER_PORT_SERIAL:
- return Tags.PEER_PORT;
+ return "env";
+ case LANGUAGE_SERIAL:
+ return "language";
+ case RUNTIME_ID_SERIAL:
+ return "runtime-id";
+ case DD_TRACER_HOST_SERIAL:
+ return "_dd.tracer_host";
+ case DD_GIT_COMMIT_SHA_SERIAL:
+ return "_dd.git.commit.sha";
+ case DD_GIT_REPOSITORY_URL_SERIAL:
+ return "_dd.git.repository_url";
+ case DD_PROFILING_ENABLED_SERIAL:
+ return "_dd.profiling.enabled";
+ case DD_DSM_ENABLED_SERIAL:
+ return "_dd.dsm.enabled";
+ case DD_APPSEC_ENABLED_SERIAL:
+ return "_dd.appsec.enabled";
+ case DD_DJM_ENABLED_SERIAL:
+ return "_dd.djm.enabled";
+ case DD_CIVISIBILITY_ENABLED_SERIAL:
+ return "_dd.civisibility.enabled";
+ case DD_PARENT_ID_SERIAL:
+ return "_dd.parent_id";
case COMPONENT_SERIAL:
- return Tags.COMPONENT;
+ return "component";
case SPAN_KIND_SERIAL:
- return Tags.SPAN_KIND;
- case LANGUAGE_SERIAL:
- return DDTags.LANGUAGE_TAG_KEY;
+ return "span.kind";
+ case DD_INTEGRATION_SERIAL:
+ return "_dd.integration";
+ case DD_SVC_SRC_SERIAL:
+ return "_dd.svc_src";
+ case ERROR_TYPE_SERIAL:
+ return "error.type";
+ case ERROR_MESSAGE_SERIAL:
+ return "error.message";
+ case ERROR_STACK_SERIAL:
+ return "error.stack";
case DB_TYPE_SERIAL:
- return Tags.DB_TYPE;
+ return "db.type";
case DB_INSTANCE_SERIAL:
- return Tags.DB_INSTANCE;
- case DB_USER_SERIAL:
- return Tags.DB_USER;
+ return "db.instance";
case DB_OPERATION_SERIAL:
- return Tags.DB_OPERATION;
+ return "db.operation";
+ case DB_USER_SERIAL:
+ return "db.user";
case DB_POOL_NAME_SERIAL:
- return Tags.DB_POOL_NAME;
+ return "db.pool.name";
+ case DB_STATEMENT_SERIAL:
+ return "db.statement";
+ case HTTP_METHOD_SERIAL:
+ return "http.method";
+ case HTTP_STATUS_CODE_SERIAL:
+ return "http.status_code";
+ case NETWORK_PROTOCOL_VERSION_SERIAL:
+ return "network.protocol.version";
+ case HTTP_URL_SERIAL:
+ return "http.url";
+ case HTTP_RESEND_COUNT_SERIAL:
+ return "http.resend_count";
+ case HTTP_ROUTE_SERIAL:
+ return "http.route";
+ case HTTP_HOSTNAME_SERIAL:
+ return "http.hostname";
+ case HTTP_USERAGENT_SERIAL:
+ return "http.useragent";
+ case HTTP_QUERY_STRING_SERIAL:
+ return "http.query.string";
+ case SERVLET_PATH_SERIAL:
+ return "servlet.path";
+ case SERVLET_CONTEXT_SERIAL:
+ return "servlet.context";
+ case VIEW_NAME_SERIAL:
+ return "view.name";
+ case TEST_NAME_SERIAL:
+ return "test.name";
+ case TEST_SUITE_SERIAL:
+ return "test.suite";
+ case TEST_STATUS_SERIAL:
+ return "test.status";
+ case TEST_FRAMEWORK_SERIAL:
+ return "test.framework";
+ case PEER_SERVICE_SERIAL:
+ return "peer.service";
+ case DD_PEER_SERVICE_SOURCE_SERIAL:
+ return "_dd.peer.service.source";
+ case DD_PEER_SERVICE_REMAPPED_FROM_SERIAL:
+ return "_dd.peer.service.remapped_from";
+ case PEER_HOSTNAME_SERIAL:
+ return "peer.hostname";
+ case PEER_IPV4_SERIAL:
+ return "peer.ipv4";
+ case PEER_IPV6_SERIAL:
+ return "peer.ipv6";
+ case PEER_PORT_SERIAL:
+ return "peer.port";
default:
return null;
}
@@ -316,12 +493,7 @@ public long keyOf(String name) {
KnownTagCodec.register(RESOLVER);
}
- /**
- * Forces resolver registration. Merely invoking this static method runs {@code } (which
- * registers {@link #RESOLVER}), so calling it once at tracer init flips the dense store live;
- * idempotent. Until something references this class the registry stays dormant and {@code keyOf}
- * returns 0, so tag storage is byte-identical to the bucket-only behavior.
- */
+ /** Forces resolver registration by triggering . Idempotent. */
public static void init() {}
private KnownTags() {}
diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java
index 7c30f373881..28732f2fc00 100644
--- a/internal-api/src/main/java/datadog/trace/api/TagMap.java
+++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java
@@ -1052,6 +1052,37 @@ public EntryChange next() {
private Object[] knownValues;
private int knownCount;
+ /**
+ * Two-tier presence filter over the dense store — the fast path that lets a definitely-absent
+ * known tag append in O(1) instead of paying the {@link #knownIndexOf} scan (the common per-build
+ * insert). Each tag's id carries a declaration coordinate {@code (group-decl, field-decl)} (see
+ * {@link KnownTagCodec}); a tag is present ONLY IF its group-decl bit is set in {@link
+ * #knownGroupMask} AND its field-decl bit is set in {@link #knownFieldMask}. Either bit clear ⟹
+ * definitely absent ⟹ skip the scan.
+ *
+ *
+ * - Tier 1 — group mask: one bit per declaration group ({@code 1L << group-decl}). A
+ * clear group bit proves EVERY tag of that group is absent, so seeding a fresh group (e.g.
+ * a {@code SpanPrototype} bulk insert) skips the scan for the whole group — the bulk-insert
+ * payoff. Disjoint groups across two maps ({@code (a.knownGroupMask & b.knownGroupMask) ==
+ * 0}) prove nothing shadows across them, which the read-through shadow check exploits (see
+ * {@link #parentDenseHidden}).
+ *
- Tier 2 — field bloom: when the group bit clashes, fall back to one shared word
+ * over {@code field-decl & 63}. A clear field bit still proves absence; a clash falls
+ * through to the authoritative scan.
+ *
+ *
+ * Superset semantics: bits are set on every add and NEVER cleared on remove (a stale bit only
+ * costs a scan, never a wrong answer), so correctness never depends on the coordinate→bit
+ * collision rate — only the fast-path hit rate does. With a single declaration group (today), the
+ * group bit is always set once anything is stored, so this degrades gracefully to exactly the
+ * tier-2 field bloom. A collision-minimizing coordinate assignment (the tag registry) later only
+ * raises the hit rate.
+ */
+ private long knownGroupMask;
+
+ private long knownFieldMask;
+
private static final int KNOWN_INIT_CAP =
12; // generous per-type max stopgap; exact per-type sizing comes with the tag registry
@@ -1392,27 +1423,64 @@ private void ensureKnownCapacity() {
}
}
+ /**
+ * Tier-1 presence bit for {@code tagId}: {@code 1L << group-decl} (group-decl is 6 bits ≤ 63).
+ */
+ private static long knownGroupBit(long tagId) {
+ return 1L << KnownTagCodec.groupDecl(tagId);
+ }
+
+ /**
+ * Tier-2 presence bit for {@code tagId}: {@code field-decl} folded into one shared word ({@code
+ * field-decl & 63}). Crude to start; a collision-minimizing coordinate assignment (the tag
+ * registry) later only raises the hit rate — the scan stays authoritative.
+ */
+ private static long knownFieldBit(long tagId) {
+ return 1L << (KnownTagCodec.fieldDecl(tagId) & 63);
+ }
+
+ /**
+ * Whether {@code tagId} MAY be present in the dense store (both tiers say maybe), vs DEFINITELY
+ * absent (either tier's bit is clear). Group bit checked first so a fresh group short-circuits
+ * before touching the field bloom.
+ */
+ private boolean knownMaybePresent(long tagId) {
+ return (this.knownGroupMask & knownGroupBit(tagId)) != 0
+ && (this.knownFieldMask & knownFieldBit(tagId)) != 0;
+ }
+
/**
* Stores a known tag's value densely (no {@link Entry} alloc). Overwrites in place when present
* (returning the prior value materialized as an Entry, per the {@code Map} contract — usually
- * discarded by {@code set}); otherwise appends, growing x2 as needed.
+ * discarded by {@code set}); otherwise appends, growing x2 as needed. The two-tier presence
+ * filter skips the {@link #knownIndexOf} scan when the tag is definitely absent (the common
+ * per-build case), so an append is O(1) instead of O(n).
*/
private Entry putKnownValue(long tagId, Object value) {
- int i = this.knownIndexOf(tagId);
- if (i >= 0) {
- Object prior = this.knownValues[i];
- this.knownValues[i] = value;
- return materializeKnown(tagId, prior);
+ long groupBit = knownGroupBit(tagId);
+ long fieldBit = knownFieldBit(tagId);
+ // maybe present only if BOTH tiers say so; either bit clear ⟹ definitely absent ⟹ append
+ if ((this.knownGroupMask & groupBit) != 0 && (this.knownFieldMask & fieldBit) != 0) {
+ int i = this.knownIndexOf(tagId);
+ if (i >= 0) {
+ Object prior = this.knownValues[i];
+ this.knownValues[i] = value;
+ return materializeKnown(tagId, prior);
+ }
+ // filter false positive (coordinate collision) -> fall through to append
}
this.ensureKnownCapacity();
int slot = this.knownCount++;
this.knownIds[slot] = tagId;
this.knownValues[slot] = value;
+ this.knownGroupMask |= groupBit;
+ this.knownFieldMask |= fieldBit;
return null;
}
/** Raw dense value for {@code tagId}, or {@code null} when absent (no Entry, no boxing). */
private Object knownRawValue(long tagId) {
+ if (!this.knownMaybePresent(tagId)) return null; // definitely absent, no scan
int i = this.knownIndexOf(tagId);
return i < 0 ? null : this.knownValues[i];
}
@@ -1421,6 +1489,7 @@ private Object knownRawValue(long tagId) {
* Removes a known tag from the dense store (swap-with-last), returning the prior Entry or null.
*/
private Entry removeKnown(long tagId) {
+ if (!this.knownMaybePresent(tagId)) return null; // definitely absent
int i = this.knownIndexOf(tagId);
if (i < 0) return null;
Object prior = this.knownValues[i];
@@ -1429,6 +1498,8 @@ private Entry removeKnown(long tagId) {
this.knownValues[i] = this.knownValues[last];
this.knownIds[last] = 0L;
this.knownValues[last] = null;
+ // knownGroupMask/knownFieldMask intentionally NOT cleared: a stale-set bit only costs a scan;
+ // clearing could drop a bit still shared (via collision) by a present id -> false negative.
return materializeKnown(tagId, prior);
}
@@ -1447,7 +1518,9 @@ private static Entry materializeKnown(long tagId, Object value) {
private boolean parentDenseVisible(long tagId, TagMap fromAncestor) {
String tag = null; // resolved lazily, only if a nearer level carries tombstones
for (TagMap nearer = this; nearer != fromAncestor; nearer = nearer.parent) {
- if (nearer.knownIndexOf(tagId) >= 0) return false; // shadowed by a nearer dense entry
+ // shadowed by a nearer dense entry — the two-tier filter prunes the scan when definitely
+ // absent, so a nearer level with disjoint groups never pays a scan here (read-through win)
+ if (nearer.knownMaybePresent(tagId) && nearer.knownIndexOf(tagId) >= 0) return false;
if (nearer.removedFromParent != null) {
if (tag == null) tag = KnownTagCodec.nameOf(tagId);
if (nearer.removedFromParent.contains(tag)) return false; // tombstoned by a nearer level
@@ -1907,6 +1980,8 @@ private void putAllIntoEmptyMap(TagMap that) {
this.knownIds = Arrays.copyOf(that.knownIds, that.knownIds.length);
this.knownValues = Arrays.copyOf(that.knownValues, that.knownValues.length);
this.knownCount = that.knownCount;
+ this.knownGroupMask = that.knownGroupMask;
+ this.knownFieldMask = that.knownFieldMask;
}
}
@@ -2294,6 +2369,8 @@ public void clear() {
this.knownIds = null;
this.knownValues = null;
this.knownCount = 0;
+ this.knownGroupMask = 0L;
+ this.knownFieldMask = 0L;
}
public TagMap freeze() {
diff --git a/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java b/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java
index 11d01bc7ec8..aaffecf8e8c 100644
--- a/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java
+++ b/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java
@@ -11,49 +11,51 @@
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/**
- * Parity test for the keyOf substrate (slice 1): the {@link KnownTags} registry + the {@link
- * KnownTagCodec.Resolver} it registers. Verifies name ↔ id resolution without any dense store
- * — {@code keyOf}/{@code nameOf} depend only on globalSerial + name, not on the (dormant)
- * positional layout.
+ * Parity test for the keyOf substrate: the generated {@link KnownTags} registry + the {@link
+ * KnownTagCodec.Resolver} it registers. Verifies name ↔ id resolution and the intercepted /
+ * reserved / stored partitioning. {@code keyOf}/{@code nameOf} depend only on globalSerial + name,
+ * not on the (dormant) positional layout, so this is independent of the group/field-decl
+ * coordinates the tag registry assigns.
*/
class KnownTagsTest {
- /** (name, id) pairs — the full registry. keyOf returns the id verbatim (incl. INTERCEPTED). */
+ /** (name, id) pairs across the groups — keyOf returns the id verbatim (incl. INTERCEPTED). */
static Stream knownTags() {
return Stream.of(
- Arguments.of(Tags.ERROR, KnownTags.ERROR_ID),
- Arguments.of(DDTags.PARENT_ID, KnownTags.PARENT_ID),
- Arguments.of(DDTags.BASE_SERVICE, KnownTags.BASE_SERVICE_ID),
- Arguments.of(Tags.VERSION, KnownTags.VERSION_ID),
- Arguments.of(KnownTags.ENV, KnownTags.ENV_ID),
- Arguments.of(DDTags.DJM_ENABLED, KnownTags.DJM_ENABLED_ID),
- Arguments.of(DDTags.DSM_ENABLED, KnownTags.DSM_ENABLED_ID),
- Arguments.of(DDTags.TRACER_HOST, KnownTags.TRACER_HOST_ID),
- Arguments.of(DDTags.DD_INTEGRATION, KnownTags.INTEGRATION_ID),
- Arguments.of(DDTags.DD_SVC_SRC, KnownTags.SVC_SRC_ID),
- Arguments.of(Tags.PEER_SERVICE, KnownTags.PEER_SERVICE_ID),
- Arguments.of(DDTags.PEER_SERVICE_REMAPPED_FROM, KnownTags.PEER_SERVICE_REMAPPED_FROM_ID),
- Arguments.of(Tags.HTTP_METHOD, KnownTags.HTTP_METHOD_ID),
- Arguments.of(Tags.HTTP_ROUTE, KnownTags.HTTP_ROUTE_ID),
- Arguments.of(Tags.HTTP_URL, KnownTags.HTTP_URL_ID),
- Arguments.of(Tags.PEER_HOSTNAME, KnownTags.PEER_HOSTNAME_ID),
- Arguments.of(Tags.PEER_HOST_IPV4, KnownTags.PEER_HOST_IPV4_ID),
- Arguments.of(Tags.PEER_HOST_IPV6, KnownTags.PEER_HOST_IPV6_ID),
- Arguments.of(Tags.PEER_PORT, KnownTags.PEER_PORT_ID),
- Arguments.of(Tags.COMPONENT, KnownTags.COMPONENT_ID),
- Arguments.of(Tags.SPAN_KIND, KnownTags.SPAN_KIND_ID),
- Arguments.of(DDTags.LANGUAGE_TAG_KEY, KnownTags.LANGUAGE_ID),
- Arguments.of(Tags.DB_TYPE, KnownTags.DB_TYPE_ID),
- Arguments.of(Tags.DB_INSTANCE, KnownTags.DB_INSTANCE_ID),
- Arguments.of(Tags.DB_USER, KnownTags.DB_USER_ID),
- Arguments.of(Tags.DB_OPERATION, KnownTags.DB_OPERATION_ID),
- Arguments.of(Tags.DB_POOL_NAME, KnownTags.DB_POOL_NAME_ID));
+ Arguments.of(Tags.ERROR, KnownTags.ERROR),
+ Arguments.of(DDTags.PARENT_ID, KnownTags.DD_PARENT_ID),
+ Arguments.of(DDTags.BASE_SERVICE, KnownTags.DD_BASE_SERVICE),
+ Arguments.of(Tags.VERSION, KnownTags.VERSION),
+ Arguments.of("env", KnownTags.ENV),
+ Arguments.of(DDTags.DJM_ENABLED, KnownTags.DD_DJM_ENABLED),
+ Arguments.of(DDTags.DSM_ENABLED, KnownTags.DD_DSM_ENABLED),
+ Arguments.of(DDTags.TRACER_HOST, KnownTags.DD_TRACER_HOST),
+ Arguments.of(DDTags.DD_INTEGRATION, KnownTags.DD_INTEGRATION),
+ Arguments.of(DDTags.DD_SVC_SRC, KnownTags.DD_SVC_SRC),
+ Arguments.of(Tags.PEER_SERVICE, KnownTags.PEER_SERVICE),
+ Arguments.of(DDTags.PEER_SERVICE_REMAPPED_FROM, KnownTags.DD_PEER_SERVICE_REMAPPED_FROM),
+ Arguments.of(Tags.HTTP_METHOD, KnownTags.HTTP_METHOD),
+ Arguments.of(Tags.HTTP_ROUTE, KnownTags.HTTP_ROUTE),
+ Arguments.of(Tags.HTTP_URL, KnownTags.HTTP_URL),
+ Arguments.of(Tags.PEER_HOSTNAME, KnownTags.PEER_HOSTNAME),
+ Arguments.of(Tags.PEER_HOST_IPV4, KnownTags.PEER_IPV4),
+ Arguments.of(Tags.PEER_HOST_IPV6, KnownTags.PEER_IPV6),
+ Arguments.of(Tags.PEER_PORT, KnownTags.PEER_PORT),
+ Arguments.of(Tags.COMPONENT, KnownTags.COMPONENT),
+ Arguments.of(Tags.SPAN_KIND, KnownTags.SPAN_KIND),
+ Arguments.of(DDTags.LANGUAGE_TAG_KEY, KnownTags.LANGUAGE),
+ Arguments.of(Tags.DB_TYPE, KnownTags.DB_TYPE),
+ Arguments.of(Tags.DB_INSTANCE, KnownTags.DB_INSTANCE),
+ Arguments.of(Tags.DB_USER, KnownTags.DB_USER),
+ Arguments.of(Tags.DB_OPERATION, KnownTags.DB_OPERATION),
+ Arguments.of(Tags.DB_POOL_NAME, KnownTags.DB_POOL_NAME));
}
/**
@@ -61,17 +63,22 @@ static Stream knownTags() {
*/
static Stream interceptedTags() {
return Stream.of(
- Arguments.of(KnownTags.ERROR_ID),
- Arguments.of(KnownTags.PEER_SERVICE_ID),
- Arguments.of(KnownTags.HTTP_METHOD_ID),
- Arguments.of(KnownTags.HTTP_URL_ID),
- Arguments.of(KnownTags.SPAN_KIND_ID));
+ Arguments.of(KnownTags.ERROR),
+ Arguments.of(KnownTags.PEER_SERVICE),
+ Arguments.of(KnownTags.HTTP_METHOD),
+ Arguments.of(KnownTags.HTTP_URL),
+ Arguments.of(KnownTags.SPAN_KIND));
+ }
+
+ @BeforeAll
+ static void registerResolver() {
+ // Generated ids are compile-time constants (literal), so a constant reference is inlined and
+ // never triggers KnownTags.. init() forces class-load -> KnownTagCodec.register.
+ KnownTags.init();
}
@Test
- void resolverIsActiveOnceReferenced() {
- // referencing any constant triggers KnownTags. -> KnownTagCodec.register
- assertTrue(KnownTags.ERROR_ID != 0L);
+ void resolverIsActiveAfterInit() {
assertTrue(KnownTagCodec.isActive());
assertEquals(KnownTags.SLOT_COUNT, KnownTagCodec.slotCount());
}
@@ -88,13 +95,6 @@ void nameOfResolvesIdToName(String name, long id) {
assertEquals(name, KnownTagCodec.nameOf(id), "nameOf(" + name + ")");
}
- @ParameterizedTest
- @MethodSource("knownTags")
- void nameHashMatchesEntryHash(String name, long id) {
- assertEquals(
- (int) TagMap.Entry._hash(name), KnownTagCodec.nameHash(id), "nameHash(" + name + ")");
- }
-
@ParameterizedTest
@MethodSource("interceptedTags")
void interceptedTagsCarryFlag(long id) {
@@ -125,18 +125,18 @@ void unknownNamesResolveToZero() {
@Test
void unknownIdsResolveToNullName() {
assertNull(KnownTagCodec.nameOf(0L));
- assertNull(KnownTagCodec.nameOf(KnownTagCodec.tagId(9999, "made.up")));
+ assertNull(KnownTagCodec.nameOf(KnownTagCodec.tagId(9999))); // serial with no assigned tag
}
@Test
void errorIsReservedTheRestAreStored() {
- assertTrue(KnownTagCodec.isReserved(KnownTags.ERROR_ID), "ERROR reserved");
- assertFalse(KnownTagCodec.isStored(KnownTags.ERROR_ID), "ERROR not stored");
+ assertTrue(KnownTagCodec.isReserved(KnownTags.ERROR), "ERROR reserved");
+ assertFalse(KnownTagCodec.isStored(KnownTags.ERROR), "ERROR not stored");
knownTags()
.forEach(
a -> {
long id = (Long) a.get()[1];
- if (id != KnownTags.ERROR_ID) {
+ if (id != KnownTags.ERROR) {
assertTrue(KnownTagCodec.isStored(id), "stored: " + a.get()[0]);
assertFalse(KnownTagCodec.isReserved(id), "not reserved: " + a.get()[0]);
}
diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapDenseForkedTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapDenseForkedTest.java
index a82742c3acd..70e41c47798 100644
--- a/internal-api/src/test/java/datadog/trace/api/TagMapDenseForkedTest.java
+++ b/internal-api/src/test/java/datadog/trace/api/TagMapDenseForkedTest.java
@@ -35,8 +35,9 @@ class TagMapDenseForkedTest {
@BeforeAll
static void registerResolver() {
- // referencing any KnownTags constant triggers its -> KnownTagCodec.register
- assertTrue(KnownTags.BASE_SERVICE_ID != 0L);
+ // Generated ids are compile-time constants (literal), so a constant reference is inlined and
+ // never triggers KnownTags.. init() forces class-load -> KnownTagCodec.register.
+ KnownTags.init();
assertTrue(KnownTagCodec.isActive(), "resolver must be live for the dense store to engage");
assertTrue(
KnownTagCodec.isStored(KnownTagCodec.keyOf(BASE_SERVICE)), "base_service routes dense");
diff --git a/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java b/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java
index a29795c542a..72e88ccd627 100644
--- a/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java
+++ b/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java
@@ -58,7 +58,7 @@ enum Regime {
public long keyOf(String name) {
if (name.startsWith("known-")) {
int n = Integer.parseInt(name.substring("known-".length()));
- return KnownTagCodec.tagId(KnownTagCodec.FIRST_STORED_SERIAL + n, name);
+ return KnownTagCodec.tagId(KnownTagCodec.FIRST_STORED_SERIAL + n, n);
}
return 0L;
}