Skip to content

Commit

Permalink
Translate http.response.status_code (OpenTelemetry) to http.status_co…
Browse files Browse the repository at this point in the history
…de (Datadog) (#7138)
  • Loading branch information
mcculls committed Jun 7, 2024
1 parent bff441b commit fff0006
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public final class OtelConventions {
static final String SPAN_KIND_INTERNAL = "internal";
static final String OPERATION_NAME_SPECIFIC_ATTRIBUTE = "operation.name";
static final String ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES = "analytics.event";
static final String HTTP_RESPONSE_STATUS_CODE_ATTRIBUTE = "http.response.status_code";

private static final Logger LOGGER = LoggerFactory.getLogger(OtelConventions.class);

private OtelConventions() {}
Expand Down Expand Up @@ -104,6 +106,11 @@ public static <T> boolean applyReservedAttribute(AgentSpan span, AttributeKey<T>
span.setMetric(ANALYTICS_SAMPLE_RATE, ((Boolean) value) ? 1 : 0);
return true;
}
case LONG:
if (HTTP_RESPONSE_STATUS_CODE_ATTRIBUTE.equals(name) && value instanceof Number) {
span.setHttpStatusCode(((Number) value).intValue());
return true;
}
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package datadog.opentelemetry.shim.trace;

import static datadog.opentelemetry.shim.trace.OtelConventions.ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES;
import static datadog.opentelemetry.shim.trace.OtelConventions.HTTP_RESPONSE_STATUS_CODE_ATTRIBUTE;
import static datadog.opentelemetry.shim.trace.OtelConventions.OPERATION_NAME_SPECIFIC_ATTRIBUTE;
import static datadog.opentelemetry.shim.trace.OtelConventions.toSpanKindTagValue;
import static datadog.opentelemetry.shim.trace.OtelExtractedContext.extract;
Expand Down Expand Up @@ -40,12 +41,19 @@ public class OtelSpanBuilder implements SpanBuilder {
* set).
*/
private int overriddenAnalyticsSampleRate;
/**
* HTTP status code overridden value by {@link
* OtelConventions#HTTP_RESPONSE_STATUS_CODE_ATTRIBUTE} reserved attribute ({@code -1} if not
* set).
*/
private int overriddenHttpStatusCode;

public OtelSpanBuilder(AgentTracer.SpanBuilder delegate) {
this.delegate = delegate;
this.spanKindSet = false;
this.overriddenOperationName = null;
this.overriddenAnalyticsSampleRate = -1;
this.overriddenHttpStatusCode = -1;
}

@Override
Expand Down Expand Up @@ -98,6 +106,11 @@ public SpanBuilder setAttribute(String key, String value) {

@Override
public SpanBuilder setAttribute(String key, long value) {
// Check reserved attributes
if (HTTP_RESPONSE_STATUS_CODE_ATTRIBUTE.equals(key)) {
this.overriddenHttpStatusCode = (int) value;
return this;
}
this.delegate.withTag(key, value);
return this;
}
Expand All @@ -121,7 +134,28 @@ public SpanBuilder setAttribute(String key, boolean value) {

@Override
public <T> SpanBuilder setAttribute(AttributeKey<T> key, T value) {
String name = key.getKey();
switch (key.getType()) {
case STRING:
if (value instanceof String) {
setAttribute(name, (String) value);
break;
}
case BOOLEAN:
if (value instanceof Boolean) {
setAttribute(name, ((Boolean) value).booleanValue());
break;
}
case LONG:
if (value instanceof Number) {
setAttribute(name, ((Number) value).longValue());
break;
}
case DOUBLE:
if (value instanceof Number) {
setAttribute(name, ((Number) value).doubleValue());
break;
}
case STRING_ARRAY:
case BOOLEAN_ARRAY:
case LONG_ARRAY:
Expand All @@ -130,16 +164,16 @@ public <T> SpanBuilder setAttribute(AttributeKey<T> key, T value) {
List<?> valueList = (List<?>) value;
if (valueList.isEmpty()) {
// Store as object to prevent delegate to remove tag when value is empty
this.delegate.withTag(key.getKey(), (Object) "");
this.delegate.withTag(name, (Object) "");
} else {
for (int index = 0; index < valueList.size(); index++) {
this.delegate.withTag(key.getKey() + "." + index, valueList.get(index));
this.delegate.withTag(name + "." + index, valueList.get(index));
}
}
}
break;
default:
this.delegate.withTag(key.getKey(), value);
this.delegate.withTag(name, value);
break;
}
return this;
Expand Down Expand Up @@ -181,6 +215,9 @@ public Span startSpan() {
if (this.overriddenAnalyticsSampleRate != -1) {
delegate.setMetric(ANALYTICS_SAMPLE_RATE, this.overriddenAnalyticsSampleRate);
}
if (this.overriddenHttpStatusCode != -1) {
delegate.setHttpStatusCode(this.overriddenHttpStatusCode);
}
return new OtelSpan(delegate);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDTags
import datadog.trace.bootstrap.instrumentation.api.Tags
import io.opentelemetry.api.GlobalOpenTelemetry
import io.opentelemetry.context.Context
import io.opentelemetry.context.ThreadLocalContextStorage
Expand All @@ -9,6 +10,7 @@ import static datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND
import static datadog.opentelemetry.shim.trace.OtelConventions.OPERATION_NAME_SPECIFIC_ATTRIBUTE
import static datadog.opentelemetry.shim.trace.OtelConventions.SPAN_KIND_INTERNAL
import static datadog.opentelemetry.shim.trace.OtelConventions.toSpanKindTagValue
import static io.opentelemetry.api.common.AttributeKey.longKey
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
Expand Down Expand Up @@ -191,6 +193,65 @@ class OpenTelemetry14ConventionsTest extends AgentTestRunner {
false | "" | 0
}

def "test span http.response.status_code specific tag"() {
setup:
def builder = tracer.spanBuilder("some-name")

when:
if (setInBuilder) {
if (attributeKey) {
builder.setAttribute(longKey("http.response.status_code"), value)
} else {
builder.setAttribute("http.response.status_code", value)
}
}
def result = builder.startSpan()
if (!setInBuilder) {
if (attributeKey) {
result.setAttribute(longKey("http.response.status_code"), value)
} else {
result.setAttribute("http.response.status_code", value)
}
}
result.end()

then:
assertTraces(1) {
trace(1) {
span {
parent()
operationName "internal"
tags {
defaultTags()
"$SPAN_KIND" "$SPAN_KIND_INTERNAL"
if (value != null) {
"$Tags.HTTP_STATUS" expectedStatus
}
}
}
}
}

where:
setInBuilder | attributeKey | value | expectedStatus
true | false | null | 0 // Not used
true | false | 200 | 200
true | false | 404L | 404
true | false | 500 as Long | 500
false | false | null | 0 // Not used
false | false | 200 | 200
false | false | 404L | 404
false | false | 500 as Long | 500
true | true | null | 0 // Not used
true | true | 200 | 200
true | true | 404L | 404
true | true | 500 as Long | 500
false | true | null | 0 // Not used
false | true | 200 | 200
false | true | 404L | 404
false | true | 500 as Long | 500
}

@Override
void cleanup() {
// Test for context leak
Expand Down

0 comments on commit fff0006

Please sign in to comment.