Skip to content

Commit 871ce37

Browse files
committed
Migrate JMS to Byte Buddy and add JMS 1 support
This does not yet include instrumentation for MessageListener. I think there is actually no difference between the instrumentation so we should combine them. I also added TEXT_MAP as a format that maps to the HTTP Codec. There is some remapping inside the instrumentation to remove dashes before setting as a property. This should also be changed when an official format is defined. This also currently has a problem with Spring Boot applications as the JMS Util references classes not on the system classpath (it needs to be injected into the child classpath)
1 parent cd497ad commit 871ce37

File tree

24 files changed

+961
-161
lines changed

24 files changed

+961
-161
lines changed

dd-java-agent-ittests/dd-java-agent-ittests.gradle

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ dependencies {
2525
}
2626
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.8.2'
2727
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.8.2'
28-
testCompile group: 'javax.jms', name: 'javax.jms-api', version: '2.0.1'
29-
testCompile group: 'org.apache.activemq.tooling', name: 'activemq-junit', version: '5.14.5'
30-
testCompile group: 'org.apache.activemq', name: 'activemq-broker', version: '5.14.5'
3128

3229
// JDBC tests:
3330
testCompile group: 'com.h2database', name: 'h2', version: '1.4.196'

dd-java-agent-ittests/src/test/java/com/datadoghq/agent/integration/JMSInstrumentationTest.java

Lines changed: 0 additions & 44 deletions
This file was deleted.

dd-java-agent/dd-java-agent.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ dependencies {
2424
compile(project(':dd-java-agent:integrations:aws-sdk')) {
2525
transitive = false
2626
}
27+
compile(project(':dd-java-agent:integrations:jms-1')) {
28+
transitive = false
29+
}
30+
compile(project(':dd-java-agent:integrations:jms-2')) {
31+
transitive = false
32+
}
2733
compile(project(':dd-java-agent:integrations:servlet-2')) {
2834
transitive = false
2935
}

dd-java-agent/integrations/helpers/helpers.gradle

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ dependencies {
1515

1616
compile group: 'io.opentracing.contrib', name: 'opentracing-web-servlet-filter', version: '0.0.9'
1717
compile group: 'io.opentracing.contrib', name: 'opentracing-okhttp3', version: '0.0.5'
18-
compile group: 'io.opentracing.contrib', name: 'opentracing-jms-common', version: '0.0.3'
19-
compile group: 'io.opentracing.contrib', name: 'opentracing-jms-2', version: '0.0.3'
2018
compile group: 'io.opentracing.contrib', name: 'opentracing-aws-sdk', version: '0.0.2'
2119
compile group: 'io.opentracing.contrib', name: 'opentracing-cassandra-driver', version: '0.0.2'
2220
compile group: 'io.opentracing.contrib', name: 'opentracing-apache-httpclient', version: '0.0.2'

dd-java-agent/integrations/helpers/src/main/java/com/datadoghq/agent/integration/JMSMessageConsumerHelper.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

dd-java-agent/integrations/helpers/src/main/java/com/datadoghq/agent/integration/JMSMessageProducerHelper.java

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.datadoghq.agent.integration;
2+
3+
import javax.jms.Destination;
4+
import javax.jms.Message;
5+
import javax.jms.Queue;
6+
import javax.jms.TemporaryQueue;
7+
import javax.jms.TemporaryTopic;
8+
import javax.jms.Topic;
9+
10+
public class JmsUtil {
11+
12+
public static String toResourceName(final Message message, final Destination destination) {
13+
Destination jmsDestination = null;
14+
try {
15+
jmsDestination = message.getJMSDestination();
16+
} catch (final Exception e) {
17+
}
18+
if (jmsDestination == null) {
19+
jmsDestination = destination;
20+
}
21+
if (jmsDestination instanceof TemporaryQueue) {
22+
return "Temporary Queue";
23+
}
24+
if (jmsDestination instanceof TemporaryTopic) {
25+
return "Temporary Topic";
26+
}
27+
try {
28+
if (jmsDestination instanceof Queue) {
29+
return "Queue " + ((Queue) jmsDestination).getQueueName();
30+
}
31+
if (jmsDestination instanceof Topic) {
32+
return "Topic " + ((Topic) jmsDestination).getTopicName();
33+
}
34+
} catch (final Exception e) {
35+
}
36+
return "Unknown Destination";
37+
}
38+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.datadoghq.agent.integration;
2+
3+
import io.opentracing.propagation.TextMap;
4+
import java.util.Enumeration;
5+
import java.util.HashMap;
6+
import java.util.Iterator;
7+
import java.util.Map;
8+
import javax.jms.JMSException;
9+
import javax.jms.Message;
10+
11+
public class MessagePropertyTextMap implements TextMap {
12+
static final String DASH = "_$dash$_";
13+
14+
private final Message message;
15+
16+
public MessagePropertyTextMap(final Message message) {
17+
this.message = message;
18+
}
19+
20+
@Override
21+
public Iterator<Map.Entry<String, String>> iterator() {
22+
final Map<String, String> map = new HashMap<>();
23+
try {
24+
final Enumeration enumeration = message.getPropertyNames();
25+
if (enumeration != null) {
26+
while (enumeration.hasMoreElements()) {
27+
final String key = (String) enumeration.nextElement();
28+
final Object value = message.getObjectProperty(key);
29+
if (value instanceof String) {
30+
map.put(key.replace(DASH, "-"), (String) value);
31+
}
32+
}
33+
}
34+
} catch (final JMSException e) {
35+
throw new RuntimeException(e);
36+
}
37+
return map.entrySet().iterator();
38+
}
39+
40+
@Override
41+
public void put(final String key, final String value) {
42+
try {
43+
message.setStringProperty(key.replace("-", DASH), value);
44+
} catch (final JMSException e) {
45+
throw new RuntimeException(e);
46+
}
47+
}
48+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apply plugin: 'version-scan'
2+
3+
versionScan {
4+
group = "javax.jms"
5+
module = "jms-api"
6+
versions = "(,2]"
7+
legacyModule = "javax.jms-api"
8+
verifyMissing = [
9+
"javax.jms.JMSContext",
10+
"javax.jms.CompletionListener",
11+
]
12+
}
13+
14+
apply from: "${rootDir}/gradle/java.gradle"
15+
16+
dependencies {
17+
compileOnly group: 'javax.jms', name: 'jms-api', version: '1.1-rev-1'
18+
compileOnly project(':dd-java-agent:integrations:helpers')
19+
20+
compile deps.bytebuddy
21+
compile deps.opentracing
22+
compile deps.autoservice
23+
24+
compile project(':dd-trace')
25+
compile project(':dd-java-agent:tooling')
26+
testCompile project(':dd-java-agent:tooling').sourceSets.test.output
27+
testCompile project(':dd-java-agent:integrations:helpers')
28+
29+
testCompile deps.testLogging
30+
testCompile group: 'org.apache.activemq.tooling', name: 'activemq-junit', version: '5.14.5'
31+
testCompile group: 'org.apache.activemq', name: 'activemq-pool', version: '5.14.5'
32+
testCompile group: 'org.apache.activemq', name: 'activemq-broker', version: '5.14.5'
33+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package dd.inst.jms1;
2+
3+
import static com.datadoghq.agent.integration.JmsUtil.toResourceName;
4+
import static dd.trace.ClassLoaderMatcher.classLoaderHasClasses;
5+
import static dd.trace.ExceptionHandlers.defaultExceptionHandler;
6+
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
7+
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
8+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.not;
11+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
12+
13+
import com.datadoghq.agent.integration.MessagePropertyTextMap;
14+
import com.datadoghq.trace.DDTags;
15+
import com.google.auto.service.AutoService;
16+
import dd.trace.Instrumenter;
17+
import io.opentracing.ActiveSpan;
18+
import io.opentracing.SpanContext;
19+
import io.opentracing.propagation.Format;
20+
import io.opentracing.tag.Tags;
21+
import io.opentracing.util.GlobalTracer;
22+
import java.util.Collections;
23+
import java.util.concurrent.TimeUnit;
24+
import javax.jms.Message;
25+
import javax.jms.MessageConsumer;
26+
import net.bytebuddy.agent.builder.AgentBuilder;
27+
import net.bytebuddy.asm.Advice;
28+
29+
@AutoService(Instrumenter.class)
30+
public final class JMS1MessageConsumerInstrumentation implements Instrumenter {
31+
32+
@Override
33+
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
34+
return agentBuilder
35+
.type(
36+
not(isInterface()).and(hasSuperType(named("javax.jms.MessageConsumer"))),
37+
not(classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener")))
38+
.transform(
39+
new AgentBuilder.Transformer.ForAdvice()
40+
.advice(
41+
named("receive").and(takesArguments(0)).and(isPublic()),
42+
ConsumerAdvice.class.getName())
43+
.advice(
44+
named("receiveNoWait").and(takesArguments(0)).and(isPublic()),
45+
ConsumerAdvice.class.getName())
46+
.withExceptionHandler(defaultExceptionHandler()))
47+
.asDecorator();
48+
}
49+
50+
public static class ConsumerAdvice {
51+
52+
@Advice.OnMethodEnter
53+
public static long startSpan() {
54+
return System.currentTimeMillis();
55+
}
56+
57+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
58+
public static void stopSpan(
59+
@Advice.This final MessageConsumer consumer,
60+
@Advice.Enter final long startTime,
61+
@Advice.Return final Message message,
62+
@Advice.Thrown final Throwable throwable) {
63+
64+
final SpanContext extractedContext =
65+
GlobalTracer.get().extract(Format.Builtin.TEXT_MAP, new MessagePropertyTextMap(message));
66+
67+
final ActiveSpan span =
68+
GlobalTracer.get()
69+
.buildSpan("jms.consume")
70+
.asChildOf(extractedContext)
71+
.withTag(DDTags.SERVICE_NAME, "jms")
72+
.withTag(Tags.COMPONENT.getKey(), "jms1")
73+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CONSUMER)
74+
.withTag("span.origin.type", consumer.getClass().getName())
75+
.withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(startTime))
76+
.startActive();
77+
78+
if (throwable != null) {
79+
Tags.ERROR.set(span, Boolean.TRUE);
80+
span.log(Collections.singletonMap("error.object", throwable));
81+
}
82+
span.setTag(DDTags.RESOURCE_NAME, "Consumed from " + toResourceName(message, null));
83+
span.deactivate();
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)