Skip to content

Commit e30ccde

Browse files
authored
Add payload and headers capture for spring webflux (#224)
* Add payload and headers capture for spring webflux Signed-off-by: Pavol Loffay <[email protected]> * docs Signed-off-by: Pavol Loffay <[email protected]> * Fix format Signed-off-by: Pavol Loffay <[email protected]> * Add tests Signed-off-by: Pavol Loffay <[email protected]> * More tests Signed-off-by: Pavol Loffay <[email protected]>
1 parent 4989a26 commit e30ccde

File tree

22 files changed

+1239
-552
lines changed

22 files changed

+1239
-552
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ List of supported frameworks with additional capabilities:
2222
| [OkHttp](https://github.com/square/okhttp/) | 3.0+ |
2323
| [Servlet](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html) | 2.3+ |
2424
| [Spark Web Framework](https://github.com/perwendel/spark) | 2.3+ |
25+
| [Spring Webflux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html) (only server) | 5.0+ |
2526
| [Vert.x](https://vertx.io) (only server) | 3.0+ |
2627

2728
### Adding custom filter implementation

instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/NettyChannelPipelineInstrumentation.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ public static void addHandler(
9898
.getName(),
9999
HttpServerTracingHandler.class.getName(),
100100
new HttpServerTracingHandler());
101+
102+
// add OTEL request handler to start spans
103+
pipeline.addBefore(
104+
HttpServerTracingHandler.class.getName(),
105+
io.opentelemetry.javaagent.instrumentation.netty.v4_0.server
106+
.HttpServerRequestTracingHandler.class
107+
.getName(),
108+
new io.opentelemetry.javaagent.instrumentation.netty.v4_0.server
109+
.HttpServerRequestTracingHandler());
110+
101111
pipeline.addLast(
102112
HttpServerBlockingRequestHandler.class.getName(),
103113
new HttpServerBlockingRequestHandler());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Copyright The Hypertrace Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0;
18+
19+
import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.NettyTestServer.RESPONSE_BODY;
20+
import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.NettyTestServer.RESPONSE_HEADER_NAME;
21+
import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.NettyTestServer.RESPONSE_HEADER_VALUE;
22+
23+
import io.opentelemetry.sdk.trace.data.SpanData;
24+
import java.io.IOException;
25+
import java.util.Arrays;
26+
import java.util.List;
27+
import java.util.concurrent.ExecutionException;
28+
import java.util.concurrent.TimeoutException;
29+
import okhttp3.MediaType;
30+
import okhttp3.Request;
31+
import okhttp3.RequestBody;
32+
import okhttp3.Response;
33+
import okio.Buffer;
34+
import okio.BufferedSink;
35+
import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes;
36+
import org.hypertrace.agent.testing.AbstractInstrumenterTest;
37+
import org.junit.jupiter.api.AfterEach;
38+
import org.junit.jupiter.api.Assertions;
39+
import org.junit.jupiter.api.BeforeEach;
40+
import org.junit.jupiter.api.Test;
41+
42+
public abstract class AbstractNetty40ServerInstrumentationTest extends AbstractInstrumenterTest {
43+
44+
public static final String REQUEST_HEADER_NAME = "reqheader";
45+
public static final String REQUEST_HEADER_VALUE = "reqheadervalue";
46+
47+
private static int port;
48+
private static NettyTestServer nettyTestServer;
49+
50+
@BeforeEach
51+
private void startServer() throws IOException, InterruptedException {
52+
nettyTestServer = createNetty();
53+
port = nettyTestServer.create();
54+
}
55+
56+
@AfterEach
57+
private void stopServer() throws ExecutionException, InterruptedException {
58+
nettyTestServer.stopServer();
59+
}
60+
61+
protected abstract NettyTestServer createNetty();
62+
63+
@Test
64+
public void get() throws IOException, TimeoutException, InterruptedException {
65+
Request request =
66+
new Request.Builder()
67+
.url(String.format("http://localhost:%d/get_no_content", port))
68+
.header(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE)
69+
.get()
70+
.build();
71+
72+
try (Response response = httpClient.newCall(request).execute()) {
73+
Assertions.assertEquals(204, response.code());
74+
}
75+
76+
List<List<SpanData>> traces = TEST_WRITER.getTraces();
77+
TEST_WRITER.waitForTraces(1);
78+
Assertions.assertEquals(1, traces.size());
79+
List<SpanData> trace = traces.get(0);
80+
Assertions.assertEquals(1, trace.size());
81+
SpanData spanData = trace.get(0);
82+
83+
Assertions.assertEquals(
84+
REQUEST_HEADER_VALUE,
85+
spanData
86+
.getAttributes()
87+
.get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME)));
88+
Assertions.assertEquals(
89+
RESPONSE_HEADER_VALUE,
90+
spanData
91+
.getAttributes()
92+
.get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME)));
93+
Assertions.assertNull(
94+
spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY));
95+
Assertions.assertNull(
96+
spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY));
97+
}
98+
99+
@Test
100+
public void postJson() throws IOException, TimeoutException, InterruptedException {
101+
RequestBody requestBody = requestBody(true, 3000, 75);
102+
Request request =
103+
new Request.Builder()
104+
.url(String.format("http://localhost:%d/post", port))
105+
.header(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE)
106+
.header("Transfer-Encoding", "chunked")
107+
.post(requestBody)
108+
.build();
109+
110+
try (Response response = httpClient.newCall(request).execute()) {
111+
Assertions.assertEquals(200, response.code());
112+
Assertions.assertEquals(RESPONSE_BODY, response.body().string());
113+
}
114+
115+
List<List<SpanData>> traces = TEST_WRITER.getTraces();
116+
TEST_WRITER.waitForTraces(1);
117+
Assertions.assertEquals(1, traces.size());
118+
List<SpanData> trace = traces.get(0);
119+
Assertions.assertEquals(1, trace.size());
120+
SpanData spanData = trace.get(0);
121+
122+
Assertions.assertEquals(
123+
REQUEST_HEADER_VALUE,
124+
spanData
125+
.getAttributes()
126+
.get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME)));
127+
Assertions.assertEquals(
128+
RESPONSE_HEADER_VALUE,
129+
spanData
130+
.getAttributes()
131+
.get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME)));
132+
Buffer requestBodyBuffer = new Buffer();
133+
requestBody.writeTo(requestBodyBuffer);
134+
Assertions.assertEquals(
135+
new String(requestBodyBuffer.readByteArray()),
136+
spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY));
137+
Assertions.assertEquals(
138+
RESPONSE_BODY,
139+
spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY));
140+
}
141+
142+
@Test
143+
public void blocking() throws IOException, TimeoutException, InterruptedException {
144+
Request request =
145+
new Request.Builder()
146+
.url(String.format("http://localhost:%d/post", port))
147+
.header(REQUEST_HEADER_NAME, REQUEST_HEADER_VALUE)
148+
.header("mockblock", "true")
149+
.get()
150+
.build();
151+
152+
try (Response response = httpClient.newCall(request).execute()) {
153+
Assertions.assertEquals(403, response.code());
154+
Assertions.assertTrue(response.body().string().isEmpty());
155+
}
156+
157+
List<List<SpanData>> traces = TEST_WRITER.getTraces();
158+
TEST_WRITER.waitForTraces(1);
159+
Assertions.assertEquals(1, traces.size());
160+
List<SpanData> trace = traces.get(0);
161+
Assertions.assertEquals(1, trace.size());
162+
SpanData spanData = trace.get(0);
163+
164+
Assertions.assertEquals(
165+
REQUEST_HEADER_VALUE,
166+
spanData
167+
.getAttributes()
168+
.get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME)));
169+
Assertions.assertNull(
170+
spanData
171+
.getAttributes()
172+
.get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME)));
173+
Assertions.assertNull(
174+
spanData
175+
.getAttributes()
176+
.get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_BODY)));
177+
}
178+
179+
private RequestBody requestBody(final boolean chunked, final long size, final int writeSize) {
180+
final byte[] buffer = new byte[writeSize];
181+
Arrays.fill(buffer, (byte) 'x');
182+
183+
return new RequestBody() {
184+
@Override
185+
public MediaType contentType() {
186+
return MediaType.get("application/json; charset=utf-8");
187+
}
188+
189+
@Override
190+
public long contentLength() throws IOException {
191+
return chunked ? -1L : size;
192+
}
193+
194+
@Override
195+
public void writeTo(BufferedSink sink) throws IOException {
196+
for (int count = 0; count < size; count += writeSize) {
197+
sink.write(buffer, 0, (int) Math.min(size - count, writeSize));
198+
}
199+
}
200+
};
201+
}
202+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright The Hypertrace Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0;
18+
19+
import io.netty.handler.codec.http.HttpServerCodec;
20+
import java.util.Arrays;
21+
22+
public class Netty40HttpServerCodecInstrumentationTest
23+
extends AbstractNetty40ServerInstrumentationTest {
24+
25+
@Override
26+
protected NettyTestServer createNetty() {
27+
return new NettyTestServer(Arrays.asList(HttpServerCodec.class));
28+
}
29+
}

0 commit comments

Comments
 (0)