Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package datadog.trace.core.otlp.trace;

import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DD_MEASURED;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DD_PARTIAL_VERSION;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DD_TOP_LEVEL;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DD_WAS_LONG_RUNNING;
import static datadog.trace.common.writer.ddagent.TraceMapper.ORIGIN_KEY;
import static datadog.trace.common.writer.ddagent.TraceMapper.PROCESS_TAGS_KEY;
import static datadog.trace.common.writer.ddagent.TraceMapper.SAMPLING_PRIORITY_KEY;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_ID;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_NAME;

import datadog.trace.api.KnownTagCodec;
import datadog.trace.api.KnownTags;
import datadog.trace.api.TagMap;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/** Span attribute keys shared by the OTLP protobuf and JSON trace writers. */
final class OtlpSpanAttributes {

static final UTF8BytesString RESOURCE_NAME = UTF8BytesString.create("resource.name");
static final UTF8BytesString OPERATION_NAME = UTF8BytesString.create("operation.name");
static final UTF8BytesString SPAN_TYPE = UTF8BytesString.create("span.type");

/*
* Span fields and intercepted Metadata fields bypass TagMap entry projection. Resolve their
* keys through the registry here so OpenTelemetry names stay consistent with entry tags.
*/
static final UTF8BytesString SERVICE_NAME_KEY = otelKey(KnownTags.SERVICE_ID);

static final UTF8BytesString HTTP_STATUS_CODE_KEY = otelKey(KnownTags.HTTP_STATUS_CODE_ID);

static final UTF8BytesString HTTP_STATUS_CODE_KEY_DD =
UTF8BytesString.create(KnownTags.HTTP_STATUS_CODE_NAME);

/**
* Keys that baggage must not export as attributes: the attributes the writers emit directly from
* span fields and intercepted Metadata fields (the span's own value wins), plus _dd.p.tid, whose
* upper 64 bits the OTLP traceId already carries.
*/
static final Set<String> SKIPPED_BAGGAGE_KEYS =
new HashSet<>(
Arrays.asList(
"_dd.p.tid",
SERVICE_NAME_KEY.toString(),
RESOURCE_NAME.toString(),
OPERATION_NAME.toString(),
SPAN_TYPE.toString(),
SAMPLING_PRIORITY_KEY.toString(),
DD_MEASURED.toString(),
DD_TOP_LEVEL.toString(),
DD_PARTIAL_VERSION.toString(),
DD_WAS_LONG_RUNNING.toString(),
THREAD_ID.toString(),
THREAD_NAME.toString(),
HTTP_STATUS_CODE_KEY.toString(),
HTTP_STATUS_CODE_KEY_DD.toString(),
ORIGIN_KEY.toString(),
PROCESS_TAGS_KEY.toString()));

/**
* True if a span tag is exported under exactly {@code key}, so baggage under that key would
* duplicate it. TagMap lookups resolve aliases (http.request.method finds http.method), so the
* matching entry's exported name is compared with the key: it depends on OTel semantics and, for
* direction-scoped tags, on the span's direction (peer.port is server.port on a client).
*/
static boolean isExportedTagKey(TagMap tags, String key, int direction, boolean otelSemantics) {
if (exportsAs(tags.getEntry(key), key, direction, otelSemantics)) {
return true;
}
if (otelSemantics) {
long directionalTagId = KnownTagCodec.directionalKeyOf(key, direction);
return directionalTagId != 0
&& exportsAs(tags.getEntry(directionalTagId), key, direction, true);
}
return false;
}

private static boolean exportsAs(
TagMap.Entry entry, String key, int direction, boolean otelSemantics) {
return entry != null
&& key.equals(otelSemantics ? entry.openTelemetryTag(direction) : entry.tag());
}

/** The OpenTelemetry-namespace key for a known tag, as named by the registry. */
private static UTF8BytesString otelKey(long tagId) {
return UTF8BytesString.create(KnownTagCodec.openTelemetryTagOf(tagId));
}

private OtlpSpanAttributes() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,27 @@
import static datadog.trace.common.writer.ddagent.TraceMapper.SAMPLING_PRIORITY_KEY;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_ID;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_NAME;
import static datadog.trace.core.CoreSpan.OWN_PROPAGATION_TAGS;
import static datadog.trace.core.CoreSpan.ROOT_PROPAGATION_TAGS;
import static datadog.trace.core.otlp.common.OtlpCommonJson.hexSpanId;
import static datadog.trace.core.otlp.common.OtlpCommonJson.hexTraceId;
import static datadog.trace.core.otlp.common.OtlpCommonJson.writeAttribute;
import static datadog.trace.core.otlp.common.OtlpTraceFlags.NO_TRACE_FLAGS;
import static datadog.trace.core.otlp.common.OtlpTraceFlags.REMOTE_TRACE_FLAG;
import static datadog.trace.core.otlp.common.OtlpTraceFlags.SAMPLED_TRACE_FLAG;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.HTTP_STATUS_CODE_KEY;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.HTTP_STATUS_CODE_KEY_DD;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.OPERATION_NAME;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.RESOURCE_NAME;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SERVICE_NAME_KEY;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SKIPPED_BAGGAGE_KEYS;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SPAN_TYPE;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.isExportedTagKey;
import static datadog.trace.core.otlp.trace.OtlpSpanKind.spanKind;

import datadog.json.JsonWriter;
import datadog.trace.api.Config;
import datadog.trace.api.DDTags;
import datadog.trace.api.KnownTagCodec;
import datadog.trace.api.KnownTags;
import datadog.trace.api.TagMap;
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
Expand All @@ -43,27 +50,6 @@
/** Provides writers for OpenTelemetry's "trace.proto" JSON encoding. */
public final class OtlpTraceJson {

private static final UTF8BytesString RESOURCE_NAME = UTF8BytesString.create("resource.name");
private static final UTF8BytesString OPERATION_NAME = UTF8BytesString.create("operation.name");
private static final UTF8BytesString SPAN_TYPE = UTF8BytesString.create("span.type");

/*
* Span fields and intercepted Metadata fields bypass TagMap entry projection. Resolve their
* keys through the registry here so JSON and protobuf use the same OpenTelemetry names.
*/
private static final UTF8BytesString SERVICE_NAME_KEY = otelKey(KnownTags.SERVICE_ID);

private static final UTF8BytesString HTTP_STATUS_CODE_KEY =
otelKey(KnownTags.HTTP_STATUS_CODE_ID);

private static final UTF8BytesString HTTP_STATUS_CODE_KEY_DD =
UTF8BytesString.create(KnownTags.HTTP_STATUS_CODE_NAME);

/** The OpenTelemetry-namespace key for a known tag, as named by the registry. */
private static UTF8BytesString otelKey(long tagId) {
return UTF8BytesString.create(KnownTagCodec.openTelemetryTagOf(tagId));
}

private OtlpTraceJson() {}

/** Writes one complete {@code Span} JSON object. */
Expand Down Expand Up @@ -113,7 +99,10 @@ public static void writeSpan(
if (span.getSpanType() != null) {
writeSpanTag(writer, SPAN_TYPE, span.getSpanType());
}
span.processTagsAndBaggage(metaWriter, OWN_PROPAGATION_TAGS);
// Export root propagation tags on the span selected by the collector for sampling tags,
// including when the exported trace contains no local root span.
span.processTagsAndBaggage(
metaWriter, metaWriter.includeSamplingTags ? ROOT_PROPAGATION_TAGS : 0);
writer.endArray();

if (!links.isEmpty()) {
Expand Down Expand Up @@ -252,9 +241,23 @@ public void accept(Metadata metadata) {
writeSpanTag(writer, PROCESS_TAGS_KEY, metadata.processTags());
}

// The direction is computed once per span; boxing -1..2 hits the Integer cache.
TagMap tags = metadata.getTags();
// The direction is computed once per span; boxing -1..2 hits the Integer cache.
Integer direction = KnownTagCodec.directionOf(tags);
boolean otelSemantics = Config.get().isTraceOtelSemanticsEnabled();

// Baggage carries the propagation tags (e.g. the _dd.p.dm sampling decision maker) and any
// baggage items injected as tags, as in the Datadog mappers. Span tags and the attributes
// written directly above win when they are exported under the same key, which keeps OTLP
// attribute keys unique without dropping baggage whose exported key differs.
for (Map.Entry<String, String> entry : metadata.getBaggage().entrySet()) {
if (SKIPPED_BAGGAGE_KEYS.contains(entry.getKey())
|| isExportedTagKey(tags, entry.getKey(), direction, otelSemantics)) {
continue;
}
writeAttribute(writer, STRING_ATTRIBUTE, entry.getKey(), entry.getValue());
}

tags.forEach(writer, direction, OtlpTraceJson::writeSpanTag);
Comment thread
mcculls marked this conversation as resolved.
Resolved

// reset for next span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import static datadog.trace.common.writer.ddagent.TraceMapper.SAMPLING_PRIORITY_KEY;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_ID;
import static datadog.trace.common.writer.ddagent.TraceMapper.THREAD_NAME;
import static datadog.trace.core.CoreSpan.OWN_PROPAGATION_TAGS;
import static datadog.trace.core.CoreSpan.ROOT_PROPAGATION_TAGS;
import static datadog.trace.core.otlp.common.OtlpCommonProto.I32_WIRE_TYPE;
import static datadog.trace.core.otlp.common.OtlpCommonProto.I64_WIRE_TYPE;
import static datadog.trace.core.otlp.common.OtlpCommonProto.LEN_WIRE_TYPE;
Expand All @@ -30,6 +30,14 @@
import static datadog.trace.core.otlp.common.OtlpTraceFlags.NO_TRACE_FLAGS;
import static datadog.trace.core.otlp.common.OtlpTraceFlags.REMOTE_TRACE_FLAG;
import static datadog.trace.core.otlp.common.OtlpTraceFlags.SAMPLED_TRACE_FLAG;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.HTTP_STATUS_CODE_KEY;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.HTTP_STATUS_CODE_KEY_DD;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.OPERATION_NAME;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.RESOURCE_NAME;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SERVICE_NAME_KEY;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SKIPPED_BAGGAGE_KEYS;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.SPAN_TYPE;
import static datadog.trace.core.otlp.trace.OtlpSpanAttributes.isExportedTagKey;
import static datadog.trace.core.otlp.trace.OtlpSpanKind.spanKind;
import static java.nio.charset.StandardCharsets.UTF_8;

Expand All @@ -39,7 +47,6 @@
import datadog.trace.api.DDTags;
import datadog.trace.api.DDTraceId;
import datadog.trace.api.KnownTagCodec;
import datadog.trace.api.KnownTags;
import datadog.trace.api.TagMap;
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
Expand All @@ -51,31 +58,11 @@
import datadog.trace.core.otlp.common.OtlpProtoBuffer;
import datadog.trace.core.propagation.PropagationTags;
import datadog.trace.core.propagation.PropagationTags.SamplingState;
import java.util.Map;

/** Provides optimized writers for OpenTelemetry's "trace.proto" wire protocol. */
public final class OtlpTraceProto {

private static final UTF8BytesString RESOURCE_NAME = UTF8BytesString.create("resource.name");
private static final UTF8BytesString OPERATION_NAME = UTF8BytesString.create("operation.name");
private static final UTF8BytesString SPAN_TYPE = UTF8BytesString.create("span.type");

/*
* Span fields and intercepted Metadata fields bypass TagMap entry projection. Resolve their
* keys through the registry here so OpenTelemetry names stay consistent with entry tags.
*/
private static final UTF8BytesString SERVICE_NAME_KEY = otelKey(KnownTags.SERVICE_ID);

private static final UTF8BytesString HTTP_STATUS_CODE_KEY =
otelKey(KnownTags.HTTP_STATUS_CODE_ID);

private static final UTF8BytesString HTTP_STATUS_CODE_KEY_DD =
UTF8BytesString.create(KnownTags.HTTP_STATUS_CODE_NAME);

/** The OpenTelemetry-namespace key for a known tag, as named by the registry. */
private static UTF8BytesString otelKey(long tagId) {
return UTF8BytesString.create(KnownTagCodec.openTelemetryTagOf(tagId));
}

private OtlpTraceProto() {}

/** Records a scoped spans message after its nested span messages have been recorded. */
Expand Down Expand Up @@ -160,7 +147,10 @@ public static int recordSpanMessage(
writeSpanTag(buf, SPAN_TYPE, span.getSpanType());
}

span.processTagsAndBaggage(metaWriter, OWN_PROPAGATION_TAGS);
// Export root propagation tags on the span selected by the collector for sampling tags,
// including when the exported trace contains no local root span.
span.processTagsAndBaggage(
metaWriter, metaWriter.includeSamplingTags ? ROOT_PROPAGATION_TAGS : 0);

if (span.isError()) {
int stateSize = 2;
Expand Down Expand Up @@ -329,9 +319,24 @@ public void accept(Metadata metadata) {
writeSpanTag(buf, PROCESS_TAGS_KEY, metadata.processTags());
}

// The direction is computed once per span; boxing -1..2 hits the Integer cache.
TagMap tags = metadata.getTags();
// The direction is computed once per span; boxing -1..2 hits the Integer cache.
Integer direction = KnownTagCodec.directionOf(tags);
boolean otelSemantics = Config.get().isTraceOtelSemanticsEnabled();

// Baggage carries the propagation tags (e.g. the _dd.p.dm sampling decision maker) and any
// baggage items injected as tags, as in the Datadog mappers. Span tags and the attributes
// written directly above win when they are exported under the same key, which keeps OTLP
// attribute keys unique without dropping baggage whose exported key differs.
for (Map.Entry<String, String> entry : metadata.getBaggage().entrySet()) {
if (SKIPPED_BAGGAGE_KEYS.contains(entry.getKey())
|| isExportedTagKey(tags, entry.getKey(), direction, otelSemantics)) {
continue;
}
writeTag(buf, 9, LEN_WIRE_TYPE);
writeAttribute(buf, STRING_ATTRIBUTE, entry.getKey(), entry.getValue());
}

tags.forEach(buf, direction, OtlpTraceProto::writeSpanTag);

// reset for next span
Expand Down
Loading
Loading