|
26 | 26 | import io.netty.util.Attribute; |
27 | 27 | import io.opentelemetry.api.common.AttributeKey; |
28 | 28 | import io.opentelemetry.api.trace.Span; |
| 29 | +import io.opentelemetry.context.Context; |
| 30 | +import io.opentelemetry.context.Scope; |
29 | 31 | import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; |
30 | 32 | import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.AttributeKeys; |
31 | 33 | import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.DataCaptureUtils; |
| 34 | +import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.client.OtelHttpClientRequestTracingHandler; |
32 | 35 | import java.nio.charset.Charset; |
33 | 36 | import java.util.Deque; |
34 | 37 | import java.util.HashMap; |
@@ -57,50 +60,59 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { |
57 | 60 | ctx.fireChannelRead(msg); |
58 | 61 | return; |
59 | 62 | } |
60 | | - Span span = Span.fromContext(serverContexts.element().context()); |
61 | 63 |
|
62 | | - if (msg instanceof HttpRequest) { |
63 | | - HttpRequest httpRequest = (HttpRequest) msg; |
| 64 | + Context context = serverContexts.element().context(); |
64 | 65 |
|
65 | | - Map<String, String> headersMap = headersToMap(httpRequest); |
66 | | - if (instrumentationConfig.httpHeaders().request()) { |
67 | | - headersMap.forEach(span::setAttribute); |
68 | | - } |
69 | | - // used by blocking handler |
70 | | - channel.attr(AttributeKeys.REQUEST_HEADERS).set(headersMap); |
| 66 | + // Store the server context in our ThreadLocal for later use by client handlers |
| 67 | + // This is CRITICAL for proper context propagation to client spans |
| 68 | + OtelHttpClientRequestTracingHandler.storeServerContext(context); |
| 69 | + |
| 70 | + try (Scope ignored = context.makeCurrent()) { |
| 71 | + Span span = Span.fromContext(context); |
| 72 | + |
| 73 | + if (msg instanceof HttpRequest) { |
| 74 | + HttpRequest httpRequest = (HttpRequest) msg; |
| 75 | + |
| 76 | + Map<String, String> headersMap = headersToMap(httpRequest); |
| 77 | + if (instrumentationConfig.httpHeaders().request()) { |
| 78 | + headersMap.forEach(span::setAttribute); |
| 79 | + } |
| 80 | + // used by blocking handler |
| 81 | + channel.attr(AttributeKeys.REQUEST_HEADERS).set(headersMap); |
71 | 82 |
|
72 | | - CharSequence contentType = DataCaptureUtils.getContentType(httpRequest); |
73 | | - if (instrumentationConfig.httpBody().request() |
74 | | - && contentType != null |
75 | | - && ContentTypeUtils.shouldCapture(contentType.toString())) { |
| 83 | + CharSequence contentType = DataCaptureUtils.getContentType(httpRequest); |
| 84 | + if (instrumentationConfig.httpBody().request() |
| 85 | + && contentType != null |
| 86 | + && ContentTypeUtils.shouldCapture(contentType.toString())) { |
76 | 87 |
|
77 | | - CharSequence contentLengthHeader = DataCaptureUtils.getContentLength(httpRequest); |
78 | | - int contentLength = ContentLengthUtils.parseLength(contentLengthHeader); |
| 88 | + CharSequence contentLengthHeader = DataCaptureUtils.getContentLength(httpRequest); |
| 89 | + int contentLength = ContentLengthUtils.parseLength(contentLengthHeader); |
79 | 90 |
|
80 | | - String charsetString = ContentTypeUtils.parseCharset(contentType.toString()); |
81 | | - Charset charset = ContentTypeCharsetUtils.toCharset(charsetString); |
| 91 | + String charsetString = ContentTypeUtils.parseCharset(contentType.toString()); |
| 92 | + Charset charset = ContentTypeCharsetUtils.toCharset(charsetString); |
82 | 93 |
|
83 | | - // set the buffer to capture response body |
84 | | - // the buffer is used byt captureBody method |
85 | | - Attribute<BoundedByteArrayOutputStream> bufferAttr = |
86 | | - ctx.channel().attr(AttributeKeys.REQUEST_BODY_BUFFER); |
87 | | - bufferAttr.set(BoundedBuffersFactory.createStream(contentLength, charset)); |
| 94 | + // set the buffer to capture response body |
| 95 | + // the buffer is used byt captureBody method |
| 96 | + Attribute<BoundedByteArrayOutputStream> bufferAttr = |
| 97 | + ctx.channel().attr(AttributeKeys.REQUEST_BODY_BUFFER); |
| 98 | + bufferAttr.set(BoundedBuffersFactory.createStream(contentLength, charset)); |
88 | 99 |
|
89 | | - channel.attr(AttributeKeys.PROVIDED_CHARSET).set(charset); |
| 100 | + channel.attr(AttributeKeys.PROVIDED_CHARSET).set(charset); |
| 101 | + } |
90 | 102 | } |
91 | | - } |
92 | 103 |
|
93 | | - if ((msg instanceof HttpContent || msg instanceof ByteBuf) |
94 | | - && instrumentationConfig.httpBody().request()) { |
95 | | - Charset charset = channel.attr(AttributeKeys.PROVIDED_CHARSET).get(); |
96 | | - if (charset == null) { |
97 | | - charset = ContentTypeCharsetUtils.getDefaultCharset(); |
| 104 | + if ((msg instanceof HttpContent || msg instanceof ByteBuf) |
| 105 | + && instrumentationConfig.httpBody().request()) { |
| 106 | + Charset charset = channel.attr(AttributeKeys.PROVIDED_CHARSET).get(); |
| 107 | + if (charset == null) { |
| 108 | + charset = ContentTypeCharsetUtils.getDefaultCharset(); |
| 109 | + } |
| 110 | + DataCaptureUtils.captureBody( |
| 111 | + span, channel, AttributeKeys.REQUEST_BODY_BUFFER, msg, null, charset); |
98 | 112 | } |
99 | | - DataCaptureUtils.captureBody( |
100 | | - span, channel, AttributeKeys.REQUEST_BODY_BUFFER, msg, null, charset); |
101 | | - } |
102 | 113 |
|
103 | | - ctx.fireChannelRead(msg); |
| 114 | + ctx.fireChannelRead(msg); |
| 115 | + } |
104 | 116 | } |
105 | 117 |
|
106 | 118 | private static Map<String, String> headersToMap(HttpMessage httpMessage) { |
|
0 commit comments