Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d3754fe

Browse files
Armeria Jetty: ensure spans are finished (DataDog#6291)
1 parent c88f535 commit d3754fe

File tree

13 files changed

+501
-15
lines changed

13 files changed

+501
-15
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
ext {
2+
jetty11TestMinJavaVersionForTests = JavaVersion.VERSION_11
3+
}
4+
5+
muzzle {
6+
pass {
7+
group = "com.linecorp.armeria"
8+
module = "armeria-jetty11"
9+
versions = "[1.24.0,)"
10+
}
11+
pass {
12+
group = "com.linecorp.armeria"
13+
module = "armeria-jetty9"
14+
versions = "[1.24.0,)"
15+
}
16+
}
17+
18+
apply from: "$rootDir/gradle/java.gradle"
19+
apply from: "$rootDir/gradle/configure_tests.gradle"
20+
21+
addTestSuiteForDir('latestDepTest', 'test')
22+
23+
addTestSuiteForDir("jetty9Test", "test/jetty9")
24+
addTestSuiteForDir("jetty11Test", "test/jetty11")
25+
addTestSuiteExtendingForDir("jetty9LatestDepTest", "latestDepTest", "test/jetty9")
26+
addTestSuiteExtendingForDir("jetty11LatestDepTest", "latestDepTest", "test/jetty11")
27+
28+
[compileJetty11TestGroovy, jetty11Test, compileJetty11LatestDepTestGroovy, jetty11LatestDepTest].each {
29+
it.configure {
30+
javaLauncher = getJavaLauncherFor(11)
31+
}
32+
}
33+
34+
dependencies {
35+
compileOnly group: 'com.linecorp.armeria', name: 'armeria-jetty9', version: '1.24.0'
36+
jetty11TestImplementation group: 'com.linecorp.armeria', name: 'armeria-jetty11', version: '1.24.0', {
37+
exclude group: 'org.slf4j', module: 'slf4j-api'
38+
}
39+
jetty11TestImplementation "org.eclipse.jetty:jetty-server:11.0.0", {
40+
exclude group: 'org.slf4j', module: 'slf4j-api'
41+
}
42+
jetty11TestImplementation "org.eclipse.jetty:jetty-servlet:11.0.0", {
43+
exclude group: 'org.slf4j', module: 'slf4j-api'
44+
}
45+
46+
jetty11LatestDepTestImplementation group: 'com.linecorp.armeria', name: 'armeria-jetty11', version: '+', {
47+
exclude group: 'org.slf4j', module: 'slf4j-api'
48+
}
49+
jetty11LatestDepTestImplementation "org.eclipse.jetty:jetty-server:11.+", {
50+
exclude group: 'org.slf4j', module: 'slf4j-api'
51+
}
52+
jetty11LatestDepTestImplementation "org.eclipse.jetty:jetty-servlet:11.+", {
53+
exclude group: 'org.slf4j', module: 'slf4j-api'
54+
}
55+
56+
jetty9TestImplementation group: 'com.linecorp.armeria', name: 'armeria-jetty9', version: '1.24.0', {
57+
exclude group: 'org.slf4j', module: 'slf4j-api'
58+
}
59+
jetty9TestImplementation "org.eclipse.jetty:jetty-server:9.4.48.v20220622", {
60+
exclude group: 'org.slf4j', module: 'slf4j-api'
61+
}
62+
jetty9TestImplementation "org.eclipse.jetty:jetty-servlet:9.4.48.v20220622", {
63+
exclude group: 'org.slf4j', module: 'slf4j-api'
64+
}
65+
jetty9LatestDepTestImplementation group: 'com.linecorp.armeria', name: 'armeria-jetty9', version: '+', {
66+
exclude group: 'org.slf4j', module: 'slf4j-api'
67+
}
68+
jetty9LatestDepTestImplementation "org.eclipse.jetty:jetty-server:9.+", {
69+
exclude group: 'org.slf4j', module: 'slf4j-api'
70+
}
71+
jetty9LatestDepTestImplementation "org.eclipse.jetty:jetty-servlet:9.+", {
72+
exclude group: 'org.slf4j', module: 'slf4j-api'
73+
}
74+
testImplementation testFixtures(project(':dd-java-agent:instrumentation:servlet:request-3'))
75+
testImplementation testFixtures(project(':dd-java-agent:instrumentation:jetty-9'))
76+
testImplementation testFixtures(project(':dd-java-agent:instrumentation:servlet:request-5'))
77+
testImplementation testFixtures(project(':dd-java-agent:instrumentation:jetty-11'))
78+
79+
testImplementation(project(':dd-java-agent:testing')) {
80+
exclude group: 'org.eclipse.jetty', module: 'jetty-server'
81+
}
82+
// always mix everything up
83+
testRuntimeOnly project(':dd-java-agent:instrumentation:jetty-11')
84+
testRuntimeOnly project(':dd-java-agent:instrumentation:jetty-9')
85+
testRuntimeOnly(project(':dd-java-agent:instrumentation:jetty-util'))
86+
testRuntimeOnly project(':dd-java-agent:instrumentation:jetty-appsec-9.3')
87+
testRuntimeOnly project(':dd-java-agent:instrumentation:servlet:request-5')
88+
testRuntimeOnly project(':dd-java-agent:instrumentation:servlet:request-3')
89+
90+
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package datadog.trace.instrumentation.armeria.jetty;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed;
4+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
5+
6+
import com.google.auto.service.AutoService;
7+
import com.linecorp.armeria.server.ServiceRequestContext;
8+
import datadog.trace.agent.tooling.Instrumenter;
9+
import net.bytebuddy.asm.Advice;
10+
import net.bytebuddy.matcher.ElementMatcher;
11+
import org.eclipse.jetty.server.HttpChannel;
12+
13+
@AutoService(Instrumenter.class)
14+
public class ArmeriaHttpConnectionInstrumentation extends Instrumenter.Tracing
15+
implements Instrumenter.ForSingleType {
16+
public ArmeriaHttpConnectionInstrumentation() {
17+
super("armeria-jetty", "armeria");
18+
}
19+
20+
@Override
21+
public String instrumentedType() {
22+
return "org.eclipse.jetty.server.HttpChannel";
23+
}
24+
25+
@Override
26+
public ElementMatcher<ClassLoader> classLoaderMatcher() {
27+
return hasClassNamed("com.linecorp.armeria.server.ServiceRequestContext");
28+
}
29+
30+
@Override
31+
public String[] helperClassNames() {
32+
return new String[] {
33+
packageName + ".AttributeKeys",
34+
};
35+
}
36+
37+
@Override
38+
public void adviceTransformations(AdviceTransformation transformation) {
39+
transformation.applyAdvice(
40+
isConstructor(), getClass().getName() + "$JettyHttpChannelCaptureAdvice");
41+
}
42+
43+
public static class JettyHttpChannelCaptureAdvice {
44+
@Advice.OnMethodExit(suppress = Throwable.class)
45+
public static void afterInvoke(@Advice.This final HttpChannel channel) {
46+
final ServiceRequestContext current = ServiceRequestContext.currentOrNull();
47+
if (current != null) {
48+
current.setAttr(AttributeKeys.HTTP_CHANNEL_ATTRIBUTE_KEY, channel);
49+
}
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package datadog.trace.instrumentation.armeria.jetty;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
5+
import static net.bytebuddy.matcher.ElementMatchers.returns;
6+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
7+
8+
import com.google.auto.service.AutoService;
9+
import com.linecorp.armeria.common.HttpResponse;
10+
import com.linecorp.armeria.server.ServiceRequestContext;
11+
import datadog.trace.agent.tooling.Instrumenter;
12+
import net.bytebuddy.asm.Advice;
13+
import org.eclipse.jetty.server.HttpChannel;
14+
15+
@AutoService(Instrumenter.class)
16+
public class ArmeriaJettyInstrumentation extends Instrumenter.Tracing
17+
implements Instrumenter.ForSingleType {
18+
public ArmeriaJettyInstrumentation() {
19+
super("armeria-jetty", "armeria");
20+
}
21+
22+
@Override
23+
public String instrumentedType() {
24+
return "com.linecorp.armeria.server.jetty.JettyService";
25+
}
26+
27+
@Override
28+
public String[] helperClassNames() {
29+
return new String[] {
30+
packageName + ".AttributeKeys",
31+
};
32+
}
33+
34+
@Override
35+
public void adviceTransformations(AdviceTransformation transformation) {
36+
transformation.applyAdvice(
37+
isMethod()
38+
.and(named("serve"))
39+
.and(
40+
returns(named("com.linecorp.armeria.common.HttpResponse"))
41+
.and(
42+
takesArgument(
43+
0, named("com.linecorp.armeria.server.ServiceRequestContext")))),
44+
getClass().getName() + "$JettySpanCloserAdvice");
45+
}
46+
47+
public static class JettySpanCloserAdvice {
48+
@Advice.OnMethodExit(onThrowable = Throwable.class)
49+
public static void afterInvoke(
50+
@Advice.Return final HttpResponse response,
51+
@Advice.Argument(0) ServiceRequestContext context) {
52+
// get the current attribute value and clear it if existing
53+
HttpChannel channel = context.setAttr(AttributeKeys.HTTP_CHANNEL_ATTRIBUTE_KEY, null);
54+
if (channel != null) {
55+
response.whenComplete().thenRun(channel::recycle);
56+
}
57+
}
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package datadog.trace.instrumentation.armeria.jetty;
2+
3+
import datadog.trace.api.GenericClassValue;
4+
import io.netty.util.AttributeKey;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.concurrent.ConcurrentMap;
7+
import org.eclipse.jetty.server.HttpChannel;
8+
9+
public final class AttributeKeys {
10+
11+
private static final ClassValue<ConcurrentHashMap<String, AttributeKey<?>>> MAPS =
12+
GenericClassValue.constructing(ConcurrentHashMap.class);
13+
14+
public static final AttributeKey<HttpChannel> HTTP_CHANNEL_ATTRIBUTE_KEY =
15+
attributeKey("dd.armeria.jetty.channel");
16+
17+
/**
18+
* Generate an attribute key or reuse the one existing in the global app map. This implementation
19+
* creates attributes only once even if the current class is loaded by several class loaders and
20+
* prevents an issue with Apache Atlas project were this class loaded by multiple class loaders,
21+
* while the Attribute class is loaded by a third class loader and used internally for the
22+
* cassandra driver. note: drawn from netty module instrumentation
23+
*/
24+
@SuppressWarnings("unchecked")
25+
private static <T> AttributeKey<T> attributeKey(final String key) {
26+
ConcurrentMap<String, AttributeKey<?>> map = MAPS.get(AttributeKey.class);
27+
AttributeKey<T> attributeKey = (AttributeKey<T>) map.get(key);
28+
if (null == attributeKey) {
29+
attributeKey = AttributeKey.newInstance(key);
30+
AttributeKey<T> predecessor = (AttributeKey<T>) map.putIfAbsent(key, attributeKey);
31+
if (null != predecessor) {
32+
attributeKey = predecessor;
33+
}
34+
}
35+
return attributeKey;
36+
}
37+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import com.linecorp.armeria.server.Server
2+
import com.linecorp.armeria.server.jetty.JettyService
3+
import datadog.trace.agent.test.base.HttpServer
4+
import datadog.trace.agent.test.base.HttpServerTest
5+
import datadog.trace.instrumentation.servlet5.TestServlet5
6+
7+
import java.util.concurrent.TimeoutException
8+
9+
class ArmeriaJetty11ServerTest extends HttpServerTest<ArmeriaServer> {
10+
class ArmeriaServer implements HttpServer {
11+
def port
12+
def server = Server.builder()
13+
.serviceUnder("/", JettyService.of(new JettyServer(JettyServer.servletHandler(TestServlet5)).server))
14+
.build()
15+
16+
@Override
17+
void start() throws TimeoutException {
18+
server.start().get()
19+
port = server.activeLocalPort()
20+
}
21+
22+
@Override
23+
void stop() {
24+
server?.stop()?.get()
25+
}
26+
27+
@Override
28+
URI address() {
29+
URI.create("http://localhost:$port/context-path/")
30+
}
31+
}
32+
33+
@Override
34+
HttpServer server() {
35+
new ArmeriaServer()
36+
}
37+
38+
39+
@Override
40+
String component() {
41+
return "jetty-server"
42+
}
43+
44+
@Override
45+
String expectedOperationName() {
46+
"servlet.request"
47+
}
48+
@Override
49+
Map<String, Serializable> expectedExtraServerTags(HttpServerTest.ServerEndpoint endpoint) {
50+
['servlet.context': '/context-path', 'servlet.path': endpoint.path]
51+
}
52+
53+
@Override
54+
String expectedServiceName() {
55+
'context-path'
56+
}
57+
58+
@Override
59+
protected boolean enabledFinishTimingChecks() {
60+
true
61+
}
62+
63+
@Override
64+
boolean testEncodedQuery() {
65+
false
66+
}
67+
68+
@Override
69+
boolean testExceptionBody() {
70+
false
71+
}
72+
73+
@Override
74+
boolean testBodyUrlencoded() {
75+
true
76+
}
77+
78+
@Override
79+
boolean testRequestBody() {
80+
true
81+
}
82+
83+
@Override
84+
boolean testRequestBodyISVariant() {
85+
true
86+
}
87+
88+
@Override
89+
boolean testBodyMultipart() {
90+
true
91+
}
92+
93+
@Override
94+
boolean testBlocking() {
95+
false
96+
}
97+
98+
@Override
99+
boolean testUserBlocking() {
100+
false
101+
}
102+
103+
@Override
104+
boolean testBlockingOnResponse() {
105+
false
106+
}
107+
108+
@Override
109+
boolean hasExtraErrorInformation() {
110+
true
111+
}
112+
113+
@Override
114+
boolean testEncodedPath() {
115+
return false
116+
}
117+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /