Skip to content

Commit ce9ed38

Browse files
authored
Add content type to additional data spans (#388)
* add conetnt type to additional data spans * add test * add content type to vertx additional-data spans * get the method on startup * spotless apply
1 parent c9cb1fe commit ce9ed38

File tree

5 files changed

+154
-12
lines changed

5 files changed

+154
-12
lines changed

instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
import io.opentelemetry.api.GlobalOpenTelemetry;
2020
import io.opentelemetry.api.common.AttributeKey;
2121
import io.opentelemetry.api.trace.Span;
22+
import io.opentelemetry.api.trace.SpanBuilder;
2223
import io.opentelemetry.api.trace.Tracer;
2324
import io.opentelemetry.context.Context;
2425
import io.opentelemetry.instrumentation.api.util.VirtualField;
2526
import java.io.ByteArrayOutputStream;
2627
import java.io.IOException;
2728
import java.io.InputStream;
2829
import java.io.UnsupportedEncodingException;
30+
import java.lang.reflect.InvocationTargetException;
31+
import java.lang.reflect.Method;
2932
import java.nio.charset.Charset;
3033
import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap;
3134
import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes;
@@ -42,6 +45,23 @@ private InputStreamUtils() {}
4245
private static final Tracer TRACER =
4346
GlobalOpenTelemetry.get().getTracer("org.hypertrace.java.inputstream");
4447

48+
private static Method getAttribute = null;
49+
50+
static {
51+
try {
52+
getAttribute =
53+
Class.forName("io.opentelemetry.sdk.trace.SdkSpan")
54+
.getDeclaredMethod("getAttribute", AttributeKey.class);
55+
} catch (NoSuchMethodException e) {
56+
log.error("getAttribute method not found in SdkSpan class", e);
57+
} catch (ClassNotFoundException e) {
58+
log.error("SdkSpan class not found", e);
59+
}
60+
if (getAttribute != null) {
61+
getAttribute.setAccessible(true);
62+
}
63+
}
64+
4565
/**
4666
* Adds an attribute to span. If the span is ended it adds the attributed to a newly created
4767
* child.
@@ -50,12 +70,34 @@ public static void addAttribute(Span span, AttributeKey<String> attributeKey, St
5070
if (span.isRecording()) {
5171
span.setAttribute(attributeKey, value);
5272
} else {
53-
TRACER
54-
.spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME)
55-
.setParent(Context.root().with(span))
56-
.setAttribute(attributeKey, value)
57-
.startSpan()
58-
.end();
73+
SpanBuilder spanBuilder =
74+
TRACER
75+
.spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME)
76+
.setParent(Context.root().with(span))
77+
.setAttribute(attributeKey, value);
78+
79+
// Also add content type if present
80+
if (getAttribute != null
81+
&& span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) {
82+
try {
83+
Object reqContentType =
84+
getAttribute.invoke(
85+
span, HypertraceSemanticAttributes.HTTP_REQUEST_HEADER_CONTENT_TYPE);
86+
if (reqContentType != null) {
87+
spanBuilder.setAttribute("http.request.header.content-type", (String) reqContentType);
88+
}
89+
Object resContentType =
90+
getAttribute.invoke(
91+
span, HypertraceSemanticAttributes.HTTP_RESPONSE_HEADER_CONTENT_TYPE);
92+
if (resContentType != null) {
93+
spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType);
94+
}
95+
} catch (IllegalAccessException | InvocationTargetException e) {
96+
// ignore and continue
97+
log.debug("Could not invoke getAttribute on SdkSpan", e);
98+
}
99+
}
100+
spanBuilder.startSpan().end();
59101
}
60102
}
61103

instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,51 @@ private void read(InputStream inputStream, Runnable read, String expected) {
111111
SpanData spanData = trace.get(0);
112112
Assertions.assertEquals(expected, spanData.getAttributes().get(ATTRIBUTE_KEY));
113113
}
114+
115+
@Test
116+
public void readAfterSpanEnd() {
117+
118+
InputStream inputStream = new ByteArrayInputStream(STR.getBytes());
119+
120+
Span span =
121+
TEST_TRACER
122+
.spanBuilder("test-span")
123+
.setAttribute("http.request.header.content-type", "application/json")
124+
.setAttribute("http.response.header.content-type", "application/xml")
125+
.startSpan();
126+
127+
Runnable read =
128+
() -> {
129+
while (true) {
130+
try {
131+
if (inputStream.read(new byte[10], 0, 10) == -1) break;
132+
span.end();
133+
} catch (IOException e) {
134+
e.printStackTrace();
135+
}
136+
;
137+
}
138+
};
139+
140+
BoundedByteArrayOutputStream buffer =
141+
BoundedBuffersFactory.createStream(StandardCharsets.ISO_8859_1);
142+
ContextAccessor.addToInputStreamContext(
143+
inputStream, new SpanAndBuffer(span, buffer, ATTRIBUTE_KEY, StandardCharsets.ISO_8859_1));
144+
145+
read.run();
146+
147+
List<List<SpanData>> traces = TEST_WRITER.getTraces();
148+
Assertions.assertEquals(1, traces.size());
149+
150+
List<SpanData> trace = traces.get(0);
151+
Assertions.assertEquals(2, trace.size());
152+
SpanData spanData = trace.get(1);
153+
Assertions.assertEquals(STR, spanData.getAttributes().get(ATTRIBUTE_KEY));
154+
Assertions.assertEquals(
155+
"application/json",
156+
spanData.getAttributes().get(AttributeKey.stringKey("http.request.header.content-type")));
157+
Assertions.assertEquals(
158+
"application/xml",
159+
spanData.getAttributes().get(AttributeKey.stringKey("http.response.header.content-type")));
160+
}
114161
}

instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,43 @@
1717
package io.opentelemetry.javaagent.instrumentation.hypertrace.vertx;
1818

1919
import io.opentelemetry.api.GlobalOpenTelemetry;
20+
import io.opentelemetry.api.common.AttributeKey;
2021
import io.opentelemetry.api.trace.Span;
22+
import io.opentelemetry.api.trace.SpanBuilder;
2123
import io.opentelemetry.api.trace.Tracer;
2224
import io.opentelemetry.context.Context;
2325
import io.vertx.core.Handler;
2426
import io.vertx.core.buffer.Buffer;
27+
import java.lang.reflect.InvocationTargetException;
28+
import java.lang.reflect.Method;
2529
import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
2632

2733
public class ResponseBodyWrappingHandler implements Handler<Buffer> {
2834

2935
private static final Tracer tracer =
3036
GlobalOpenTelemetry.getTracer("io.opentelemetry.javaagent.vertx-core-3.0");
3137

38+
private static final Logger log = LoggerFactory.getLogger(ResponseBodyWrappingHandler.class);
39+
40+
private static Method getAttribute = null;
41+
42+
static {
43+
try {
44+
getAttribute =
45+
Class.forName("io.opentelemetry.sdk.trace.SdkSpan")
46+
.getDeclaredMethod("getAttribute", AttributeKey.class);
47+
} catch (NoSuchMethodException e) {
48+
log.error("getAttribute method not found in SdkSpan class", e);
49+
} catch (ClassNotFoundException e) {
50+
log.error("SdkSpan class not found", e);
51+
}
52+
if (getAttribute != null) {
53+
getAttribute.setAccessible(true);
54+
}
55+
}
56+
3257
private final Handler<Buffer> wrapped;
3358
private final Span span;
3459

@@ -43,12 +68,29 @@ public void handle(Buffer event) {
4368
if (span.isRecording()) {
4469
span.setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody);
4570
} else {
46-
tracer
47-
.spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME)
48-
.setParent(Context.root().with(span))
49-
.setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody)
50-
.startSpan()
51-
.end();
71+
SpanBuilder spanBuilder =
72+
tracer
73+
.spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME)
74+
.setParent(Context.root().with(span))
75+
.setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody);
76+
77+
// Also add content type if present
78+
if (getAttribute != null
79+
&& span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) {
80+
try {
81+
Object resContentType =
82+
getAttribute.invoke(
83+
span, HypertraceSemanticAttributes.HTTP_RESPONSE_HEADER_CONTENT_TYPE);
84+
if (resContentType != null) {
85+
spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType);
86+
}
87+
} catch (IllegalAccessException | InvocationTargetException e) {
88+
// ignore and continue
89+
log.debug("Could not invoke getAttribute on SdkSpan", e);
90+
}
91+
}
92+
93+
spanBuilder.startSpan().end();
5294
}
5395

5496
wrapped.handle(event);

javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public static AttributeKey<String> httpResponseHeader(String header) {
4545
public static final AttributeKey<String> HTTP_REQUEST_SESSION_ID =
4646
AttributeKey.stringKey("http.request.session_id");
4747

48+
public static final AttributeKey<String> HTTP_REQUEST_HEADER_CONTENT_TYPE =
49+
AttributeKey.stringKey("http.request.header.content-type");
50+
51+
public static final AttributeKey<String> HTTP_RESPONSE_HEADER_CONTENT_TYPE =
52+
AttributeKey.stringKey("http.response.header.content-type");
53+
4854
public static final AttributeKey<String> RPC_REQUEST_BODY =
4955
AttributeKey.stringKey("rpc.request.body");
5056
public static final AttributeKey<String> RPC_RESPONSE_BODY =

testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.hypertrace.agent.testing;
1818

19+
import io.opentelemetry.api.common.AttributeKey;
1920
import io.opentelemetry.sdk.trace.data.SpanData;
2021
import java.io.BufferedReader;
2122
import java.io.IOException;
@@ -118,6 +119,10 @@ public void postJson_echo()
118119
Assertions.assertEquals(2, traces.get(0).size());
119120
SpanData responseBodySpan = traces.get(0).get(1);
120121
assertBodies(clientSpan, responseBodySpan, body, body);
122+
Assertions.assertNotNull(
123+
responseBodySpan
124+
.getAttributes()
125+
.get(AttributeKey.stringKey("http.response.header.content-type")));
121126
} else {
122127
Assertions.assertEquals(1, traces.get(0).size());
123128
assertRequestAndResponseBody(clientSpan, body, body);

0 commit comments

Comments
 (0)