Skip to content

Commit 7cd6ef4

Browse files
committed
Fix netty 'unknown' transactions
1 parent 0f181db commit 7cd6ef4

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

instrumentation/netty-4.1.16/src/main/java/com/agent/instrumentation/netty4116/NettyUtil.java

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919

2020
public class NettyUtil {
2121

22+
// This config is added so customers can also start netty transactions at a lower level.
23+
// However, this risks netty producing transactions with 'unknown' urls.
24+
// Only use it if it provides the coverage you need for your application's use case.
25+
public static Boolean START_HTTP2_FRAME_READ_LISTENER_TXN =
26+
NewRelic.getAgent().getConfig().getValue("netty.http2.frame_read_listener.start_transaction", false);
27+
public static Boolean START_HTTP2_FRAME_CODEC_TXN = !START_HTTP2_FRAME_READ_LISTENER_TXN;
28+
2229
public static String getNettyVersion() {
2330
return "4.1.16";
2431
}

instrumentation/netty-4.1.16/src/main/java/io/netty/handler/codec/http2/FrameReadListener_Instrumentation.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package io.netty.handler.codec.http2;
99

10+
import com.agent.instrumentation.netty4116.NettyUtil;
1011
import com.newrelic.api.agent.weaver.MatchType;
1112
import com.newrelic.api.agent.weaver.Weave;
1213
import com.newrelic.api.agent.weaver.Weaver;
@@ -19,7 +20,7 @@ class FrameReadListener_Instrumentation {
1920
// Process HTTP/2 request headers and start txn
2021
public void onHeadersRead(ChannelHandlerContext_Instrumentation ctx, int streamId, Http2Headers headers, int streamDependency, short weight,
2122
boolean exclusive, int padding, boolean endOfStream) {
22-
if (ctx.pipeline().token == null) {
23+
if (NettyUtil.START_HTTP2_FRAME_CODEC_TXN && ctx.pipeline().token == null) {
2324
// NettyDispatcher class is usually initialized in AbstractBootstrap; however,
2425
// that code is not always invoked when using recent Netty versions (4.1.54)
2526
// so we check here and initialize if we haven't yet.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
*
3+
* * Copyright 2024 New Relic Corporation. All rights reserved.
4+
* * SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
package io.netty.handler.codec.http2;
9+
10+
import com.agent.instrumentation.netty4116.NettyUtil;
11+
import com.newrelic.api.agent.weaver.MatchType;
12+
import com.newrelic.api.agent.weaver.Weave;
13+
import com.newrelic.api.agent.weaver.Weaver;
14+
import io.netty.bootstrap.NettyDispatcher;
15+
import io.netty.channel.ChannelHandlerContext_Instrumentation;
16+
import io.netty.channel.ChannelPromise;
17+
18+
@Weave(type = MatchType.BaseClass, originalName = "io.netty.handler.codec.http2.Http2FrameCodec")
19+
public class Http2FrameCodec_Instrumentation {
20+
21+
// Handle the incoming request. For HTTP/2 there is no HttpRequest object
22+
// but rather a stream of Http2Frame objects that make up the full request.
23+
void onHttp2Frame(ChannelHandlerContext_Instrumentation ctx, Http2Frame frame) {
24+
if (!NettyUtil.START_HTTP2_FRAME_READ_LISTENER_TXN && frame instanceof Http2HeadersFrame && ctx.pipeline().token == null) {
25+
Http2HeadersFrame msg = (Http2HeadersFrame) frame;
26+
if (msg.isEndStream()) {
27+
// NettyDispatcher class is usually initialized in AbstractBootstrap; however,
28+
// that code is not always invoked when using recent Netty versions (4.1.54)
29+
// so we check here and initialize if we haven't yet.
30+
if (!NettyDispatcher.instrumented.get()) {
31+
NettyDispatcher.get();
32+
}
33+
NettyDispatcher.channelRead(ctx, msg.headers());
34+
}
35+
}
36+
// Order matters here!!! Weaver.callOriginal() must come after the call to NettyDispatcher.channelRead.
37+
Weaver.callOriginal();
38+
}
39+
40+
// Handle the outgoing response. For HTTP/2 there is no HttpResponse object
41+
// but rather a stream of Http2Frame objects that make up the full response.
42+
public void write(ChannelHandlerContext_Instrumentation ctx, Object msg, ChannelPromise promise) {
43+
if (msg instanceof Http2HeadersFrame) {
44+
boolean expired = NettyUtil.processResponse(msg, ctx.pipeline().token);
45+
if (expired) {
46+
ctx.pipeline().token = null;
47+
}
48+
}
49+
// Order matters here!!! Weaver.callOriginal() must come after the call to NettyUtil.processResponse.
50+
Weaver.callOriginal();
51+
}
52+
}

0 commit comments

Comments
 (0)