From 50874ce076845820b6bdd63f3fb7382452bf3e77 Mon Sep 17 00:00:00 2001 From: Shashank Patidar Date: Mon, 25 Sep 2023 18:42:14 +0530 Subject: [PATCH 1/5] add conetnt type to additional data spans --- .../java/inputstream/InputStreamUtils.java | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java index 67fb84ee..0b05f1df 100644 --- a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java +++ b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java @@ -19,6 +19,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; @@ -26,6 +27,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.charset.Charset; import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; @@ -50,12 +53,34 @@ public static void addAttribute(Span span, AttributeKey attributeKey, St if (span.isRecording()) { span.setAttribute(attributeKey, value); } else { - TRACER - .spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME) - .setParent(Context.root().with(span)) - .setAttribute(attributeKey, value) - .startSpan() - .end(); + SpanBuilder spanBuilder = + TRACER + .spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME) + .setParent(Context.root().with(span)) + .setAttribute(attributeKey, value); + + // Also add content type if present + if (span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + try { + Method getAttribute = + span.getClass().getDeclaredMethod("getAttribute", AttributeKey.class); + getAttribute.setAccessible(true); + Object reqContentType = + getAttribute.invoke(span, AttributeKey.stringKey("http.request.header.content-type")); + if (reqContentType != null) { + spanBuilder.setAttribute("http.request.header.content-type", (String) reqContentType); + } + Object resContentType = + getAttribute.invoke( + span, AttributeKey.stringKey("http.response.header.content-type")); + if (resContentType != null) { + spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // ignore and continue + } + } + spanBuilder.startSpan().end(); } } From 40de66dcc21aa16e7476b7db0aef99e4c5a21297 Mon Sep 17 00:00:00 2001 From: Shashank Patidar Date: Mon, 25 Sep 2023 18:58:58 +0530 Subject: [PATCH 2/5] add test --- .../InputStreamInstrumentationModuleTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java b/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java index 4ded7ef0..c86e6086 100644 --- a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java +++ b/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java @@ -111,4 +111,51 @@ private void read(InputStream inputStream, Runnable read, String expected) { SpanData spanData = trace.get(0); Assertions.assertEquals(expected, spanData.getAttributes().get(ATTRIBUTE_KEY)); } + + @Test + public void readAfterSpanEnd() { + + InputStream inputStream = new ByteArrayInputStream(STR.getBytes()); + + Span span = + TEST_TRACER + .spanBuilder("test-span") + .setAttribute("http.request.header.content-type", "application/json") + .setAttribute("http.response.header.content-type", "application/xml") + .startSpan(); + + Runnable read = + () -> { + while (true) { + try { + if (inputStream.read(new byte[10], 0, 10) == -1) break; + span.end(); + } catch (IOException e) { + e.printStackTrace(); + } + ; + } + }; + + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.ISO_8859_1); + ContextAccessor.addToInputStreamContext( + inputStream, new SpanAndBuffer(span, buffer, ATTRIBUTE_KEY, StandardCharsets.ISO_8859_1)); + + read.run(); + + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + + List trace = traces.get(0); + Assertions.assertEquals(2, trace.size()); + SpanData spanData = trace.get(1); + Assertions.assertEquals(STR, spanData.getAttributes().get(ATTRIBUTE_KEY)); + Assertions.assertEquals( + "application/json", + spanData.getAttributes().get(AttributeKey.stringKey("http.request.header.content-type"))); + Assertions.assertEquals( + "application/xml", + spanData.getAttributes().get(AttributeKey.stringKey("http.response.header.content-type"))); + } } From e37fc3ac2c373628145bba6ee837ba87a2510758 Mon Sep 17 00:00:00 2001 From: Shashank Patidar Date: Tue, 26 Sep 2023 03:14:29 +0530 Subject: [PATCH 3/5] add content type to vertx additional-data spans --- .../vertx/ResponseBodyWrappingHandler.java | 34 +++++++++++++++---- .../agent/testing/AbstractHttpClientTest.java | 5 +++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java index 6cb72287..1cdd1758 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java @@ -17,11 +17,15 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.vertx; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; public class ResponseBodyWrappingHandler implements Handler { @@ -43,12 +47,30 @@ public void handle(Buffer event) { if (span.isRecording()) { span.setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody); } else { - tracer - .spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME) - .setParent(Context.root().with(span)) - .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody) - .startSpan() - .end(); + SpanBuilder spanBuilder = + tracer + .spanBuilder(HypertraceSemanticAttributes.ADDITIONAL_DATA_SPAN_NAME) + .setParent(Context.root().with(span)) + .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody); + + // Also add content type if present + if (span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + try { + Method getAttribute = + span.getClass().getDeclaredMethod("getAttribute", AttributeKey.class); + getAttribute.setAccessible(true); + Object resContentType = + getAttribute.invoke( + span, AttributeKey.stringKey("http.response.header.content-type")); + if (resContentType != null) { + spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // ignore and continue + } + } + + spanBuilder.startSpan().end(); } wrapped.handle(event); diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java b/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java index 4e8bb258..51764b19 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java +++ b/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java @@ -16,6 +16,7 @@ package org.hypertrace.agent.testing; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.trace.data.SpanData; import java.io.BufferedReader; import java.io.IOException; @@ -118,6 +119,10 @@ public void postJson_echo() Assertions.assertEquals(2, traces.get(0).size()); SpanData responseBodySpan = traces.get(0).get(1); assertBodies(clientSpan, responseBodySpan, body, body); + Assertions.assertNotNull( + responseBodySpan + .getAttributes() + .get(AttributeKey.stringKey("http.response.header.content-type"))); } else { Assertions.assertEquals(1, traces.get(0).size()); assertRequestAndResponseBody(clientSpan, body, body); From d1c38037535111b224d603b3ca9802cb0250d505 Mon Sep 17 00:00:00 2001 From: Shashank Patidar Date: Wed, 4 Oct 2023 17:21:29 +0530 Subject: [PATCH 4/5] get the method on startup --- .../java/inputstream/InputStreamUtils.java | 27 ++++++++++++----- .../vertx/ResponseBodyWrappingHandler.java | 29 +++++++++++++++---- .../HypertraceSemanticAttributes.java | 6 ++++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java index 0b05f1df..7581bab6 100644 --- a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java +++ b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java @@ -45,6 +45,21 @@ private InputStreamUtils() {} private static final Tracer TRACER = GlobalOpenTelemetry.get().getTracer("org.hypertrace.java.inputstream"); + private static Method getAttribute = null; + + static { + try { + getAttribute = Class.forName("io.opentelemetry.sdk.trace.SdkSpan").getDeclaredMethod("getAttribute", AttributeKey.class); + } catch (NoSuchMethodException e) { + log.error("getAttribute method not found in SdkSpan class", e); + } catch (ClassNotFoundException e) { + log.error("SdkSpan class not found", e); + } + if (getAttribute != null) { + getAttribute.setAccessible(true); + } + } + /** * Adds an attribute to span. If the span is ended it adds the attributed to a newly created * child. @@ -60,24 +75,22 @@ public static void addAttribute(Span span, AttributeKey attributeKey, St .setAttribute(attributeKey, value); // Also add content type if present - if (span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + if (getAttribute != null && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { try { - Method getAttribute = - span.getClass().getDeclaredMethod("getAttribute", AttributeKey.class); - getAttribute.setAccessible(true); Object reqContentType = - getAttribute.invoke(span, AttributeKey.stringKey("http.request.header.content-type")); + getAttribute.invoke(span, HypertraceSemanticAttributes.HTTP_REQUEST_HEADER_CONTENT_TYPE); if (reqContentType != null) { spanBuilder.setAttribute("http.request.header.content-type", (String) reqContentType); } Object resContentType = getAttribute.invoke( - span, AttributeKey.stringKey("http.response.header.content-type")); + span, HypertraceSemanticAttributes.HTTP_RESPONSE_HEADER_CONTENT_TYPE); if (resContentType != null) { spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType); } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + } catch (IllegalAccessException | InvocationTargetException e) { // ignore and continue + log.debug("Could not invoke getAttribute on SdkSpan", e); } } spanBuilder.startSpan().end(); diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java index 1cdd1758..c00a4565 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java @@ -27,12 +27,31 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ResponseBodyWrappingHandler implements Handler { private static final Tracer tracer = GlobalOpenTelemetry.getTracer("io.opentelemetry.javaagent.vertx-core-3.0"); + private static final Logger log = LoggerFactory.getLogger(ResponseBodyWrappingHandler.class); + + private static Method getAttribute = null; + + static { + try { + getAttribute = Class.forName("io.opentelemetry.sdk.trace.SdkSpan").getDeclaredMethod("getAttribute", AttributeKey.class); + } catch (NoSuchMethodException e) { + log.error("getAttribute method not found in SdkSpan class", e); + } catch (ClassNotFoundException e) { + log.error("SdkSpan class not found", e); + } + if (getAttribute != null) { + getAttribute.setAccessible(true); + } + } + private final Handler wrapped; private final Span span; @@ -54,19 +73,17 @@ public void handle(Buffer event) { .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody); // Also add content type if present - if (span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + if (getAttribute != null && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { try { - Method getAttribute = - span.getClass().getDeclaredMethod("getAttribute", AttributeKey.class); - getAttribute.setAccessible(true); Object resContentType = getAttribute.invoke( - span, AttributeKey.stringKey("http.response.header.content-type")); + span, HypertraceSemanticAttributes.HTTP_RESPONSE_HEADER_CONTENT_TYPE); if (resContentType != null) { spanBuilder.setAttribute("http.response.header.content-type", (String) resContentType); } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + } catch (IllegalAccessException | InvocationTargetException e) { // ignore and continue + log.debug("Could not invoke getAttribute on SdkSpan", e); } } diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java index 5e94c8cd..20b5fd17 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java @@ -45,6 +45,12 @@ public static AttributeKey httpResponseHeader(String header) { public static final AttributeKey HTTP_REQUEST_SESSION_ID = AttributeKey.stringKey("http.request.session_id"); + public static final AttributeKey HTTP_REQUEST_HEADER_CONTENT_TYPE = + AttributeKey.stringKey("http.request.header.content-type"); + + public static final AttributeKey HTTP_RESPONSE_HEADER_CONTENT_TYPE = + AttributeKey.stringKey("http.response.header.content-type"); + public static final AttributeKey RPC_REQUEST_BODY = AttributeKey.stringKey("rpc.request.body"); public static final AttributeKey RPC_RESPONSE_BODY = From 798ab735d645fafc9f33d423881c5f65dd767e45 Mon Sep 17 00:00:00 2001 From: Shashank Patidar Date: Wed, 4 Oct 2023 17:34:18 +0530 Subject: [PATCH 5/5] spotless apply --- .../hypertrace/java/inputstream/InputStreamUtils.java | 10 +++++++--- .../hypertrace/vertx/ResponseBodyWrappingHandler.java | 7 +++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java index 7581bab6..9fda5290 100644 --- a/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java +++ b/instrumentation/java-streams/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamUtils.java @@ -49,7 +49,9 @@ private InputStreamUtils() {} static { try { - getAttribute = Class.forName("io.opentelemetry.sdk.trace.SdkSpan").getDeclaredMethod("getAttribute", AttributeKey.class); + getAttribute = + Class.forName("io.opentelemetry.sdk.trace.SdkSpan") + .getDeclaredMethod("getAttribute", AttributeKey.class); } catch (NoSuchMethodException e) { log.error("getAttribute method not found in SdkSpan class", e); } catch (ClassNotFoundException e) { @@ -75,10 +77,12 @@ public static void addAttribute(Span span, AttributeKey attributeKey, St .setAttribute(attributeKey, value); // Also add content type if present - if (getAttribute != null && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + if (getAttribute != null + && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { try { Object reqContentType = - getAttribute.invoke(span, HypertraceSemanticAttributes.HTTP_REQUEST_HEADER_CONTENT_TYPE); + getAttribute.invoke( + span, HypertraceSemanticAttributes.HTTP_REQUEST_HEADER_CONTENT_TYPE); if (reqContentType != null) { spanBuilder.setAttribute("http.request.header.content-type", (String) reqContentType); } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java index c00a4565..06adf1f4 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java @@ -41,7 +41,9 @@ public class ResponseBodyWrappingHandler implements Handler { static { try { - getAttribute = Class.forName("io.opentelemetry.sdk.trace.SdkSpan").getDeclaredMethod("getAttribute", AttributeKey.class); + getAttribute = + Class.forName("io.opentelemetry.sdk.trace.SdkSpan") + .getDeclaredMethod("getAttribute", AttributeKey.class); } catch (NoSuchMethodException e) { log.error("getAttribute method not found in SdkSpan class", e); } catch (ClassNotFoundException e) { @@ -73,7 +75,8 @@ public void handle(Buffer event) { .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody); // Also add content type if present - if (getAttribute != null && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + if (getAttribute != null + && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { try { Object resContentType = getAttribute.invoke(