diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 991ca899596..b7eacb3cd95 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -59,6 +59,11 @@ gradlePlugin { implementationClass = "datadog.gradle.plugin.config.SupportedConfigPlugin" } + create("tag-registry-generator") { + id = "dd-trace-java.tag-registry-generator" + implementationClass = "datadog.gradle.plugin.tags.TagRegistryGeneratorPlugin" + } + create("supported-config-linter") { id = "dd-trace-java.config-inversion-linter" implementationClass = "datadog.gradle.plugin.config.ConfigInversionLinter" @@ -107,6 +112,7 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind") implementation("com.fasterxml.jackson.core:jackson-annotations") implementation("com.fasterxml.jackson.core:jackson-core") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") compileOnly(libs.develocity) } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/GenerateKnownTagsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/GenerateKnownTagsTask.kt new file mode 100644 index 00000000000..ab1c2631c60 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/GenerateKnownTagsTask.kt @@ -0,0 +1,38 @@ +package datadog.gradle.plugin.tags + +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +/** + * Generates the committed tag registry (KnownTags.java + layout reports) from the language-agnostic + * {@code tag-conventions.yaml} + the Java overlay. The actual emit lives in [TagRegistryGenerator]; + * this task just wires the inputs/outputs so Gradle can cache and up-to-date-check it. + */ +@CacheableTask +abstract class GenerateKnownTagsTask @Inject constructor(objects: ObjectFactory) : DefaultTask() { + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + val domainYaml: RegularFileProperty = objects.fileProperty() + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + val overlayYaml: RegularFileProperty = objects.fileProperty() + + @get:OutputDirectory val destinationDirectory: DirectoryProperty = objects.directoryProperty() + + @TaskAction + fun generate() { + val outDir = destinationDirectory.get().asFile + TagRegistryGenerator.generate(domainYaml.get().asFile, overlayYaml.get().asFile, outDir) + logger.lifecycle("tag-registry: generated -> $outDir") + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/KnownTagsEmitter.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/KnownTagsEmitter.kt new file mode 100644 index 00000000000..5e3b1925ac3 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/KnownTagsEmitter.kt @@ -0,0 +1,142 @@ +package datadog.gradle.plugin.tags + +/** + * Emits the generated `KnownTags.java` from a [TagRegistry]. Public API first — per-tag + * `_NAME` (string) + `_ID` (encoded long, literal) couplets with a trailing `// makeTagId(...)` + * derivation comment — then the package-private `_SERIAL_NUM` constants, the + * `StringIndex.EmbeddingSupport` keyOf table, the `serialNum` switch `nameOf`, and resolver + * registration. + */ +object KnownTagsEmitter { + + fun emit(reg: TagRegistry, pkg: String, className: String): String { + // Sanitize tag names into unique Java constant identifiers. + val used = HashSet() + val cname = HashMap() + fun mk(name: String): String { + var c = name.uppercase().replace(Regex("[^A-Za-z0-9]"), "_").replace(Regex("_+"), "_").trim('_') + if (c.isEmpty() || c[0].isDigit()) c = "T_$c" + var u = c + var n = 2 + while (u in used) { + u = "${c}_$n"; n++ + } + used.add(u) + cname[name] = u + return u + } + reg.reserved.forEach { mk(it.name) } + reg.stored.forEach { mk(it.name) } + + // Constant names. Collapse a duplicated trailing token so e.g. "resource.name" yields NAME + // (not NAME_NAME) and "_dd.parent_id" yields ID (not ID_ID); the non-duplicating pairs + // (ID + _NAME -> ID_NAME, NAME + _ID -> NAME_ID) are kept as-is. + fun withSuffix(base: String, suffix: String) = if (base.endsWith(suffix)) base else "$base$suffix" + fun nameC(name: String) = withSuffix(cname[name]!!, "_NAME") + fun idC(name: String) = withSuffix(cname[name]!!, "_ID") + fun serialC(name: String) = withSuffix(cname[name]!!, "_SERIAL_NUM") + + val order = reg.reserved.map { it.name } + reg.stored.map { it.name } // stable emit order + val b = StringBuilder() + b.appendLine("package $pkg;") + b.appendLine() + b.appendLine("import datadog.trace.util.StringIndex;") + b.appendLine() + b.appendLine("// GENERATED by the tag-registry code generator (dd-trace-java.tag-registry-generator).") + b.appendLine("// DO NOT EDIT. Source: tag-conventions.yaml + tag-conventions.java.yaml.") + b.appendLine("public final class $className {") + b.appendLine(" static final int SLOT_COUNT = ${reg.slotCount};") + b.appendLine() + + // Public API first (name + encoded id couplets), so readers see the useful parts up top; the + // serial ids and keyOf/resolver machinery follow below. Derivation is in the trailing comment. + b.appendLine(" // ---- reserved (routed to span fields or directives; not stored) ----") + for (v in reg.reserved) { + b.appendLine(" public static final String ${nameC(v.name)} = \"${v.name}\";") + b.appendLine(" public static final long ${idC(v.name)} = ${hex(v.id)};") + b.appendLine(" // makeTagId(serial=${v.serial}, group=0, field=NO_SLOT) + intercepted [${v.kind}${v.field?.let { " -> $it" } ?: ""}]") + b.appendLine() + } + + b.appendLine(" // ---- stored (dense field-decl, or bucketed when field=NO_SLOT) ----") + for (t in reg.stored) { + val field = if (t.slotted) t.fieldDecl.toString() else "NO_SLOT" + b.appendLine(" public static final String ${nameC(t.name)} = \"${t.name}\";") + b.appendLine(" public static final long ${idC(t.name)} = ${hex(t.id)};") + b.appendLine(" // makeTagId(serial=${t.serial}, group=${t.groupDecl}, field=$field)${if (t.intercepted) " + intercepted" else ""} <${t.required}${if (t.traceLevel) ", trace-level" else ""}>") + b.appendLine() + } + + // Serial numbers (globalSerial per tag) — package-private, consumed by the resolver switch. + b.appendLine(" // ---- serial numbers ----") + for (v in reg.reserved) { + b.appendLine(" static final int ${serialC(v.name)} = ${v.serial};") + } + for (t in reg.stored) { + b.appendLine(" static final int ${serialC(t.name)} = ${t.serial};") + } + b.appendLine() + + // keyOf table (open-addressed, via StringIndex.EmbeddingSupport). + b.appendLine(" private static final String[] KEYOF_NAMES = {") + order.forEach { b.appendLine(" ${nameC(it)},") } + b.appendLine(" };") + b.appendLine(" private static final long[] KEYOF_VALUES = {") + order.forEach { b.appendLine(" ${idC(it)},") } + b.appendLine(" };") + b.appendLine(" private static final int[] KEYOF_HASHES;") + b.appendLine(" private static final String[] KEYOF_KEYS;") + b.appendLine(" private static final long[] KEYOF_IDS;") + b.appendLine(" static {") + b.appendLine(" StringIndex.Data data = StringIndex.EmbeddingSupport.create(KEYOF_NAMES);") + b.appendLine(" long[] ids = new long[data.names.length];") + b.appendLine(" for (int j = 0; j < KEYOF_NAMES.length; j++) {") + b.appendLine(" ids[StringIndex.EmbeddingSupport.indexOf(data.hashes, data.names, KEYOF_NAMES[j])] = KEYOF_VALUES[j];") + b.appendLine(" }") + b.appendLine(" KEYOF_HASHES = data.hashes;") + b.appendLine(" KEYOF_KEYS = data.names;") + b.appendLine(" KEYOF_IDS = ids;") + b.appendLine(" }") + b.appendLine() + + // Resolver. + b.appendLine(" static final KnownTagCodec.Resolver RESOLVER =") + b.appendLine(" new KnownTagCodec.Resolver() {") + b.appendLine(" @Override") + b.appendLine(" public String nameOf(long tagId) {") + b.appendLine(" switch (KnownTagCodec.serialNum(tagId)) {") + for (name in order) { + b.appendLine(" case ${serialC(name)}:") + b.appendLine(" return ${nameC(name)};") + } + b.appendLine(" default:") + b.appendLine(" return null;") + b.appendLine(" }") + b.appendLine(" }") + b.appendLine() + b.appendLine(" @Override") + b.appendLine(" public int slotCount() {") + b.appendLine(" return SLOT_COUNT;") + b.appendLine(" }") + b.appendLine() + b.appendLine(" @Override") + b.appendLine(" public long keyOf(String name) {") + b.appendLine(" int slot = StringIndex.EmbeddingSupport.indexOf(KEYOF_HASHES, KEYOF_KEYS, name);") + b.appendLine(" return slot < 0 ? 0L : KEYOF_IDS[slot];") + b.appendLine(" }") + b.appendLine(" };") + b.appendLine() + b.appendLine(" static {") + b.appendLine(" KnownTagCodec.register(RESOLVER);") + b.appendLine(" }") + b.appendLine() + b.appendLine(" /** Forces resolver registration by triggering . Idempotent. */") + b.appendLine(" public static void init() {}") + b.appendLine() + b.appendLine(" private $className() {}") + b.appendLine("}") + return b.toString() + } + + private fun hex(id: Long): String = "0x%016XL".format(id) +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagConventions.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagConventions.kt new file mode 100644 index 00000000000..a01da7bc75c --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagConventions.kt @@ -0,0 +1,173 @@ +package datadog.gradle.plugin.tags + +/** + * Parsed tag-conventions domain model + the per-type tag-set resolver. Language-agnostic: it knows + * only structure (extends / include / applies) and per-tag semantics (name / type / required / + * source). Id assignment and emission are layered on top of the resolved sets. + */ +class TagConventions +private constructor( + private val spanTypes: Map, + private val mixins: Map, + private val traceLevel: List, +) { + /** A tag declaration (domain semantics only). */ + data class Tag( + val name: String, + val type: String, + val required: String, + ) + + data class SpanType( + val name: String, + val abstract: Boolean, + val extends: String?, + val include: List, + val tags: List, + ) + + data class Mixin( + val name: String, + val appliesAll: Boolean, + val appliesTo: Set, + val tags: List, + ) + + /** Concrete (instantiable) span types — the ones a layout is computed for. */ + fun concreteTypes(): List = + spanTypes.values.filter { !it.abstract }.map { it.name }.sorted() + + /** + * resolved(type) = own tags + tags up the `extends` chain (incl. base) + tags of every mixin the + * type or an ancestor `include`s + tags of every mixin whose `applies` matches. De-duped by tag + * name (first occurrence wins). Base-first order, so it is stable across runs. + */ + fun resolve(typeName: String): List { + val result = LinkedHashMap() + fun add(t: Tag) = result.putIfAbsent(t.name, t) + + val chain = ArrayList() + var cur: SpanType? = spanTypes[typeName] + while (cur != null) { + chain.add(cur) + cur = cur.extends?.let { spanTypes[it] } + } + for (st in chain.asReversed()) { + st.tags.forEach { add(it) } + for (mixinName in st.include) mixins[mixinName]?.tags?.forEach { add(it) } + } + val chainNames = chain.map { it.name }.toSet() + for (mx in mixins.values) { + if (mx.appliesAll || mx.appliesTo.any { it in chainNames }) mx.tags.forEach { add(it) } + } + return result.values.toList() + } + + /** The explicit trace-level tier tags (their own TagMap "type" on the TraceSegment). */ + fun traceLevelTags(): List = traceLevel + + /** A declaration group: the source that *declares* a set of tags (its own `tags:` list). */ + data class Group(val name: String, val kind: String, val tags: List) + + /** + * The declaration groups, in a stable order: the trace-level tier first, then every span type + * (abstract included — `base`/`http` declare real tags) sorted by name, then every mixin sorted by + * name. Each maps to one `group-decl`. A tag is *declared* once (in its own container's `tags:`); + * the same tag reached via extends/include/applies is not re-declared, so first-declaration (in + * this order) is its home group. Groups with no declared tags are omitted. + */ + fun declarationGroups(): List { + val groups = ArrayList() + if (traceLevel.isNotEmpty()) groups.add(Group(TRACE_LAYER, "trace", traceLevel)) + for (name in spanTypes.keys.sorted()) { + val st = spanTypes.getValue(name) + if (st.tags.isNotEmpty()) groups.add(Group(name, "span_type", st.tags)) + } + for (name in mixins.keys.sorted()) { + val mx = mixins.getValue(name) + if (mx.tags.isNotEmpty()) groups.add(Group(name, "mixin", mx.tags)) + } + return groups + } + + /** Full stored-tag universe (concrete span types' resolves + trace-level), de-duped by name. */ + fun allStoredTags(): List { + val union = LinkedHashMap() + for (type in concreteTypes()) for (t in resolve(type)) union.putIfAbsent(t.name, t) + for (t in traceLevel) union.putIfAbsent(t.name, t) + return union.values.toList() + } + + /** + * Full composition for a type as (origin, tag) pairs, in composition order and NOT de-duped, so a + * tag contributed by more than one source shows up more than once. Origin is the contributing + * span type (via extends), `incl:` (via include), or `appl:` (via applies). + */ + fun compose(typeName: String): List> { + val out = ArrayList>() + val chain = ArrayList() + var cur: SpanType? = spanTypes[typeName] + while (cur != null) { + chain.add(cur) + cur = cur.extends?.let { spanTypes[it] } + } + for (st in chain.asReversed()) { + st.tags.forEach { out.add(st.name to it) } + for (mixinName in st.include) mixins[mixinName]?.tags?.forEach { out.add("incl:$mixinName" to it) } + } + val chainNames = chain.map { it.name }.toSet() + for (mx in mixins.values) { + if (mx.appliesAll || mx.appliesTo.any { it in chainNames }) { + mx.tags.forEach { out.add("appl:${mx.name}" to it) } + } + } + return out + } + + companion object { + /** Group name of the trace-level tier (its own TagMap layer on the TraceSegment). */ + const val TRACE_LAYER = "" + + @Suppress("UNCHECKED_CAST") + fun parse(root: Map): TagConventions { + val spanTypesRaw = (root["span_types"] as? Map) ?: emptyMap() + val spanTypes = + spanTypesRaw.mapValues { (name, v) -> + val m = v as Map + SpanType( + name = name, + abstract = (m["abstract"] as? Boolean) ?: false, + extends = m["extends"] as? String, + include = (m["include"] as? List) ?: emptyList(), + tags = tagList(m["tags"]), + ) + } + + val mixinsRaw = (root["mixins"] as? Map) ?: emptyMap() + val mixins = + mixinsRaw.mapValues { (name, v) -> + val m = v as Map + val applies = m["applies"] + Mixin( + name = name, + appliesAll = applies == "all", + appliesTo = if (applies is List<*>) applies.map { it.toString() }.toSet() else emptySet(), + tags = tagList(m["tags"]), + ) + } + + val traceLevel = tagList((root["trace_level"] as? Map)?.get("tags")) + return TagConventions(spanTypes, mixins, traceLevel) + } + + @Suppress("UNCHECKED_CAST") + private fun tagList(tags: Any?): List = + (tags as? List>)?.map { m -> + Tag( + name = m["tag"].toString(), + type = (m["type"] as? String) ?: "string", + required = (m["required"] as? String) ?: "optional", + ) + } ?: emptyList() + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistry.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistry.kt new file mode 100644 index 00000000000..cb09eeba5ba --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistry.kt @@ -0,0 +1,132 @@ +package datadog.gradle.plugin.tags + +/** + * Assigns tag ids from a parsed [TagConventions] plus the Java overlay (intercepted set + reserved + * registry). The id encoding mirrors KnownTagCodec: [63 intercepted][62-48 serial][47-42 + * group-decl][41-32 field-decl][31-0 zero] (known ids carry no nameHash — they are dense-store + * addressed). + * + *

Groups are the declaration sources: the trace-level tier, each span type, and each mixin each + * get their own `group-decl` (see [TagConventions.declarationGroups]). A tag's home group is where + * it is *declared* (first declaration wins across the group order); the same tag reached via + * extends/include/applies is not given a second id. Within each group, `field-decl` is a plain + * ordinal over that group's slotted tags — it restarts at 0 per group, which is exactly what makes + * the two-tier presence fast path pay off: the shared field-bit word would collide across groups, + * but the group-decl tier disambiguates. No graph coloring; correctness never depends on the + * coordinate→bit collision rate (the dense scan is authoritative). + * + *

Slotting (does a tag get a field-decl / dense slot) is derived from the domain `required` + * level: required/conditional/recommended tags are slotted (dense), the rest are NO_SLOT + * (bucketed) and carry only their home group-decl (cosmetic — they never touch the dense masks). + */ +class TagRegistry +private constructor( + val stored: List, + val reserved: List, + val slotCount: Int, +) { + data class StoredTag( + val name: String, + val type: String, + val required: String, + val serial: Int, + val intercepted: Boolean, + val groupDecl: Int, + val fieldDecl: Int, + val traceLevel: Boolean, + val id: Long, + ) { + val slotted: Boolean + get() = fieldDecl != NO_SLOT + } + + data class ReservedTag( + val name: String, + val kind: String, + val field: String?, + val serial: Int, + val id: Long, + ) + + /** Java overlay: intercepted tag names + the reserved/special-key registry. */ + class Overlay(val intercepted: Set, val reserved: List) { + data class ReservedDef(val name: String, val kind: String, val field: String?) + + companion object { + @Suppress("UNCHECKED_CAST") + fun parse(root: Map): Overlay { + val intercepted = (root["intercepted"] as? List)?.toSet() ?: emptySet() + val reserved = + (root["reserved"] as? List>)?.map { m -> + ReservedDef(m["tag"].toString(), (m["kind"] as? String) ?: "directive", m["field"] as? String) + } ?: emptyList() + return Overlay(intercepted, reserved) + } + } + } + + companion object { + const val FIRST_STORED_SERIAL = 256 + const val NO_SLOT = 0x3FF // field-decl all-ones sentinel (10 bits); mirrors KnownTagCodec.NO_SLOT + const val GROUP_DECL_MAX = 0x3F // group-decl is 6 bits [47-42]; mirrors KnownTagCodec.GROUP_DECL_MASK + const val TRACE_LAYER = "" + + // Domain `required` levels that get a dense field-decl (the rest are bucketed with NO_SLOT). + val SLOTTED = setOf("required", "conditional", "recommended") + + /** + * Mirrors KnownTagCodec.makeTagId(serial, groupDecl, fieldDecl) + intercepted() — must stay in sync + * with it. group-decl [47-42], field-decl [41-32], low 32 bits zero. + */ + fun encode(serial: Int, intercepted: Boolean, groupDecl: Int, fieldDecl: Int): Long { + val id = + (serial.toLong() shl 48) or + ((groupDecl.toLong() and 0x3F) shl 42) or + ((fieldDecl.toLong() and 0x3FF) shl 32) + return if (intercepted) id or Long.MIN_VALUE else id + } + + fun build(conv: TagConventions, overlay: Overlay): TagRegistry { + val traceNames = conv.traceLevelTags().map { it.name }.toSet() + + val reserved = + overlay.reserved.mapIndexed { i, v -> + val serial = 1 + i + ReservedTag( + v.name, v.kind, v.field, serial, + encode(serial, intercepted = true, groupDecl = 0, fieldDecl = NO_SLOT)) + } + + // Walk the declaration groups in order; each group gets the next group-decl. Within a group, + // field-decl restarts at 0 and counts only slotted tags (non-slotted -> NO_SLOT). A tag is + // assigned at its first declaration; the same name seen again in a later group is skipped. + val stored = ArrayList() + val assigned = HashSet() + var nextSerial = FIRST_STORED_SERIAL + var maxGroupSlots = 0 + conv.declarationGroups().forEachIndexed { groupDecl, group -> + require(groupDecl <= GROUP_DECL_MAX) { + "too many declaration groups (${groupDecl + 1}); group-decl is 6 bits (max ${GROUP_DECL_MAX + 1})" + } + var nextFieldDecl = 0 + for (t in group.tags) { + if (!assigned.add(t.name)) continue // first declaration wins + val serial = nextSerial++ + val intercepted = t.name in overlay.intercepted + val fieldDecl = if (t.required in SLOTTED) nextFieldDecl++ else NO_SLOT + stored.add( + StoredTag( + t.name, t.type, t.required, serial, intercepted, + groupDecl = groupDecl, fieldDecl = fieldDecl, + traceLevel = t.name in traceNames, + id = encode(serial, intercepted, groupDecl, fieldDecl))) + } + if (nextFieldDecl > maxGroupSlots) maxGroupSlots = nextFieldDecl + } + + // slotCount = (max stored field-decl) + 1. With per-group numbering that is the largest + // slotted-tag count in any single group. + return TagRegistry(stored, reserved, slotCount = maxGroupSlots) + } + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGenerator.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGenerator.kt new file mode 100644 index 00000000000..9fc6111dedb --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGenerator.kt @@ -0,0 +1,176 @@ +package datadog.gradle.plugin.tags + +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import java.io.File + +/** + * Turns the language-agnostic {@code tag-conventions.yaml} + the Java overlay into the generated tag + * registry: {@code KnownTags.java} (under {@code java/}) plus verification report dumps + * (resolved-tags / tag-assignment / layout-by-type / folded-types) at the destination root. + * + * Pure function of its inputs (deterministic ordering throughout), so the same inputs always produce + * byte-identical output -- which is what the {@code verifyKnownTags} freshness gate relies on. + */ +object TagRegistryGenerator { + /** Parses the two YAML files and writes the full generated tree under [outDir]. */ + fun generate(domainYaml: File, overlayYaml: File, outDir: File) { + val mapper = ObjectMapper(YAMLFactory()) + val domain: Map = + domainYaml.inputStream().use { + mapper.readValue(it, object : TypeReference>() {}) + } + val overlayMap: Map = + overlayYaml.inputStream().use { + mapper.readValue(it, object : TypeReference>() {}) + } + + outDir.mkdirs() + // KnownTags.java goes under java/ (added as a srcDir); the .txt reports sit at the root. + val javaPkg = File(outDir, "java/datadog/trace/api").apply { mkdirs() } + + val conv = TagConventions.parse(domain) + val overlay = TagRegistry.Overlay.parse(overlayMap) + val reg = TagRegistry.build(conv, overlay) + + File(outDir, "resolved-tags.txt").writeText(resolvedReport(conv)) + File(outDir, "tag-assignment.txt").writeText(assignmentReport(conv, reg)) + File(outDir, "layout-by-type.txt").writeText(layoutByTypeReport(conv, reg)) + File(outDir, "folded-types.txt").writeText(foldedTypesReport(conv, reg)) + File(javaPkg, "KnownTags.java") + .writeText(KnownTagsEmitter.emit(reg, "datadog.trace.api", "KnownTags")) + } + + /** resolved-tags.txt — the per-type resolved sets (composition check). */ + private fun resolvedReport(conv: TagConventions): String { + val resolved = StringBuilder() + resolved.appendLine("# Resolved per-type tag sets (concrete span types).") + for (type in conv.concreteTypes()) { + val tags = conv.resolve(type) + resolved.appendLine() + resolved.appendLine("$type (${tags.size} tags):") + for (t in tags) resolved.appendLine(" - ${t.name}") + } + return resolved.toString() + } + + /** tag-assignment.txt — serials, group/field-decls, ids, per-type field-decl sets (assignment check). */ + private fun assignmentReport(conv: TagConventions, reg: TagRegistry): String { + val byName = reg.stored.associateBy { it.name } + val a = StringBuilder() + a.appendLine( + "# Tag id assignment. slotCount=${reg.slotCount} stored=${reg.stored.size} reserved=${reg.reserved.size}") + a.appendLine() + a.appendLine("# STORED serial group field int id required name") + for (t in reg.stored) { + a.appendLine( + " %6d %5d %5s %s %-18s %-12s %s".format( + t.serial, + t.groupDecl, + if (t.slotted) t.fieldDecl.toString() else "-", + if (t.intercepted) "I" else "-", + "0x%016X".format(t.id), + t.required, + t.name)) + } + a.appendLine() + a.appendLine("# RESERVED serial id kind name") + for (v in reg.reserved) { + a.appendLine( + " %6d %-18s %-12s %s%s".format( + v.serial, "0x%016X".format(v.id), v.kind, v.name, v.field?.let { " -> $it" } ?: "")) + } + a.appendLine() + a.appendLine("# PER-TYPE slotted coordinates as group:field. field-decl restarts per group, so the") + a.appendLine("# group-decl tier disambiguates the shared field-bit word. = trace-level layer.") + for (type in conv.concreteTypes()) { + val coords = + conv.resolve(type).mapNotNull { byName[it.name] } + .filter { it.slotted && !it.traceLevel } + .map { it.groupDecl to it.fieldDecl } + .sortedWith(compareBy({ it.first }, { it.second })) + .map { "${it.first}:${it.second}" } + a.appendLine(" %-14s count=%-3d coords=%s".format(type, coords.size, coords)) + } + val traceCoords = + reg.stored.filter { it.traceLevel && it.slotted } + .map { it.groupDecl to it.fieldDecl } + .sortedWith(compareBy({ it.first }, { it.second })) + .map { "${it.first}:${it.second}" } + a.appendLine(" %-14s count=%-3d coords=%s".format("", traceCoords.size, traceCoords)) + return a.toString() + } + + /** + * layout-by-type.txt — full composition per type (origins shown, NOT de-duped), each tag annotated + * with its field-decl/tier: f = dense field-decl, trace = trace-level layer, bkt = bucket. + */ + private fun layoutByTypeReport(conv: TagConventions, reg: TagRegistry): String { + val byName = reg.stored.associateBy { it.name } + val lay = StringBuilder() + lay.appendLine("# Full tag composition per concrete span type (after extends/include/applies).") + lay.appendLine("# Not de-duped: a tag from >1 source appears >1 time.") + lay.appendLine("# annotation: [gf dense group:field-decl | trace gf trace layer | bkt bucketed] I=intercepted") + for (type in conv.concreteTypes()) { + val comp = conv.compose(type) + val distinct = comp.map { it.second.name }.distinct().size + lay.appendLine() + lay.appendLine("$type (${comp.size} contributions, $distinct distinct):") + val byOrigin = LinkedHashMap>() + for ((origin, tag) in comp) byOrigin.getOrPut(origin) { ArrayList() }.add(tag) + for ((origin, tags) in byOrigin) { + lay.appendLine(" [$origin]") + for (t in tags) { + val st = byName[t.name] + val field = + when { + st == null -> "?" + st.traceLevel && st.slotted -> "trace g${st.groupDecl}f${st.fieldDecl}" + st.slotted -> "g${st.groupDecl}f${st.fieldDecl}" + else -> "bkt" + } + lay.appendLine( + " %-26s %-12s %-12s %s".format(t.name, field, t.required, if (st?.intercepted == true) "I" else "")) + } + } + } + return lay.toString() + } + + /** + * folded-types.txt — each type's full resolved set (extends + include + applies, DE-DUPED) with its + * field-decl; plus the type. This is the "type with everything folded in" view. + */ + private fun foldedTypesReport(conv: TagConventions, reg: TagRegistry): String { + val byName = reg.stored.associateBy { it.name } + fun tierField(st: TagRegistry.StoredTag?): String = + when { + st == null -> "?" + st.traceLevel -> if (st.slotted) "trace g${st.groupDecl}f${st.fieldDecl}" else "trace-bkt" + st.slotted -> "g${st.groupDecl}f${st.fieldDecl}" + else -> "bkt" + } + val f = StringBuilder() + f.appendLine("# Folded tag set per type (extends + include + applies, de-duped), with group:field-decls.") + f.appendLine( + "# field: gf=dense group:field-decl trace ...=trace-level layer bkt=bucketed trace-bkt=trace-level bucketed I=intercepted") + for (type in conv.concreteTypes()) { + val tags = conv.resolve(type) + f.appendLine() + f.appendLine("$type (${tags.size} tags):") + for (t in tags) { + val st = byName[t.name] + f.appendLine(" %-12s %-26s %s".format(tierField(st), t.name, if (st?.intercepted == true) "I" else "")) + } + } + val traceTags = + reg.stored.filter { it.traceLevel }.sortedWith(compareBy({ !it.slotted }, { it.fieldDecl }, { it.name })) + f.appendLine() + f.appendLine(" (${traceTags.size} tags):") + for (st in traceTags) { + f.appendLine(" %-12s %-26s %s".format(tierField(st), st.name, if (st.intercepted) "I" else "")) + } + return f.toString() + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGeneratorPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGeneratorPlugin.kt new file mode 100644 index 00000000000..313bd859068 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/TagRegistryGeneratorPlugin.kt @@ -0,0 +1,41 @@ +package datadog.gradle.plugin.tags + +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory + +/** Extension configuring the tag-registry generator inputs/outputs. */ +abstract class TagRegistryExtension @Inject constructor(objects: ObjectFactory) { + val domainYaml: RegularFileProperty = objects.fileProperty() + val overlayYaml: RegularFileProperty = objects.fileProperty() + val destinationDirectory: DirectoryProperty = objects.directoryProperty() +} + +/** + * Registers {@code generateKnownTags} (emits the committed tag registry) and {@code verifyKnownTags} + * (a freshness gate that regenerates and byte-compares against the committed output). The verify task + * is wired into {@code check} so stale generated sources fail CI. + */ +class TagRegistryGeneratorPlugin : Plugin { + override fun apply(project: Project) { + val ext = project.extensions.create("tagRegistry", TagRegistryExtension::class.java) + project.tasks.register("generateKnownTags", GenerateKnownTagsTask::class.java) { + domainYaml.set(ext.domainYaml) + overlayYaml.set(ext.overlayYaml) + destinationDirectory.set(ext.destinationDirectory) + } + val verify = + project.tasks.register("verifyKnownTags", VerifyKnownTagsTask::class.java) { + domainYaml.set(ext.domainYaml) + overlayYaml.set(ext.overlayYaml) + committedDirectory.set(ext.destinationDirectory) + } + // `check` is contributed by lifecycle-base (via java-library); wait for it before wiring. + project.pluginManager.withPlugin("lifecycle-base") { + project.tasks.named("check").configure { dependsOn(verify) } + } + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/VerifyKnownTagsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/VerifyKnownTagsTask.kt new file mode 100644 index 00000000000..e990e15e957 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/tags/VerifyKnownTagsTask.kt @@ -0,0 +1,67 @@ +package datadog.gradle.plugin.tags + +import java.io.File +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +/** + * Freshness gate: regenerates the tag registry into a scratch dir and byte-compares it against the + * committed [committedDirectory]. Fails (pointing at {@code generateKnownTags}) if they differ, so a + * stale commit of the generated sources can't slip through CI. Not cacheable -- it must actually run + * the generator to catch drift, and it is cheap. + */ +abstract class VerifyKnownTagsTask @Inject constructor(objects: ObjectFactory) : DefaultTask() { + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + val domainYaml: RegularFileProperty = objects.fileProperty() + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + val overlayYaml: RegularFileProperty = objects.fileProperty() + + @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) + val committedDirectory: DirectoryProperty = objects.directoryProperty() + + @TaskAction + fun verify() { + val committed = committedDirectory.get().asFile + val scratch = File(temporaryDir, "generated") + scratch.deleteRecursively() + TagRegistryGenerator.generate(domainYaml.get().asFile, overlayYaml.get().asFile, scratch) + + val diffs = ArrayList() + val freshFiles = scratch.walkTopDown().filter { it.isFile }.toList() + for (fresh in freshFiles) { + val rel = fresh.relativeTo(scratch).path + val committedFile = File(committed, rel) + when { + !committedFile.exists() -> diffs.add("missing (not committed): $rel") + committedFile.readText() != fresh.readText() -> diffs.add("out of date: $rel") + } + } + val freshRel = freshFiles.map { it.relativeTo(scratch).path }.toSet() + for (committedFile in committed.walkTopDown().filter { it.isFile }) { + val rel = committedFile.relativeTo(committed).path + if (rel !in freshRel) diffs.add("stale (no longer generated): $rel") + } + + if (diffs.isNotEmpty()) { + throw GradleException( + buildString { + appendLine("Generated tag registry is out of date with tag-conventions.yaml:") + diffs.forEach { appendLine(" - $it") } + append("Run `./gradlew :internal-api:generateKnownTags` and commit the result.") + }) + } + } +} diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle index 93a817e6452..769e153ddd9 100644 --- a/gradle/spotless.gradle +++ b/gradle/spotless.gradle @@ -21,7 +21,8 @@ spotless { // set explicit target to workaround https://github.com/diffplug/spotless/issues/1163 target 'src/**/*.java' // ignore embedded test projects and everything in build dir, e.g. generated sources - targetExclude('src/test/resources/**', buildDirectoryFiles) + // src/generated/** is emitted by code generators (e.g. the tag registry) — verified by their own freshness gate + targetExclude('src/test/resources/**', 'src/generated/**', buildDirectoryFiles) tableTestFormatter('1.1.1') googleJavaFormat('1.35.0') } diff --git a/internal-api/build.gradle.kts b/internal-api/build.gradle.kts index 4d48a434c19..f1ab6486aa2 100644 --- a/internal-api/build.gradle.kts +++ b/internal-api/build.gradle.kts @@ -5,6 +5,7 @@ import groovy.lang.Closure plugins { `java-library` id("me.champeau.jmh") + id("dd-trace-java.tag-registry-generator") } apply(from = "$rootDir/gradle/java.gradle") @@ -32,6 +33,9 @@ extra["minimumBranchCoverage"] = 0.7 extra["minimumInstructionCoverage"] = 0.8 extra["excludedClassesCoverage"] = listOf( + // Generated by the tag-registry code generator (verified fresh via verifyKnownTags). + "datadog.trace.api.KnownTags", + "datadog.trace.api.KnownTags.*", "datadog.trace.api.ClassloaderConfigurationOverrides", "datadog.trace.api.ClassloaderConfigurationOverrides.Lazy", // Interface @@ -261,6 +265,18 @@ extra["excludedClassesBranchCoverage"] = listOf( extra["excludedClassesInstructionCoverage"] = listOf("datadog.trace.util.stacktrace.StackWalkerFactory") +// Tag registry: generated KnownTags is committed under src/generated (audited via git); the srcDir +// puts it on the main compile path and `verifyKnownTags` (wired into `check`) fails CI if it drifts +// from tag-conventions.yaml. Generation is run on demand (`./gradlew :internal-api:generateKnownTags`), +// not on every build, so the committed source stays the source of truth for the compiler. +tagRegistry { + domainYaml.set(rootProject.layout.projectDirectory.file("tag-conventions.yaml")) + overlayYaml.set(rootProject.layout.projectDirectory.file("tag-conventions.java.yaml")) + destinationDirectory.set(layout.projectDirectory.dir("src/generated")) +} + +sourceSets["main"].java.srcDir("src/generated/java") + dependencies { // references TraceScope and Continuation from public api api(project(":dd-trace-api")) diff --git a/internal-api/src/generated/folded-types.txt b/internal-api/src/generated/folded-types.txt new file mode 100644 index 00000000000..f5637a82f75 --- /dev/null +++ b/internal-api/src/generated/folded-types.txt @@ -0,0 +1,93 @@ +# Folded tag set per type (extends + include + applies, de-duped), with group:field-decls. +# field: gf=dense group:field-decl trace ...=trace-level layer bkt=bucketed trace-bkt=trace-level bucketed I=intercepted + +db.client (21 tags): + g1f0 _dd.parent_id + g1f1 component + g1f2 span.kind I + g1f3 _dd.integration + bkt _dd.svc_src + g1f4 error.type + g1f5 error.message + g1f6 error.stack + g2f0 db.type + g2f1 db.instance + g2f2 db.operation + g2f3 db.user + bkt db.pool.name + g2f4 db.statement I + g8f0 peer.service I + g8f1 _dd.peer.service.source + g8f2 _dd.peer.service.remapped_from + g8f3 peer.hostname + bkt peer.ipv4 + bkt peer.ipv6 + bkt peer.port + +http.client (20 tags): + g1f0 _dd.parent_id + g1f1 component + g1f2 span.kind I + g1f3 _dd.integration + bkt _dd.svc_src + g1f4 error.type + g1f5 error.message + g1f6 error.stack + g3f0 http.method I + g3f1 http.status_code + g3f2 network.protocol.version + g4f0 http.url I + g4f1 http.resend_count + g8f0 peer.service I + g8f1 _dd.peer.service.source + g8f2 _dd.peer.service.remapped_from + g8f3 peer.hostname + bkt peer.ipv4 + bkt peer.ipv6 + bkt peer.port + +http.server (18 tags): + g1f0 _dd.parent_id + g1f1 component + g1f2 span.kind I + g1f3 _dd.integration + bkt _dd.svc_src + g1f4 error.type + g1f5 error.message + g1f6 error.stack + g3f0 http.method I + g3f1 http.status_code + g3f2 network.protocol.version + g4f0 http.url I + g5f0 http.route + g5f1 http.hostname + g5f2 http.useragent + g5f3 http.query.string + bkt servlet.path + bkt servlet.context I + +view.render (9 tags): + g1f0 _dd.parent_id + g1f1 component + g1f2 span.kind I + g1f3 _dd.integration + bkt _dd.svc_src + g1f4 error.type + g1f5 error.message + g1f6 error.stack + g6f0 view.name + + (13 tags): + trace g0f0 _dd.base_service + trace g0f1 version + trace g0f2 env + trace g0f3 language + trace g0f4 runtime-id + trace g0f5 _dd.tracer_host + trace g0f6 _dd.git.commit.sha + trace g0f7 _dd.git.repository_url + trace g0f8 _dd.profiling.enabled + trace g0f9 _dd.dsm.enabled + trace g0f10 _dd.appsec.enabled + trace g0f11 _dd.djm.enabled + trace g0f12 _dd.civisibility.enabled diff --git a/internal-api/src/generated/java/datadog/trace/api/KnownTags.java b/internal-api/src/generated/java/datadog/trace/api/KnownTags.java new file mode 100644 index 00000000000..4505b73da71 --- /dev/null +++ b/internal-api/src/generated/java/datadog/trace/api/KnownTags.java @@ -0,0 +1,602 @@ +package datadog.trace.api; + +import datadog.trace.util.StringIndex; + +// GENERATED by the tag-registry code generator (dd-trace-java.tag-registry-generator). +// DO NOT EDIT. Source: tag-conventions.yaml + tag-conventions.java.yaml. +public final class KnownTags { + static final int SLOT_COUNT = 13; + + // ---- reserved (routed to span fields or directives; not stored) ---- + public static final String ERROR_NAME = "error"; + public static final long ERROR_ID = 0x800103FF00000000L; + // makeTagId(serial=1, group=0, field=NO_SLOT) + intercepted [structural -> error] + + public static final String SERVICE_NAME = "service"; + public static final long SERVICE_ID = 0x800203FF00000000L; + // makeTagId(serial=2, group=0, field=NO_SLOT) + intercepted [structural -> service] + + public static final String RESOURCE_NAME = "resource.name"; + public static final long RESOURCE_NAME_ID = 0x800303FF00000000L; + // makeTagId(serial=3, group=0, field=NO_SLOT) + intercepted [structural -> resource] + + public static final String SPAN_TYPE_NAME = "span.type"; + public static final long SPAN_TYPE_ID = 0x800403FF00000000L; + // makeTagId(serial=4, group=0, field=NO_SLOT) + intercepted [structural -> type] + + public static final String ORIGIN_NAME = "origin"; + public static final long ORIGIN_ID = 0x800503FF00000000L; + // makeTagId(serial=5, group=0, field=NO_SLOT) + intercepted [structural -> origin] + + public static final String SAMPLING_PRIORITY_NAME = "sampling.priority"; + public static final long SAMPLING_PRIORITY_ID = 0x800603FF00000000L; + // makeTagId(serial=6, group=0, field=NO_SLOT) + intercepted [directive] + + public static final String MANUAL_KEEP_NAME = "manual.keep"; + public static final long MANUAL_KEEP_ID = 0x800703FF00000000L; + // makeTagId(serial=7, group=0, field=NO_SLOT) + intercepted [directive] + + public static final String MANUAL_DROP_NAME = "manual.drop"; + public static final long MANUAL_DROP_ID = 0x800803FF00000000L; + // makeTagId(serial=8, group=0, field=NO_SLOT) + intercepted [directive] + + public static final String MEASURED_NAME = "measured"; + public static final long MEASURED_ID = 0x800903FF00000000L; + // makeTagId(serial=9, group=0, field=NO_SLOT) + intercepted [directive] + + public static final String ANALYTICS_SAMPLE_RATE_NAME = "analytics.sample_rate"; + public static final long ANALYTICS_SAMPLE_RATE_ID = 0x800A03FF00000000L; + // makeTagId(serial=10, group=0, field=NO_SLOT) + intercepted [directive] + + // ---- stored (dense field-decl, or bucketed when field=NO_SLOT) ---- + public static final String DD_BASE_SERVICE_NAME = "_dd.base_service"; + public static final long DD_BASE_SERVICE_ID = 0x0100000000000000L; + // makeTagId(serial=256, group=0, field=0) + + public static final String VERSION_NAME = "version"; + public static final long VERSION_ID = 0x0101000100000000L; + // makeTagId(serial=257, group=0, field=1) + + public static final String ENV_NAME = "env"; + public static final long ENV_ID = 0x0102000200000000L; + // makeTagId(serial=258, group=0, field=2) + + public static final String LANGUAGE_NAME = "language"; + public static final long LANGUAGE_ID = 0x0103000300000000L; + // makeTagId(serial=259, group=0, field=3) + + public static final String RUNTIME_ID_NAME = "runtime-id"; + public static final long RUNTIME_ID = 0x0104000400000000L; + // makeTagId(serial=260, group=0, field=4) + + public static final String DD_TRACER_HOST_NAME = "_dd.tracer_host"; + public static final long DD_TRACER_HOST_ID = 0x0105000500000000L; + // makeTagId(serial=261, group=0, field=5) + + public static final String DD_GIT_COMMIT_SHA_NAME = "_dd.git.commit.sha"; + public static final long DD_GIT_COMMIT_SHA_ID = 0x0106000600000000L; + // makeTagId(serial=262, group=0, field=6) + + public static final String DD_GIT_REPOSITORY_URL_NAME = "_dd.git.repository_url"; + public static final long DD_GIT_REPOSITORY_URL_ID = 0x0107000700000000L; + // makeTagId(serial=263, group=0, field=7) + + public static final String DD_PROFILING_ENABLED_NAME = "_dd.profiling.enabled"; + public static final long DD_PROFILING_ENABLED_ID = 0x0108000800000000L; + // makeTagId(serial=264, group=0, field=8) + + public static final String DD_DSM_ENABLED_NAME = "_dd.dsm.enabled"; + public static final long DD_DSM_ENABLED_ID = 0x0109000900000000L; + // makeTagId(serial=265, group=0, field=9) + + public static final String DD_APPSEC_ENABLED_NAME = "_dd.appsec.enabled"; + public static final long DD_APPSEC_ENABLED_ID = 0x010A000A00000000L; + // makeTagId(serial=266, group=0, field=10) + + public static final String DD_DJM_ENABLED_NAME = "_dd.djm.enabled"; + public static final long DD_DJM_ENABLED_ID = 0x010B000B00000000L; + // makeTagId(serial=267, group=0, field=11) + + public static final String DD_CIVISIBILITY_ENABLED_NAME = "_dd.civisibility.enabled"; + public static final long DD_CIVISIBILITY_ENABLED_ID = 0x010C000C00000000L; + // makeTagId(serial=268, group=0, field=12) + + public static final String DD_PARENT_ID_NAME = "_dd.parent_id"; + public static final long DD_PARENT_ID = 0x010D040000000000L; + // makeTagId(serial=269, group=1, field=0) + + public static final String COMPONENT_NAME = "component"; + public static final long COMPONENT_ID = 0x010E040100000000L; + // makeTagId(serial=270, group=1, field=1) + + public static final String SPAN_KIND_NAME = "span.kind"; + public static final long SPAN_KIND_ID = 0x810F040200000000L; + // makeTagId(serial=271, group=1, field=2) + intercepted + + public static final String DD_INTEGRATION_NAME = "_dd.integration"; + public static final long DD_INTEGRATION_ID = 0x0110040300000000L; + // makeTagId(serial=272, group=1, field=3) + + public static final String DD_SVC_SRC_NAME = "_dd.svc_src"; + public static final long DD_SVC_SRC_ID = 0x011107FF00000000L; + // makeTagId(serial=273, group=1, field=NO_SLOT) + + public static final String ERROR_TYPE_NAME = "error.type"; + public static final long ERROR_TYPE_ID = 0x0112040400000000L; + // makeTagId(serial=274, group=1, field=4) + + public static final String ERROR_MESSAGE_NAME = "error.message"; + public static final long ERROR_MESSAGE_ID = 0x0113040500000000L; + // makeTagId(serial=275, group=1, field=5) + + public static final String ERROR_STACK_NAME = "error.stack"; + public static final long ERROR_STACK_ID = 0x0114040600000000L; + // makeTagId(serial=276, group=1, field=6) + + public static final String DB_TYPE_NAME = "db.type"; + public static final long DB_TYPE_ID = 0x0115080000000000L; + // makeTagId(serial=277, group=2, field=0) + + public static final String DB_INSTANCE_NAME = "db.instance"; + public static final long DB_INSTANCE_ID = 0x0116080100000000L; + // makeTagId(serial=278, group=2, field=1) + + public static final String DB_OPERATION_NAME = "db.operation"; + public static final long DB_OPERATION_ID = 0x0117080200000000L; + // makeTagId(serial=279, group=2, field=2) + + public static final String DB_USER_NAME = "db.user"; + public static final long DB_USER_ID = 0x0118080300000000L; + // makeTagId(serial=280, group=2, field=3) + + public static final String DB_POOL_NAME = "db.pool.name"; + public static final long DB_POOL_NAME_ID = 0x01190BFF00000000L; + // makeTagId(serial=281, group=2, field=NO_SLOT) + + public static final String DB_STATEMENT_NAME = "db.statement"; + public static final long DB_STATEMENT_ID = 0x811A080400000000L; + // makeTagId(serial=282, group=2, field=4) + intercepted + + public static final String HTTP_METHOD_NAME = "http.method"; + public static final long HTTP_METHOD_ID = 0x811B0C0000000000L; + // makeTagId(serial=283, group=3, field=0) + intercepted + + public static final String HTTP_STATUS_CODE_NAME = "http.status_code"; + public static final long HTTP_STATUS_CODE_ID = 0x011C0C0100000000L; + // makeTagId(serial=284, group=3, field=1) + + public static final String NETWORK_PROTOCOL_VERSION_NAME = "network.protocol.version"; + public static final long NETWORK_PROTOCOL_VERSION_ID = 0x011D0C0200000000L; + // makeTagId(serial=285, group=3, field=2) + + public static final String HTTP_URL_NAME = "http.url"; + public static final long HTTP_URL_ID = 0x811E100000000000L; + // makeTagId(serial=286, group=4, field=0) + intercepted + + public static final String HTTP_RESEND_COUNT_NAME = "http.resend_count"; + public static final long HTTP_RESEND_COUNT_ID = 0x011F100100000000L; + // makeTagId(serial=287, group=4, field=1) + + public static final String HTTP_ROUTE_NAME = "http.route"; + public static final long HTTP_ROUTE_ID = 0x0120140000000000L; + // makeTagId(serial=288, group=5, field=0) + + public static final String HTTP_HOSTNAME_NAME = "http.hostname"; + public static final long HTTP_HOSTNAME_ID = 0x0121140100000000L; + // makeTagId(serial=289, group=5, field=1) + + public static final String HTTP_USERAGENT_NAME = "http.useragent"; + public static final long HTTP_USERAGENT_ID = 0x0122140200000000L; + // makeTagId(serial=290, group=5, field=2) + + public static final String HTTP_QUERY_STRING_NAME = "http.query.string"; + public static final long HTTP_QUERY_STRING_ID = 0x0123140300000000L; + // makeTagId(serial=291, group=5, field=3) + + public static final String SERVLET_PATH_NAME = "servlet.path"; + public static final long SERVLET_PATH_ID = 0x012417FF00000000L; + // makeTagId(serial=292, group=5, field=NO_SLOT) + + public static final String SERVLET_CONTEXT_NAME = "servlet.context"; + public static final long SERVLET_CONTEXT_ID = 0x812517FF00000000L; + // makeTagId(serial=293, group=5, field=NO_SLOT) + intercepted + + public static final String VIEW_NAME = "view.name"; + public static final long VIEW_NAME_ID = 0x0126180000000000L; + // makeTagId(serial=294, group=6, field=0) + + public static final String TEST_NAME = "test.name"; + public static final long TEST_NAME_ID = 0x01271C0000000000L; + // makeTagId(serial=295, group=7, field=0) + + public static final String TEST_SUITE_NAME = "test.suite"; + public static final long TEST_SUITE_ID = 0x01281C0100000000L; + // makeTagId(serial=296, group=7, field=1) + + public static final String TEST_STATUS_NAME = "test.status"; + public static final long TEST_STATUS_ID = 0x01291C0200000000L; + // makeTagId(serial=297, group=7, field=2) + + public static final String TEST_FRAMEWORK_NAME = "test.framework"; + public static final long TEST_FRAMEWORK_ID = 0x012A1C0300000000L; + // makeTagId(serial=298, group=7, field=3) + + public static final String PEER_SERVICE_NAME = "peer.service"; + public static final long PEER_SERVICE_ID = 0x812B200000000000L; + // makeTagId(serial=299, group=8, field=0) + intercepted + + public static final String DD_PEER_SERVICE_SOURCE_NAME = "_dd.peer.service.source"; + public static final long DD_PEER_SERVICE_SOURCE_ID = 0x012C200100000000L; + // makeTagId(serial=300, group=8, field=1) + + public static final String DD_PEER_SERVICE_REMAPPED_FROM_NAME = "_dd.peer.service.remapped_from"; + public static final long DD_PEER_SERVICE_REMAPPED_FROM_ID = 0x012D200200000000L; + // makeTagId(serial=301, group=8, field=2) + + public static final String PEER_HOSTNAME_NAME = "peer.hostname"; + public static final long PEER_HOSTNAME_ID = 0x012E200300000000L; + // makeTagId(serial=302, group=8, field=3) + + public static final String PEER_IPV4_NAME = "peer.ipv4"; + public static final long PEER_IPV4_ID = 0x012F23FF00000000L; + // makeTagId(serial=303, group=8, field=NO_SLOT) + + public static final String PEER_IPV6_NAME = "peer.ipv6"; + public static final long PEER_IPV6_ID = 0x013023FF00000000L; + // makeTagId(serial=304, group=8, field=NO_SLOT) + + public static final String PEER_PORT_NAME = "peer.port"; + public static final long PEER_PORT_ID = 0x013123FF00000000L; + // makeTagId(serial=305, group=8, field=NO_SLOT) + + // ---- serial numbers ---- + static final int ERROR_SERIAL_NUM = 1; + static final int SERVICE_SERIAL_NUM = 2; + static final int RESOURCE_NAME_SERIAL_NUM = 3; + static final int SPAN_TYPE_SERIAL_NUM = 4; + static final int ORIGIN_SERIAL_NUM = 5; + static final int SAMPLING_PRIORITY_SERIAL_NUM = 6; + static final int MANUAL_KEEP_SERIAL_NUM = 7; + static final int MANUAL_DROP_SERIAL_NUM = 8; + static final int MEASURED_SERIAL_NUM = 9; + static final int ANALYTICS_SAMPLE_RATE_SERIAL_NUM = 10; + static final int DD_BASE_SERVICE_SERIAL_NUM = 256; + static final int VERSION_SERIAL_NUM = 257; + static final int ENV_SERIAL_NUM = 258; + static final int LANGUAGE_SERIAL_NUM = 259; + static final int RUNTIME_ID_SERIAL_NUM = 260; + static final int DD_TRACER_HOST_SERIAL_NUM = 261; + static final int DD_GIT_COMMIT_SHA_SERIAL_NUM = 262; + static final int DD_GIT_REPOSITORY_URL_SERIAL_NUM = 263; + static final int DD_PROFILING_ENABLED_SERIAL_NUM = 264; + static final int DD_DSM_ENABLED_SERIAL_NUM = 265; + static final int DD_APPSEC_ENABLED_SERIAL_NUM = 266; + static final int DD_DJM_ENABLED_SERIAL_NUM = 267; + static final int DD_CIVISIBILITY_ENABLED_SERIAL_NUM = 268; + static final int DD_PARENT_ID_SERIAL_NUM = 269; + static final int COMPONENT_SERIAL_NUM = 270; + static final int SPAN_KIND_SERIAL_NUM = 271; + static final int DD_INTEGRATION_SERIAL_NUM = 272; + static final int DD_SVC_SRC_SERIAL_NUM = 273; + static final int ERROR_TYPE_SERIAL_NUM = 274; + static final int ERROR_MESSAGE_SERIAL_NUM = 275; + static final int ERROR_STACK_SERIAL_NUM = 276; + static final int DB_TYPE_SERIAL_NUM = 277; + static final int DB_INSTANCE_SERIAL_NUM = 278; + static final int DB_OPERATION_SERIAL_NUM = 279; + static final int DB_USER_SERIAL_NUM = 280; + static final int DB_POOL_NAME_SERIAL_NUM = 281; + static final int DB_STATEMENT_SERIAL_NUM = 282; + static final int HTTP_METHOD_SERIAL_NUM = 283; + static final int HTTP_STATUS_CODE_SERIAL_NUM = 284; + static final int NETWORK_PROTOCOL_VERSION_SERIAL_NUM = 285; + static final int HTTP_URL_SERIAL_NUM = 286; + static final int HTTP_RESEND_COUNT_SERIAL_NUM = 287; + static final int HTTP_ROUTE_SERIAL_NUM = 288; + static final int HTTP_HOSTNAME_SERIAL_NUM = 289; + static final int HTTP_USERAGENT_SERIAL_NUM = 290; + static final int HTTP_QUERY_STRING_SERIAL_NUM = 291; + static final int SERVLET_PATH_SERIAL_NUM = 292; + static final int SERVLET_CONTEXT_SERIAL_NUM = 293; + static final int VIEW_NAME_SERIAL_NUM = 294; + static final int TEST_NAME_SERIAL_NUM = 295; + static final int TEST_SUITE_SERIAL_NUM = 296; + static final int TEST_STATUS_SERIAL_NUM = 297; + static final int TEST_FRAMEWORK_SERIAL_NUM = 298; + static final int PEER_SERVICE_SERIAL_NUM = 299; + static final int DD_PEER_SERVICE_SOURCE_SERIAL_NUM = 300; + static final int DD_PEER_SERVICE_REMAPPED_FROM_SERIAL_NUM = 301; + static final int PEER_HOSTNAME_SERIAL_NUM = 302; + static final int PEER_IPV4_SERIAL_NUM = 303; + static final int PEER_IPV6_SERIAL_NUM = 304; + static final int PEER_PORT_SERIAL_NUM = 305; + + private static final String[] KEYOF_NAMES = { + ERROR_NAME, + SERVICE_NAME, + RESOURCE_NAME, + SPAN_TYPE_NAME, + ORIGIN_NAME, + SAMPLING_PRIORITY_NAME, + MANUAL_KEEP_NAME, + MANUAL_DROP_NAME, + MEASURED_NAME, + ANALYTICS_SAMPLE_RATE_NAME, + DD_BASE_SERVICE_NAME, + VERSION_NAME, + ENV_NAME, + LANGUAGE_NAME, + RUNTIME_ID_NAME, + DD_TRACER_HOST_NAME, + DD_GIT_COMMIT_SHA_NAME, + DD_GIT_REPOSITORY_URL_NAME, + DD_PROFILING_ENABLED_NAME, + DD_DSM_ENABLED_NAME, + DD_APPSEC_ENABLED_NAME, + DD_DJM_ENABLED_NAME, + DD_CIVISIBILITY_ENABLED_NAME, + DD_PARENT_ID_NAME, + COMPONENT_NAME, + SPAN_KIND_NAME, + DD_INTEGRATION_NAME, + DD_SVC_SRC_NAME, + ERROR_TYPE_NAME, + ERROR_MESSAGE_NAME, + ERROR_STACK_NAME, + DB_TYPE_NAME, + DB_INSTANCE_NAME, + DB_OPERATION_NAME, + DB_USER_NAME, + DB_POOL_NAME, + DB_STATEMENT_NAME, + HTTP_METHOD_NAME, + HTTP_STATUS_CODE_NAME, + NETWORK_PROTOCOL_VERSION_NAME, + HTTP_URL_NAME, + HTTP_RESEND_COUNT_NAME, + HTTP_ROUTE_NAME, + HTTP_HOSTNAME_NAME, + HTTP_USERAGENT_NAME, + HTTP_QUERY_STRING_NAME, + SERVLET_PATH_NAME, + SERVLET_CONTEXT_NAME, + VIEW_NAME, + TEST_NAME, + TEST_SUITE_NAME, + TEST_STATUS_NAME, + TEST_FRAMEWORK_NAME, + PEER_SERVICE_NAME, + DD_PEER_SERVICE_SOURCE_NAME, + DD_PEER_SERVICE_REMAPPED_FROM_NAME, + PEER_HOSTNAME_NAME, + PEER_IPV4_NAME, + PEER_IPV6_NAME, + PEER_PORT_NAME, + }; + private static final long[] KEYOF_VALUES = { + ERROR_ID, + SERVICE_ID, + RESOURCE_NAME_ID, + SPAN_TYPE_ID, + ORIGIN_ID, + SAMPLING_PRIORITY_ID, + MANUAL_KEEP_ID, + MANUAL_DROP_ID, + MEASURED_ID, + ANALYTICS_SAMPLE_RATE_ID, + DD_BASE_SERVICE_ID, + VERSION_ID, + ENV_ID, + LANGUAGE_ID, + RUNTIME_ID, + DD_TRACER_HOST_ID, + DD_GIT_COMMIT_SHA_ID, + DD_GIT_REPOSITORY_URL_ID, + DD_PROFILING_ENABLED_ID, + DD_DSM_ENABLED_ID, + DD_APPSEC_ENABLED_ID, + DD_DJM_ENABLED_ID, + DD_CIVISIBILITY_ENABLED_ID, + DD_PARENT_ID, + COMPONENT_ID, + SPAN_KIND_ID, + DD_INTEGRATION_ID, + DD_SVC_SRC_ID, + ERROR_TYPE_ID, + ERROR_MESSAGE_ID, + ERROR_STACK_ID, + DB_TYPE_ID, + DB_INSTANCE_ID, + DB_OPERATION_ID, + DB_USER_ID, + DB_POOL_NAME_ID, + DB_STATEMENT_ID, + HTTP_METHOD_ID, + HTTP_STATUS_CODE_ID, + NETWORK_PROTOCOL_VERSION_ID, + HTTP_URL_ID, + HTTP_RESEND_COUNT_ID, + HTTP_ROUTE_ID, + HTTP_HOSTNAME_ID, + HTTP_USERAGENT_ID, + HTTP_QUERY_STRING_ID, + SERVLET_PATH_ID, + SERVLET_CONTEXT_ID, + VIEW_NAME_ID, + TEST_NAME_ID, + TEST_SUITE_ID, + TEST_STATUS_ID, + TEST_FRAMEWORK_ID, + PEER_SERVICE_ID, + DD_PEER_SERVICE_SOURCE_ID, + DD_PEER_SERVICE_REMAPPED_FROM_ID, + PEER_HOSTNAME_ID, + PEER_IPV4_ID, + PEER_IPV6_ID, + PEER_PORT_ID, + }; + private static final int[] KEYOF_HASHES; + private static final String[] KEYOF_KEYS; + private static final long[] KEYOF_IDS; + static { + StringIndex.Data data = StringIndex.EmbeddingSupport.create(KEYOF_NAMES); + long[] ids = new long[data.names.length]; + for (int j = 0; j < KEYOF_NAMES.length; j++) { + ids[StringIndex.EmbeddingSupport.indexOf(data.hashes, data.names, KEYOF_NAMES[j])] = KEYOF_VALUES[j]; + } + KEYOF_HASHES = data.hashes; + KEYOF_KEYS = data.names; + KEYOF_IDS = ids; + } + + static final KnownTagCodec.Resolver RESOLVER = + new KnownTagCodec.Resolver() { + @Override + public String nameOf(long tagId) { + switch (KnownTagCodec.serialNum(tagId)) { + case ERROR_SERIAL_NUM: + return ERROR_NAME; + case SERVICE_SERIAL_NUM: + return SERVICE_NAME; + case RESOURCE_NAME_SERIAL_NUM: + return RESOURCE_NAME; + case SPAN_TYPE_SERIAL_NUM: + return SPAN_TYPE_NAME; + case ORIGIN_SERIAL_NUM: + return ORIGIN_NAME; + case SAMPLING_PRIORITY_SERIAL_NUM: + return SAMPLING_PRIORITY_NAME; + case MANUAL_KEEP_SERIAL_NUM: + return MANUAL_KEEP_NAME; + case MANUAL_DROP_SERIAL_NUM: + return MANUAL_DROP_NAME; + case MEASURED_SERIAL_NUM: + return MEASURED_NAME; + case ANALYTICS_SAMPLE_RATE_SERIAL_NUM: + return ANALYTICS_SAMPLE_RATE_NAME; + case DD_BASE_SERVICE_SERIAL_NUM: + return DD_BASE_SERVICE_NAME; + case VERSION_SERIAL_NUM: + return VERSION_NAME; + case ENV_SERIAL_NUM: + return ENV_NAME; + case LANGUAGE_SERIAL_NUM: + return LANGUAGE_NAME; + case RUNTIME_ID_SERIAL_NUM: + return RUNTIME_ID_NAME; + case DD_TRACER_HOST_SERIAL_NUM: + return DD_TRACER_HOST_NAME; + case DD_GIT_COMMIT_SHA_SERIAL_NUM: + return DD_GIT_COMMIT_SHA_NAME; + case DD_GIT_REPOSITORY_URL_SERIAL_NUM: + return DD_GIT_REPOSITORY_URL_NAME; + case DD_PROFILING_ENABLED_SERIAL_NUM: + return DD_PROFILING_ENABLED_NAME; + case DD_DSM_ENABLED_SERIAL_NUM: + return DD_DSM_ENABLED_NAME; + case DD_APPSEC_ENABLED_SERIAL_NUM: + return DD_APPSEC_ENABLED_NAME; + case DD_DJM_ENABLED_SERIAL_NUM: + return DD_DJM_ENABLED_NAME; + case DD_CIVISIBILITY_ENABLED_SERIAL_NUM: + return DD_CIVISIBILITY_ENABLED_NAME; + case DD_PARENT_ID_SERIAL_NUM: + return DD_PARENT_ID_NAME; + case COMPONENT_SERIAL_NUM: + return COMPONENT_NAME; + case SPAN_KIND_SERIAL_NUM: + return SPAN_KIND_NAME; + case DD_INTEGRATION_SERIAL_NUM: + return DD_INTEGRATION_NAME; + case DD_SVC_SRC_SERIAL_NUM: + return DD_SVC_SRC_NAME; + case ERROR_TYPE_SERIAL_NUM: + return ERROR_TYPE_NAME; + case ERROR_MESSAGE_SERIAL_NUM: + return ERROR_MESSAGE_NAME; + case ERROR_STACK_SERIAL_NUM: + return ERROR_STACK_NAME; + case DB_TYPE_SERIAL_NUM: + return DB_TYPE_NAME; + case DB_INSTANCE_SERIAL_NUM: + return DB_INSTANCE_NAME; + case DB_OPERATION_SERIAL_NUM: + return DB_OPERATION_NAME; + case DB_USER_SERIAL_NUM: + return DB_USER_NAME; + case DB_POOL_NAME_SERIAL_NUM: + return DB_POOL_NAME; + case DB_STATEMENT_SERIAL_NUM: + return DB_STATEMENT_NAME; + case HTTP_METHOD_SERIAL_NUM: + return HTTP_METHOD_NAME; + case HTTP_STATUS_CODE_SERIAL_NUM: + return HTTP_STATUS_CODE_NAME; + case NETWORK_PROTOCOL_VERSION_SERIAL_NUM: + return NETWORK_PROTOCOL_VERSION_NAME; + case HTTP_URL_SERIAL_NUM: + return HTTP_URL_NAME; + case HTTP_RESEND_COUNT_SERIAL_NUM: + return HTTP_RESEND_COUNT_NAME; + case HTTP_ROUTE_SERIAL_NUM: + return HTTP_ROUTE_NAME; + case HTTP_HOSTNAME_SERIAL_NUM: + return HTTP_HOSTNAME_NAME; + case HTTP_USERAGENT_SERIAL_NUM: + return HTTP_USERAGENT_NAME; + case HTTP_QUERY_STRING_SERIAL_NUM: + return HTTP_QUERY_STRING_NAME; + case SERVLET_PATH_SERIAL_NUM: + return SERVLET_PATH_NAME; + case SERVLET_CONTEXT_SERIAL_NUM: + return SERVLET_CONTEXT_NAME; + case VIEW_NAME_SERIAL_NUM: + return VIEW_NAME; + case TEST_NAME_SERIAL_NUM: + return TEST_NAME; + case TEST_SUITE_SERIAL_NUM: + return TEST_SUITE_NAME; + case TEST_STATUS_SERIAL_NUM: + return TEST_STATUS_NAME; + case TEST_FRAMEWORK_SERIAL_NUM: + return TEST_FRAMEWORK_NAME; + case PEER_SERVICE_SERIAL_NUM: + return PEER_SERVICE_NAME; + case DD_PEER_SERVICE_SOURCE_SERIAL_NUM: + return DD_PEER_SERVICE_SOURCE_NAME; + case DD_PEER_SERVICE_REMAPPED_FROM_SERIAL_NUM: + return DD_PEER_SERVICE_REMAPPED_FROM_NAME; + case PEER_HOSTNAME_SERIAL_NUM: + return PEER_HOSTNAME_NAME; + case PEER_IPV4_SERIAL_NUM: + return PEER_IPV4_NAME; + case PEER_IPV6_SERIAL_NUM: + return PEER_IPV6_NAME; + case PEER_PORT_SERIAL_NUM: + return PEER_PORT_NAME; + default: + return null; + } + } + + @Override + public int slotCount() { + return SLOT_COUNT; + } + + @Override + public long keyOf(String name) { + int slot = StringIndex.EmbeddingSupport.indexOf(KEYOF_HASHES, KEYOF_KEYS, name); + return slot < 0 ? 0L : KEYOF_IDS[slot]; + } + }; + + static { + KnownTagCodec.register(RESOLVER); + } + + /** Forces resolver registration by triggering . Idempotent. */ + public static void init() {} + + private KnownTags() {} +} diff --git a/internal-api/src/generated/layout-by-type.txt b/internal-api/src/generated/layout-by-type.txt new file mode 100644 index 00000000000..f56ac49a1ae --- /dev/null +++ b/internal-api/src/generated/layout-by-type.txt @@ -0,0 +1,91 @@ +# Full tag composition per concrete span type (after extends/include/applies). +# Not de-duped: a tag from >1 source appears >1 time. +# annotation: [gf dense group:field-decl | trace gf trace layer | bkt bucketed] I=intercepted + +db.client (21 contributions, 21 distinct): + [base] + _dd.parent_id g1f0 required + component g1f1 required + span.kind g1f2 required I + _dd.integration g1f3 recommended + _dd.svc_src bkt optional + error.type g1f4 recommended + error.message g1f5 recommended + error.stack g1f6 recommended + [db.client] + db.type g2f0 required + db.instance g2f1 recommended + db.operation g2f2 recommended + db.user g2f3 recommended + db.pool.name bkt optional + db.statement g2f4 recommended I + [incl:peer] + peer.service g8f0 recommended I + _dd.peer.service.source g8f1 recommended + _dd.peer.service.remapped_from g8f2 recommended + peer.hostname g8f3 recommended + peer.ipv4 bkt optional + peer.ipv6 bkt optional + peer.port bkt optional + +http.client (20 contributions, 20 distinct): + [base] + _dd.parent_id g1f0 required + component g1f1 required + span.kind g1f2 required I + _dd.integration g1f3 recommended + _dd.svc_src bkt optional + error.type g1f4 recommended + error.message g1f5 recommended + error.stack g1f6 recommended + [http] + http.method g3f0 required I + http.status_code g3f1 conditional + network.protocol.version g3f2 recommended + [http.client] + http.url g4f0 required I + http.resend_count g4f1 recommended + [incl:peer] + peer.service g8f0 recommended I + _dd.peer.service.source g8f1 recommended + _dd.peer.service.remapped_from g8f2 recommended + peer.hostname g8f3 recommended + peer.ipv4 bkt optional + peer.ipv6 bkt optional + peer.port bkt optional + +http.server (18 contributions, 18 distinct): + [base] + _dd.parent_id g1f0 required + component g1f1 required + span.kind g1f2 required I + _dd.integration g1f3 recommended + _dd.svc_src bkt optional + error.type g1f4 recommended + error.message g1f5 recommended + error.stack g1f6 recommended + [http] + http.method g3f0 required I + http.status_code g3f1 conditional + network.protocol.version g3f2 recommended + [http.server] + http.url g4f0 required I + http.route g5f0 conditional + http.hostname g5f1 required + http.useragent g5f2 recommended + http.query.string g5f3 recommended + servlet.path bkt optional + servlet.context bkt optional I + +view.render (9 contributions, 9 distinct): + [base] + _dd.parent_id g1f0 required + component g1f1 required + span.kind g1f2 required I + _dd.integration g1f3 recommended + _dd.svc_src bkt optional + error.type g1f4 recommended + error.message g1f5 recommended + error.stack g1f6 recommended + [view.render] + view.name g6f0 recommended diff --git a/internal-api/src/generated/resolved-tags.txt b/internal-api/src/generated/resolved-tags.txt new file mode 100644 index 00000000000..0edb3479608 --- /dev/null +++ b/internal-api/src/generated/resolved-tags.txt @@ -0,0 +1,77 @@ +# Resolved per-type tag sets (concrete span types). + +db.client (21 tags): + - _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 + - peer.service + - _dd.peer.service.source + - _dd.peer.service.remapped_from + - peer.hostname + - peer.ipv4 + - peer.ipv6 + - peer.port + +http.client (20 tags): + - _dd.parent_id + - component + - span.kind + - _dd.integration + - _dd.svc_src + - error.type + - error.message + - error.stack + - http.method + - http.status_code + - network.protocol.version + - http.url + - http.resend_count + - peer.service + - _dd.peer.service.source + - _dd.peer.service.remapped_from + - peer.hostname + - peer.ipv4 + - peer.ipv6 + - peer.port + +http.server (18 tags): + - _dd.parent_id + - component + - span.kind + - _dd.integration + - _dd.svc_src + - error.type + - error.message + - error.stack + - http.method + - http.status_code + - network.protocol.version + - http.url + - http.route + - http.hostname + - http.useragent + - http.query.string + - servlet.path + - servlet.context + +view.render (9 tags): + - _dd.parent_id + - component + - span.kind + - _dd.integration + - _dd.svc_src + - error.type + - error.message + - error.stack + - view.name diff --git a/internal-api/src/generated/tag-assignment.txt b/internal-api/src/generated/tag-assignment.txt new file mode 100644 index 00000000000..cf17c61f701 --- /dev/null +++ b/internal-api/src/generated/tag-assignment.txt @@ -0,0 +1,73 @@ +# Tag id assignment. slotCount=13 stored=50 reserved=10 + +# STORED serial group field int id required name + 256 0 0 - 0x0100000000000000 required _dd.base_service + 257 0 1 - 0x0101000100000000 recommended version + 258 0 2 - 0x0102000200000000 recommended env + 259 0 3 - 0x0103000300000000 required language + 260 0 4 - 0x0104000400000000 required runtime-id + 261 0 5 - 0x0105000500000000 recommended _dd.tracer_host + 262 0 6 - 0x0106000600000000 recommended _dd.git.commit.sha + 263 0 7 - 0x0107000700000000 recommended _dd.git.repository_url + 264 0 8 - 0x0108000800000000 recommended _dd.profiling.enabled + 265 0 9 - 0x0109000900000000 recommended _dd.dsm.enabled + 266 0 10 - 0x010A000A00000000 recommended _dd.appsec.enabled + 267 0 11 - 0x010B000B00000000 recommended _dd.djm.enabled + 268 0 12 - 0x010C000C00000000 recommended _dd.civisibility.enabled + 269 1 0 - 0x010D040000000000 required _dd.parent_id + 270 1 1 - 0x010E040100000000 required component + 271 1 2 I 0x810F040200000000 required span.kind + 272 1 3 - 0x0110040300000000 recommended _dd.integration + 273 1 - - 0x011107FF00000000 optional _dd.svc_src + 274 1 4 - 0x0112040400000000 recommended error.type + 275 1 5 - 0x0113040500000000 recommended error.message + 276 1 6 - 0x0114040600000000 recommended error.stack + 277 2 0 - 0x0115080000000000 required db.type + 278 2 1 - 0x0116080100000000 recommended db.instance + 279 2 2 - 0x0117080200000000 recommended db.operation + 280 2 3 - 0x0118080300000000 recommended db.user + 281 2 - - 0x01190BFF00000000 optional db.pool.name + 282 2 4 I 0x811A080400000000 recommended db.statement + 283 3 0 I 0x811B0C0000000000 required http.method + 284 3 1 - 0x011C0C0100000000 conditional http.status_code + 285 3 2 - 0x011D0C0200000000 recommended network.protocol.version + 286 4 0 I 0x811E100000000000 required http.url + 287 4 1 - 0x011F100100000000 recommended http.resend_count + 288 5 0 - 0x0120140000000000 conditional http.route + 289 5 1 - 0x0121140100000000 required http.hostname + 290 5 2 - 0x0122140200000000 recommended http.useragent + 291 5 3 - 0x0123140300000000 recommended http.query.string + 292 5 - - 0x012417FF00000000 optional servlet.path + 293 5 - I 0x812517FF00000000 optional servlet.context + 294 6 0 - 0x0126180000000000 recommended view.name + 295 7 0 - 0x01271C0000000000 recommended test.name + 296 7 1 - 0x01281C0100000000 recommended test.suite + 297 7 2 - 0x01291C0200000000 recommended test.status + 298 7 3 - 0x012A1C0300000000 recommended test.framework + 299 8 0 I 0x812B200000000000 recommended peer.service + 300 8 1 - 0x012C200100000000 recommended _dd.peer.service.source + 301 8 2 - 0x012D200200000000 recommended _dd.peer.service.remapped_from + 302 8 3 - 0x012E200300000000 recommended peer.hostname + 303 8 - - 0x012F23FF00000000 optional peer.ipv4 + 304 8 - - 0x013023FF00000000 optional peer.ipv6 + 305 8 - - 0x013123FF00000000 optional peer.port + +# RESERVED serial id kind name + 1 0x800103FF00000000 structural error -> error + 2 0x800203FF00000000 structural service -> service + 3 0x800303FF00000000 structural resource.name -> resource + 4 0x800403FF00000000 structural span.type -> type + 5 0x800503FF00000000 structural origin -> origin + 6 0x800603FF00000000 directive sampling.priority + 7 0x800703FF00000000 directive manual.keep + 8 0x800803FF00000000 directive manual.drop + 9 0x800903FF00000000 directive measured + 10 0x800A03FF00000000 directive analytics.sample_rate + +# PER-TYPE slotted coordinates as group:field. field-decl restarts per group, so the +# group-decl tier disambiguates the shared field-bit word. = trace-level layer. + db.client count=16 coords=[1:0, 1:1, 1:2, 1:3, 1:4, 1:5, 1:6, 2:0, 2:1, 2:2, 2:3, 2:4, 8:0, 8:1, 8:2, 8:3] + http.client count=16 coords=[1:0, 1:1, 1:2, 1:3, 1:4, 1:5, 1:6, 3:0, 3:1, 3:2, 4:0, 4:1, 8:0, 8:1, 8:2, 8:3] + http.server count=15 coords=[1:0, 1:1, 1:2, 1:3, 1:4, 1:5, 1:6, 3:0, 3:1, 3:2, 4:0, 5:0, 5:1, 5:2, 5:3] + view.render count=8 coords=[1:0, 1:1, 1:2, 1:3, 1:4, 1:5, 1:6, 6:0] + count=13 coords=[0:0, 0:1, 0:2, 0:3, 0:4, 0:5, 0:6, 0:7, 0:8, 0:9, 0:10, 0:11, 0:12] 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 e85294170e7..f9ac524a5fd 100644 --- a/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java +++ b/internal-api/src/main/java/datadog/trace/api/KnownTagCodec.java @@ -34,7 +34,7 @@ public static boolean isActive() { * 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) { + public static int serialNum(long tagId) { return (int) ((tagId >>> 48) & 0x7FFF); } @@ -107,13 +107,13 @@ public static int fieldDecl(long tagId) { /** 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; + int serialNum = serialNum(tagId); + return serialNum > 0 && serialNum < FIRST_STORED_SERIAL; } /** True if the tagId names a generated, map-stored (slotted/bucketed) tag. */ public static boolean isStored(long tagId) { - return globalSerial(tagId) >= FIRST_STORED_SERIAL; + return serialNum(tagId) >= FIRST_STORED_SERIAL; } /** @@ -140,21 +140,23 @@ public static boolean isUnslotted(long tagId) { } /** - * 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. + * Builds a tagId from all three parts: {@code serialNum} (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 #serialNum}/{@link #groupDecl}/{@link #fieldDecl}. Intended for + * the code generator and tests. */ - public static long tagId(int globalSerial, int groupDecl, int fieldDecl) { - return ((long) globalSerial << 48) + public static long makeTagId(int serialNum, int groupDecl, int fieldDecl) { + return ((long) serialNum << 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 in group 0 — shorthand for {@link #makeTagId(int, int, int)} with group-decl 0. + */ + public static long makeTagId(int serialNum, int fieldDecl) { + return makeTagId(serialNum, 0, fieldDecl); } /** @@ -162,8 +164,8 @@ public static long tagId(int globalSerial, int fieldDecl) { * 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) { - return tagId(globalSerial, NO_SLOT); + public static long makeTagId(int serialNum) { + return makeTagId(serialNum, 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 deleted file mode 100644 index c1e6ccd4a49..00000000000 --- a/internal-api/src/main/java/datadog/trace/api/KnownTags.java +++ /dev/null @@ -1,500 +0,0 @@ -package datadog.trace.api; - -import datadog.trace.util.StringIndex; - -// 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 { - 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; - - private static final String[] KEYOF_NAMES = { - "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, - 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 int[] KEYOF_HASHES; - private static final String[] KEYOF_KEYS; - private static final long[] KEYOF_IDS; - - static { - StringIndex.Data data = StringIndex.EmbeddingSupport.create(KEYOF_NAMES); - long[] ids = new long[data.names.length]; - for (int j = 0; j < KEYOF_NAMES.length; j++) { - ids[StringIndex.EmbeddingSupport.indexOf(data.hashes, data.names, KEYOF_NAMES[j])] = - KEYOF_VALUES[j]; - } - KEYOF_HASHES = data.hashes; - KEYOF_KEYS = data.names; - KEYOF_IDS = ids; - } - - static final KnownTagCodec.Resolver RESOLVER = - new KnownTagCodec.Resolver() { - @Override - public String nameOf(long tagId) { - switch (KnownTagCodec.globalSerial(tagId)) { - case ERROR_SERIAL: - 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 "version"; - case ENV_SERIAL: - 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 "component"; - case SPAN_KIND_SERIAL: - 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 "db.type"; - case DB_INSTANCE_SERIAL: - return "db.instance"; - case DB_OPERATION_SERIAL: - return "db.operation"; - case DB_USER_SERIAL: - return "db.user"; - case DB_POOL_NAME_SERIAL: - 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; - } - } - - @Override - public int slotCount() { - return SLOT_COUNT; - } - - @Override - public long keyOf(String name) { - int slot = StringIndex.EmbeddingSupport.indexOf(KEYOF_HASHES, KEYOF_KEYS, name); - return slot < 0 ? 0L : KEYOF_IDS[slot]; - } - }; - - static { - KnownTagCodec.register(RESOLVER); - } - - /** 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 28732f2fc00..8ff476b4df6 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -1066,7 +1066,7 @@ public EntryChange next() { * 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}). + * {@link #parentDenseVisible}). *

  • 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. 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 aaffecf8e8c..0114d9ba122 100644 --- a/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java +++ b/internal-api/src/test/java/datadog/trace/api/KnownTagsTest.java @@ -29,33 +29,33 @@ class KnownTagsTest { /** (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), + Arguments.of(Tags.ERROR, KnownTags.ERROR_ID), 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)); + Arguments.of(DDTags.BASE_SERVICE, KnownTags.DD_BASE_SERVICE_ID), + Arguments.of(Tags.VERSION, KnownTags.VERSION_ID), + Arguments.of("env", KnownTags.ENV_ID), + Arguments.of(DDTags.DJM_ENABLED, KnownTags.DD_DJM_ENABLED_ID), + Arguments.of(DDTags.DSM_ENABLED, KnownTags.DD_DSM_ENABLED_ID), + Arguments.of(DDTags.TRACER_HOST, KnownTags.DD_TRACER_HOST_ID), + Arguments.of(DDTags.DD_INTEGRATION, KnownTags.DD_INTEGRATION_ID), + Arguments.of(DDTags.DD_SVC_SRC, KnownTags.DD_SVC_SRC_ID), + Arguments.of(Tags.PEER_SERVICE, KnownTags.PEER_SERVICE_ID), + Arguments.of(DDTags.PEER_SERVICE_REMAPPED_FROM, KnownTags.DD_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_IPV4_ID), + Arguments.of(Tags.PEER_HOST_IPV6, KnownTags.PEER_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)); } /** @@ -63,11 +63,11 @@ static Stream knownTags() { */ static Stream interceptedTags() { return Stream.of( - Arguments.of(KnownTags.ERROR), - Arguments.of(KnownTags.PEER_SERVICE), - Arguments.of(KnownTags.HTTP_METHOD), - Arguments.of(KnownTags.HTTP_URL), - Arguments.of(KnownTags.SPAN_KIND)); + 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)); } @BeforeAll @@ -125,18 +125,18 @@ void unknownNamesResolveToZero() { @Test void unknownIdsResolveToNullName() { assertNull(KnownTagCodec.nameOf(0L)); - assertNull(KnownTagCodec.nameOf(KnownTagCodec.tagId(9999))); // serial with no assigned tag + assertNull(KnownTagCodec.nameOf(KnownTagCodec.makeTagId(9999))); // serial with no assigned tag } @Test void errorIsReservedTheRestAreStored() { - assertTrue(KnownTagCodec.isReserved(KnownTags.ERROR), "ERROR reserved"); - assertFalse(KnownTagCodec.isStored(KnownTags.ERROR), "ERROR not stored"); + assertTrue(KnownTagCodec.isReserved(KnownTags.ERROR_ID), "ERROR reserved"); + assertFalse(KnownTagCodec.isStored(KnownTags.ERROR_ID), "ERROR not stored"); knownTags() .forEach( a -> { long id = (Long) a.get()[1]; - if (id != KnownTags.ERROR) { + if (id != KnownTags.ERROR_ID) { assertTrue(KnownTagCodec.isStored(id), "stored: " + a.get()[0]); assertFalse(KnownTagCodec.isReserved(id), "not reserved: " + a.get()[0]); } @@ -146,7 +146,7 @@ void errorIsReservedTheRestAreStored() { @Test void globalSerialsAreUnique() { List serials = new ArrayList<>(); - knownTags().forEach(a -> serials.add((long) KnownTagCodec.globalSerial((Long) a.get()[1]))); + knownTags().forEach(a -> serials.add((long) KnownTagCodec.serialNum((Long) a.get()[1]))); assertEquals(serials.size(), new HashSet<>(serials).size(), "globalSerials must be unique"); } } 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 72e88ccd627..f1c8a744d4b 100644 --- a/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java +++ b/internal-api/src/test/java/datadog/trace/api/TagMapDenseFuzzForkedTest.java @@ -58,14 +58,14 @@ 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, n); + return KnownTagCodec.makeTagId(KnownTagCodec.FIRST_STORED_SERIAL + n, n); } return 0L; } @Override public String nameOf(long tagId) { - int serial = KnownTagCodec.globalSerial(tagId); + int serial = KnownTagCodec.serialNum(tagId); return serial >= KnownTagCodec.FIRST_STORED_SERIAL ? "known-" + (serial - KnownTagCodec.FIRST_STORED_SERIAL) : null; diff --git a/tag-conventions.java.yaml b/tag-conventions.java.yaml new file mode 100644 index 00000000000..ab28b64545a --- /dev/null +++ b/tag-conventions.java.yaml @@ -0,0 +1,36 @@ +# dd-trace-java overlay — impl hints + special-tag registry. +# Consumed alongside the language-agnostic tag-conventions.yaml. Keyed by tag name +# (these are tag-intrinsic, not per-type). Only exceptions are listed. +# --------------------------------------------------------------------------- +# NOTE: the id coordinate (group-decl / field-decl) is NOT here — the generator assigns it from the +# declaration groups; the dense-vs-bucket split derives from the domain `required` level. + +# Tags whose set-path is handled by the Java TagInterceptor (side-effecting, but still stored). +# Generated ids carry the intercepted flag (bit 63). +intercepted: + - span.kind + - http.method + - http.url + - servlet.context + - db.statement + - peer.service + +# Reserved / special keys: accepted by the public setTag(...) API but ROUTED to a span field or a +# trace directive instead of tag storage. Reserved-tier ids (serial < FIRST_STORED_SERIAL, no slot). +# "Reserved" names the shared mechanism (the tracer reserves the key and handles it); the two kinds +# split on whether a value exists: structural has one (in a span/trace field), directive has none. +# The generated id->handler dispatch table is the data-driven replacement for the imperative +# TagInterceptor chain. +# kind: structural -> sets a span/trace field (`field:` names it) +# kind: directive -> triggers sampling/trace behavior +reserved: + - { tag: error, kind: structural, field: error } + - { tag: service, kind: structural, field: service, aliases: [service.name] } + - { tag: resource.name, kind: structural, field: resource } + - { tag: span.type, kind: structural, field: type } + - { tag: origin, kind: structural, field: origin } # trace-level field + - { tag: sampling.priority, kind: directive } + - { tag: manual.keep, kind: directive } + - { tag: manual.drop, kind: directive } + - { tag: measured, kind: directive } + - { tag: analytics.sample_rate, kind: directive } # legacy diff --git a/tag-conventions.yaml b/tag-conventions.yaml new file mode 100644 index 00000000000..2eff24f1784 --- /dev/null +++ b/tag-conventions.yaml @@ -0,0 +1,134 @@ +# Tag conventions — LANGUAGE-AGNOSTIC domain spec (structure + semantics only) +# --------------------------------------------------------------------------- +# The code generator consumes THIS file (domain) plus a per-language overlay +# (tag-conventions..yaml: impl hints like `intercepted`, and the reserved/ +# special-tag registry) to emit each language's tag-id constants, id<->name +# resolver, and slot (bitmask-bit) assignment. +# +# 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). +# +# tag fields (DOMAIN only): tag | type (string|int|long|boolean|double) +# | required (required|conditional|recommended|optional|opt_in) | aliases. +# 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. +trace_level: + tags: + - { tag: _dd.base_service, type: string, required: required } + - { tag: version, type: string, required: recommended } + - { tag: env, type: string, required: recommended } + - { tag: language, type: string, required: required } + - { tag: runtime-id, type: string, required: required } + - { tag: _dd.tracer_host, type: string, required: recommended } + - { tag: _dd.git.commit.sha, type: string, required: recommended } + - { tag: _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. + - { tag: _dd.profiling.enabled, type: boolean, required: recommended } + - { tag: _dd.dsm.enabled, type: boolean, required: recommended } + - { tag: _dd.appsec.enabled, type: boolean, required: recommended } + - { tag: _dd.djm.enabled, type: boolean, required: recommended } + - { tag: _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: + - { tag: _dd.parent_id, type: string, required: required } + - { tag: component, type: string, required: required } + - { tag: span.kind, type: string, required: required } + - { tag: _dd.integration, type: string, required: recommended } + - { tag: _dd.svc_src, type: string, required: optional } + - { tag: error.type, type: string, required: recommended } + - { tag: error.message, type: string, required: recommended } + - { tag: error.stack, type: string, required: recommended } + + http: + abstract: true + extends: base + tags: + - { tag: http.method, type: string, required: required, aliases: [http.request.method] } + - { tag: http.status_code, type: int, required: conditional, aliases: [http.response.status_code] } + - { tag: network.protocol.version, type: string, required: recommended } + + http.server: + extends: http + tags: + - { tag: http.url, type: string, required: required, aliases: [url.full] } + - { tag: http.route, type: string, required: conditional } + - { tag: http.hostname, type: string, required: required, aliases: [server.address] } + - { tag: http.useragent, type: string, required: recommended } + - { tag: http.query.string, type: string, required: recommended, aliases: [url.query] } + - { tag: servlet.path, type: string, required: optional } + - { tag: servlet.context, type: string, required: optional } + + http.client: + extends: http + include: [ peer ] + tags: + - { tag: http.url, type: string, required: required, aliases: [url.full] } + - { tag: http.resend_count, type: int, required: recommended } + + db.client: + extends: base + include: [ peer ] + tags: + - { tag: db.type, type: string, required: required, aliases: [db.system] } + - { tag: db.instance, type: string, required: recommended } + - { tag: db.operation, type: string, required: recommended, aliases: [db.operation.name] } + - { tag: db.user, type: string, required: recommended } + - { tag: db.pool.name, type: string, required: optional } + - { tag: db.statement, type: string, required: recommended, aliases: [db.query.text] } + + view.render: + extends: base + tags: + - { tag: view.name, type: string, required: recommended } + +mixins: + # peer — outbound/remote-peer capability, PULLED via `include` by client span types. + peer: + tags: + - { tag: peer.service, type: string, required: recommended } + - { tag: _dd.peer.service.source, type: string, required: recommended } + - { tag: _dd.peer.service.remapped_from, type: string, required: recommended } + - { tag: peer.hostname, type: string, required: recommended } + - { tag: peer.ipv4, type: string } + - { tag: peer.ipv6, type: string } + - { tag: peer.port, type: int } + + # 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 (not modeled here yet). + ci_visibility: + enabled_by: dd.civisibility.enabled + applies: [ test ] + tags: + - { tag: test.name, type: string, required: recommended } + - { tag: test.suite, type: string, required: recommended } + - { tag: test.status, type: string, required: recommended } + - { tag: 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. +# - Reserved/special keys (service, resource.name, error, sampling.priority, ...) route to span +# fields/directives, not tag storage — they live in the per-language overlay, not here. +# ---------------------------------------------------------------------------