From 7cf2cb86f8def4d041d7dc7d6593da2b1f865ce3 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Sat, 4 Jan 2025 01:01:12 +0000 Subject: [PATCH 1/2] Support configuring a different namespace in JMS module --- .../instrumentation/jms/JMSDecorator.java | 2 +- .../JMSMessageConsumerInstrumentation.java | 9 +++++-- .../JMSMessageProducerInstrumentation.java | 13 ++++++--- .../instrumentation/jms/JavaxJmsModule.java | 27 ++++++++++++------- .../MDBMessageConsumerInstrumentation.java | 13 ++++++--- .../jms/MessageInstrumentation.java | 7 ++++- .../jms/SessionInstrumentation.java | 19 ++++++++----- 7 files changed, 61 insertions(+), 29 deletions(-) diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java index 9cc12dff9ee..afd27180483 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java @@ -116,7 +116,7 @@ public static void logJMSException(JMSException ex) { @Override protected String[] instrumentationNames() { - return new String[] {"jms", "jms-1", "jms-2"}; + return new String[] {"jms"}; } @Override diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageConsumerInstrumentation.java index 8cd53a2c479..f71cf372b61 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageConsumerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageConsumerInstrumentation.java @@ -34,10 +34,15 @@ public final class JMSMessageConsumerInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + private final String namespace; + + public JMSMessageConsumerInstrumentation(String namespace) { + this.namespace = namespace; + } @Override public String hierarchyMarkerType() { - return "javax.jms.MessageConsumer"; + return namespace + ".jms.MessageConsumer"; } @Override @@ -59,7 +64,7 @@ public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( isMethod() .and(named("setMessageListener")) - .and(takesArgument(0, hasInterface(named("javax.jms.MessageListener")))), + .and(takesArgument(0, hasInterface(named(namespace + ".jms.MessageListener")))), getClass().getName() + "$DecorateMessageListener"); } diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java index 7defd35ee4c..e65eb41726b 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java @@ -29,10 +29,15 @@ public final class JMSMessageProducerInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + private final String namespace; + + public JMSMessageProducerInstrumentation(String namespace) { + this.namespace = namespace; + } @Override public String hierarchyMarkerType() { - return "javax.jms.MessageProducer"; + return namespace + ".jms.MessageProducer"; } @Override @@ -43,12 +48,12 @@ public ElementMatcher hierarchyMatcher() { @Override public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( - named("send").and(takesArgument(0, named("javax.jms.Message"))).and(isPublic()), + named("send").and(takesArgument(0, named(namespace + ".jms.Message"))).and(isPublic()), JMSMessageProducerInstrumentation.class.getName() + "$ProducerAdvice"); transformer.applyAdvice( named("send") - .and(takesArgument(0, hasInterface(named("javax.jms.Destination")))) - .and(takesArgument(1, named("javax.jms.Message"))) + .and(takesArgument(0, hasInterface(named(namespace + ".jms.Destination")))) + .and(takesArgument(1, named(namespace + ".jms.Message"))) .and(isPublic()), JMSMessageProducerInstrumentation.class.getName() + "$ProducerWithDestinationAdvice"); } diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java index 23d8f8139f1..a1a3627dc3a 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java @@ -13,8 +13,15 @@ @AutoService(InstrumenterModule.class) public class JavaxJmsModule extends InstrumenterModule.Tracing { + private final String namespace; + public JavaxJmsModule() { - super("jms", "jms-1", "jms-2"); + this("javax", "jms", "jms-1", "jms-2"); + } + + public JavaxJmsModule(String namespace, String instrumentationName, String... additionalNames) { + super(instrumentationName, additionalNames); + this.namespace = namespace; } @Override @@ -31,20 +38,20 @@ public String[] helperClassNames() { @Override public Map contextStore() { Map contextStore = new HashMap<>(4); - contextStore.put("javax.jms.MessageConsumer", MessageConsumerState.class.getName()); - contextStore.put("javax.jms.MessageProducer", MessageProducerState.class.getName()); - contextStore.put("javax.jms.Message", SessionState.class.getName()); - contextStore.put("javax.jms.Session", SessionState.class.getName()); + contextStore.put(namespace + ".jms.MessageConsumer", MessageConsumerState.class.getName()); + contextStore.put(namespace + ".jms.MessageProducer", MessageProducerState.class.getName()); + contextStore.put(namespace + ".jms.Message", SessionState.class.getName()); + contextStore.put(namespace + ".jms.Session", SessionState.class.getName()); return contextStore; } @Override public List typeInstrumentations() { return Arrays.asList( - new JMSMessageConsumerInstrumentation(), - new JMSMessageProducerInstrumentation(), - new MDBMessageConsumerInstrumentation(), - new MessageInstrumentation(), - new SessionInstrumentation()); + new JMSMessageConsumerInstrumentation(namespace), + new JMSMessageProducerInstrumentation(namespace), + new MDBMessageConsumerInstrumentation(namespace), + new MessageInstrumentation(namespace), + new SessionInstrumentation(namespace)); } } diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MDBMessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MDBMessageConsumerInstrumentation.java index e2733c62100..dae0d88efb6 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MDBMessageConsumerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MDBMessageConsumerInstrumentation.java @@ -30,18 +30,23 @@ public final class MDBMessageConsumerInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + private final String namespace; + + public MDBMessageConsumerInstrumentation(String namespace) { + this.namespace = namespace; + } @Override public String hierarchyMarkerType() { - return "javax.jms.MessageListener"; + return namespace + ".jms.MessageListener"; } @Override public ElementMatcher hierarchyMatcher() { return implementsInterface(named(hierarchyMarkerType())) .and( - hasSuperType(declaresAnnotation(named("javax.ejb.MessageDriven"))) - .or(implementsInterface(named("javax.ejb.MessageDrivenBean")))); + hasSuperType(declaresAnnotation(named(namespace + ".ejb.MessageDriven"))) + .or(implementsInterface(named(namespace + ".ejb.MessageDrivenBean")))); } @Override @@ -51,7 +56,7 @@ public void methodAdvice(MethodTransformer transformer) { .and(isPublic()) .and(named("onMessage")) .and(takesArguments(1)) - .and(takesArgument(0, (named("javax.jms.Message")))), + .and(takesArgument(0, (named(namespace + ".jms.Message")))), getClass().getName() + "$MDBAdvice"); } diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MessageInstrumentation.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MessageInstrumentation.java index ec2119bdce5..8bfda025d32 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MessageInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/MessageInstrumentation.java @@ -17,10 +17,15 @@ public class MessageInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + private final String namespace; + + public MessageInstrumentation(String namespace) { + this.namespace = namespace; + } @Override public String hierarchyMarkerType() { - return "javax.jms.Message"; + return namespace + ".jms.Message"; } @Override diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java index 8b2f8db31ca..d447752711a 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java @@ -30,10 +30,15 @@ public class SessionInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { + private final String namespace; + + public SessionInstrumentation(String namespace) { + this.namespace = namespace; + } @Override public String hierarchyMarkerType() { - return "javax.jms.Session"; + return namespace + ".jms.Session"; } @Override @@ -47,38 +52,38 @@ public void methodAdvice(MethodTransformer transformer) { isMethod() .and(named("createProducer")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Destination"))), + .and(takesArgument(0, named(namespace + ".jms.Destination"))), getClass().getName() + "$CreateProducer"); transformer.applyAdvice( isMethod() .and(named("createSender")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Queue"))), + .and(takesArgument(0, named(namespace + ".jms.Queue"))), getClass().getName() + "$CreateProducer"); transformer.applyAdvice( isMethod() .and(named("createPublisher")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Topic"))), + .and(takesArgument(0, named(namespace + ".jms.Topic"))), getClass().getName() + "$CreateProducer"); transformer.applyAdvice( isMethod() .and(named("createConsumer")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Destination"))), + .and(takesArgument(0, named(namespace + ".jms.Destination"))), getClass().getName() + "$CreateConsumer"); transformer.applyAdvice( isMethod() .and(named("createReceiver")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Queue"))), + .and(takesArgument(0, named(namespace + ".jms.Queue"))), getClass().getName() + "$CreateConsumer"); transformer.applyAdvice( isMethod() .and(namedOneOf("createSubscriber", "createDurableSubscriber")) .and(isPublic()) - .and(takesArgument(0, named("javax.jms.Topic"))), + .and(takesArgument(0, named(namespace + ".jms.Topic"))), getClass().getName() + "$CreateConsumer"); transformer.applyAdvice( From 8aa6562f99fba1b45925a595b02e42bad22ce949 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Sat, 4 Jan 2025 01:44:39 +0000 Subject: [PATCH 2/2] Re-use JMS module for jakarta namespace --- .../instrumentation/jakarta-jms/build.gradle | 4 +- .../jakarta/jms/DatadogMessageListener.java | 84 ------- .../jakarta/jms/JMSDecorator.java | 230 ----------------- .../JMSMessageConsumerInstrumentation.java | 219 ---------------- .../JMSMessageProducerInstrumentation.java | 185 -------------- .../MDBMessageConsumerInstrumentation.java | 108 -------- .../jakarta/jms/MessageExtractAdapter.java | 73 ------ .../jakarta/jms/MessageInjectAdapter.java | 43 ---- .../jakarta/jms/MessageInstrumentation.java | 61 ----- .../jakarta/jms/SessionInstrumentation.java | 233 ------------------ .../instrumentation/jms/JakartaJmsModule.java | 24 ++ .../instrumentation/jms/build.gradle | 2 + .../instrumentation/jms/JavaxJmsModule.java | 5 + 13 files changed, 34 insertions(+), 1237 deletions(-) delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/DatadogMessageListener.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSDecorator.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageConsumerInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageProducerInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MDBMessageConsumerInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageExtractAdapter.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInjectAdapter.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/SessionInstrumentation.java create mode 100644 dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jms/JakartaJmsModule.java diff --git a/dd-java-agent/instrumentation/jakarta-jms/build.gradle b/dd-java-agent/instrumentation/jakarta-jms/build.gradle index c1682486974..9f3c53f4af5 100644 --- a/dd-java-agent/instrumentation/jakarta-jms/build.gradle +++ b/dd-java-agent/instrumentation/jakarta-jms/build.gradle @@ -4,6 +4,7 @@ ext { muzzle { pass { + name = "jakarta.jms" group = "jakarta.jms" module = "jakarta.jms-api" versions = "[3.0.0,4)" @@ -28,7 +29,8 @@ compileTestGroovy { } dependencies { - compileOnly 'jakarta.jms:jakarta.jms-api:3.0.0' + implementation project(':dd-java-agent:instrumentation:jms') + testImplementation 'jakarta.jms:jakarta.jms-api:3.0.0' testImplementation 'jakarta.ejb:jakarta.ejb-api:4.0.0' testImplementation group: 'org.springframework', name: 'spring-jms', version: '6.0.11' diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/DatadogMessageListener.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/DatadogMessageListener.java deleted file mode 100644 index 9ddab9f436c..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/DatadogMessageListener.java +++ /dev/null @@ -1,84 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.BROKER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.CONSUMER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_CONSUME; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_DELIVER; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.TIME_IN_QUEUE_ENABLED; -import static datadog.trace.instrumentation.jakarta.jms.MessageExtractAdapter.GETTER; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import datadog.trace.bootstrap.ContextStore; -import datadog.trace.bootstrap.instrumentation.api.AgentScope; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.jms.MessageConsumerState; -import datadog.trace.bootstrap.instrumentation.jms.SessionState; -import jakarta.jms.Message; -import jakarta.jms.MessageListener; - -public class DatadogMessageListener implements MessageListener { - - private final ContextStore messageAckStore; - private final MessageConsumerState consumerState; - private final MessageListener messageListener; - - public DatadogMessageListener( - ContextStore messageAckStore, - MessageConsumerState consumerState, - MessageListener messageListener) { - this.messageAckStore = messageAckStore; - this.consumerState = consumerState; - this.messageListener = messageListener; - } - - @Override - public void onMessage(Message message) { - AgentSpan span; - AgentSpan.Context propagatedContext = null; - if (!consumerState.isPropagationDisabled()) { - propagatedContext = propagate().extract(message, GETTER); - } - long startMillis = GETTER.extractTimeInQueueStart(message); - if (startMillis == 0 || !TIME_IN_QUEUE_ENABLED) { - span = startSpan(JMS_CONSUME, propagatedContext); - } else { - long batchId = GETTER.extractMessageBatchId(message); - AgentSpan timeInQueue = consumerState.getTimeInQueueSpan(batchId); - if (null == timeInQueue) { - timeInQueue = startSpan(JMS_DELIVER, propagatedContext, MILLISECONDS.toMicros(startMillis)); - BROKER_DECORATE.afterStart(timeInQueue); - BROKER_DECORATE.onTimeInQueue( - timeInQueue, - consumerState.getBrokerResourceName(), - consumerState.getBrokerServiceName()); - consumerState.setTimeInQueueSpan(batchId, timeInQueue); - } - span = startSpan(JMS_CONSUME, timeInQueue.context()); - } - CONSUMER_DECORATE.afterStart(span); - CONSUMER_DECORATE.onConsume(span, message, consumerState.getConsumerResourceName()); - SessionState sessionState = consumerState.getSessionState(); - if (sessionState.isClientAcknowledge()) { - // consumed spans will be finished by a call to Message.acknowledge - sessionState.finishOnAcknowledge(span); - messageAckStore.put(message, sessionState); - } else if (sessionState.isTransactedSession()) { - // span will be finished by Session.commit/rollback/close - sessionState.finishOnCommit(span); - } - try (AgentScope scope = activateSpan(span)) { - messageListener.onMessage(message); - } catch (RuntimeException | Error thrown) { - CONSUMER_DECORATE.onError(span, thrown); - throw thrown; - } finally { - if (sessionState.isAutoAcknowledge()) { - span.finish(); - consumerState.finishTimeInQueueSpan(false); - } - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSDecorator.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSDecorator.java deleted file mode 100644 index 7d4b7530de9..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSDecorator.java +++ /dev/null @@ -1,230 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.RECORD_QUEUE_TIME_MS; - -import datadog.trace.api.Config; -import datadog.trace.api.Functions.Join; -import datadog.trace.api.Functions.PrefixJoin; -import datadog.trace.api.cache.DDCache; -import datadog.trace.api.cache.DDCaches; -import datadog.trace.api.naming.SpanNaming; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes; -import datadog.trace.bootstrap.instrumentation.api.Tags; -import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; -import datadog.trace.bootstrap.instrumentation.decorator.MessagingClientDecorator; -import jakarta.jms.Destination; -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import jakarta.jms.Queue; -import jakarta.jms.TemporaryQueue; -import jakarta.jms.TemporaryTopic; -import jakarta.jms.Topic; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.function.Supplier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class JMSDecorator extends MessagingClientDecorator { - private static final Logger log = LoggerFactory.getLogger(JMSDecorator.class); - - public static final CharSequence JMS = UTF8BytesString.create("jms"); - public static final CharSequence JMS_CONSUME = - UTF8BytesString.create( - SpanNaming.instance().namingSchema().messaging().inboundOperation(JMS.toString())); - public static final CharSequence JMS_PRODUCE = - UTF8BytesString.create( - SpanNaming.instance().namingSchema().messaging().outboundOperation(JMS.toString())); - public static final CharSequence JMS_DELIVER = UTF8BytesString.create("jms.deliver"); - - public static final boolean JMS_LEGACY_TRACING = Config.get().isJmsLegacyTracingEnabled(); - - public static final boolean TIME_IN_QUEUE_ENABLED = - Config.get().isTimeInQueueEnabled(!JMS_LEGACY_TRACING, "jms"); - public static final String JMS_PRODUCED_KEY = "x_datadog_jms_produced"; - public static final String JMS_BATCH_ID_KEY = "x_datadog_jms_batch_id"; - - private static final Join QUEUE_JOINER = PrefixJoin.of("Queue "); - private static final Join TOPIC_JOINER = PrefixJoin.of("Topic "); - - private final DDCache resourceNameCache = - DDCaches.newFixedSizeCache(32); - - private final String resourcePrefix; - - private final UTF8BytesString queueTempResourceName; - private final UTF8BytesString topicTempResourceName; - - private final Function queueResourceJoiner; - private final Function topicResourceJoiner; - - private final String spanKind; - private final CharSequence spanType; - private final Supplier serviceNameSupplier; - - public static final JMSDecorator PRODUCER_DECORATE = - new JMSDecorator( - "Produced for ", - Tags.SPAN_KIND_PRODUCER, - InternalSpanTypes.MESSAGE_PRODUCER, - SpanNaming.instance() - .namingSchema() - .messaging() - .outboundService(JMS.toString(), JMS_LEGACY_TRACING)); - - public static final JMSDecorator CONSUMER_DECORATE = - new JMSDecorator( - "Consumed from ", - Tags.SPAN_KIND_CONSUMER, - InternalSpanTypes.MESSAGE_CONSUMER, - SpanNaming.instance() - .namingSchema() - .messaging() - .inboundService(JMS.toString(), JMS_LEGACY_TRACING)); - public static final JMSDecorator BROKER_DECORATE = - new JMSDecorator( - "", - Tags.SPAN_KIND_BROKER, - InternalSpanTypes.MESSAGE_BROKER, - SpanNaming.instance().namingSchema().messaging().timeInQueueService(JMS.toString())); - - public JMSDecorator( - String resourcePrefix, - String spanKind, - CharSequence spanType, - Supplier serviceNameSupplier) { - this.resourcePrefix = resourcePrefix; - - this.queueTempResourceName = UTF8BytesString.create(resourcePrefix + "Temporary Queue"); - this.topicTempResourceName = UTF8BytesString.create(resourcePrefix + "Temporary Topic"); - - this.queueResourceJoiner = QUEUE_JOINER.curry(resourcePrefix); - this.topicResourceJoiner = TOPIC_JOINER.curry(resourcePrefix); - - this.spanKind = spanKind; - this.spanType = spanType; - this.serviceNameSupplier = serviceNameSupplier; - } - - @Override - protected String[] instrumentationNames() { - return new String[] {"jakarta-jms"}; - } - - @Override - protected CharSequence spanType() { - return spanType; - } - - @Override - protected String service() { - return serviceNameSupplier.get(); - } - - @Override - protected CharSequence component() { - return JMS; - } - - @Override - protected String spanKind() { - return spanKind; - } - - public static void logJMSException(JMSException ex) { - if (log.isDebugEnabled()) { - log.debug("JMS exception during instrumentation", ex); - } - } - - public void onConsume(AgentSpan span, Message message, CharSequence resourceName) { - if (null != resourceName) { - span.setResourceName(resourceName); - } - - try { - final long produceTime = message.getJMSTimestamp(); - if (produceTime > 0) { - final long consumeTime = TimeUnit.NANOSECONDS.toMillis(span.getStartTime()); - span.setTag(RECORD_QUEUE_TIME_MS, Math.max(0L, consumeTime - produceTime)); - } - } catch (Exception e) { - log.debug("Unable to get jms timestamp", e); - } - } - - public void onProduce(AgentSpan span, CharSequence resourceName) { - if (null != resourceName) { - span.setResourceName(resourceName); - } - } - - public static boolean canInject(Message message) { - // JMS->SQS already stores the trace context in 'X-Amzn-Trace-Id' / 'AWSTraceHeader', - // so skip storing same context again to avoid SQS limit of 10 attributes per message. - return !message.getClass().getName().startsWith("com.amazon.sqs.javamessaging"); - } - - public void onTimeInQueue(AgentSpan span, CharSequence resourceName, String serviceName) { - if (null != resourceName) { - span.setResourceName(resourceName); - } - if (null != serviceName) { - span.setServiceName(serviceName); - } - } - - private static final String TIBCO_TMP_PREFIX = "$TMP$"; - - public CharSequence toResourceName(String destinationName, boolean isQueue) { - if (null == destinationName) { - return isQueue ? queueTempResourceName : topicTempResourceName; - } - Function joiner = - isQueue ? queueResourceJoiner : topicResourceJoiner; - // some systems may have queues and topics with the same name - since we won't know which was - // cached first we check the character after the initial prefix to see if it's Q (for Queue) - - // if that's what we expect we can use the cached value, otherwise generate the correct name - CharSequence resourceName = resourceNameCache.computeIfAbsent(destinationName, joiner); - if ((resourceName.charAt(resourcePrefix.length()) == 'Q') == isQueue) { - return resourceName; - } - return joiner.apply(destinationName); - } - - public String getDestinationName(Destination destination) { - String name = null; - try { - if (destination instanceof Queue) { - // WebLogic mixes all JMS Destination interfaces in a single base type which means we can't - // rely on instanceof and have to instead check the result of getQueueName vs getTopicName - if (!(destination instanceof TemporaryQueue) || isWebLogicDestination(destination)) { - name = ((Queue) destination).getQueueName(); - } - } - // check Topic name if Queue name is null because this might be a WebLogic destination - if (null == name && destination instanceof Topic) { - if (!(destination instanceof TemporaryTopic) || isWebLogicDestination(destination)) { - name = ((Topic) destination).getTopicName(); - } - } - } catch (Exception e) { - log.debug("Unable to get jms destination name", e); - } - return null != name && !name.startsWith(TIBCO_TMP_PREFIX) ? name : null; - } - - public boolean isQueue(Destination destination) { - try { - // handle WebLogic by treating everything as a Queue unless it's a Topic with a name - return !(destination instanceof Topic) || null == ((Topic) destination).getTopicName(); - } catch (Exception e) { - return true; // assume it's a Queue if we can't check the details - } - } - - private static boolean isWebLogicDestination(Destination destination) { - return destination.getClass().getName().startsWith("weblogic.jms.common."); - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageConsumerInstrumentation.java deleted file mode 100644 index f1008a36607..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageConsumerInstrumentation.java +++ /dev/null @@ -1,219 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateNext; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.closePrevious; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.BROKER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.CONSUMER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_CONSUME; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_DELIVER; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.TIME_IN_QUEUE_ENABLED; -import static datadog.trace.instrumentation.jakarta.jms.MessageExtractAdapter.GETTER; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; -import datadog.trace.bootstrap.InstrumentationContext; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.jms.MessageConsumerState; -import datadog.trace.bootstrap.instrumentation.jms.SessionState; -import jakarta.jms.Message; -import jakarta.jms.MessageConsumer; -import jakarta.jms.MessageListener; -import java.util.HashMap; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; - -@AutoService(InstrumenterModule.class) -public final class JMSMessageConsumerInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { - - public JMSMessageConsumerInstrumentation() { - super("jakarta-jms"); - } - - @Override - public String hierarchyMarkerType() { - return "jakarta.jms.MessageConsumer"; - } - - @Override - public ElementMatcher hierarchyMatcher() { - return implementsInterface(named(hierarchyMarkerType())); - } - - @Override - public String[] helperClassNames() { - return new String[] { - packageName + ".JMSDecorator", - packageName + ".MessageExtractAdapter", - packageName + ".MessageExtractAdapter$1", - packageName + ".DatadogMessageListener" - }; - } - - @Override - public Map contextStore() { - Map contextStore = new HashMap<>(4); - contextStore.put("jakarta.jms.MessageConsumer", MessageConsumerState.class.getName()); - contextStore.put("jakarta.jms.Message", SessionState.class.getName()); - return contextStore; - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - named("receive").and(takesArguments(0).or(takesArguments(1))).and(isPublic()), - JMSMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice"); - transformer.applyAdvice( - named("receiveNoWait").and(takesArguments(0)).and(isPublic()), - JMSMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice"); - transformer.applyAdvice( - named("close").and(takesArguments(0)).and(isPublic()), - JMSMessageConsumerInstrumentation.class.getName() + "$Close"); - transformer.applyAdvice( - isMethod() - .and(named("setMessageListener")) - .and(takesArgument(0, hasInterface(named("jakarta.jms.MessageListener")))), - getClass().getName() + "$DecorateMessageListener"); - } - - public static class ConsumerAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static MessageConsumerState beforeReceive(@Advice.This final MessageConsumer consumer) { - MessageConsumerState consumerState = - InstrumentationContext.get(MessageConsumer.class, MessageConsumerState.class) - .get(consumer); - - // ignore consumers who aren't bound to a tracked session via consumerState - if (null == consumerState) { - return null; - } - - boolean finishSpan = consumerState.getSessionState().isAutoAcknowledge(); - closePrevious(finishSpan); - if (finishSpan) { - consumerState.finishTimeInQueueSpan(false); - } - - // don't create spans for nested receive calls, even if different consumers are involved - final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(MessageConsumer.class); - if (callDepth > 0) { - return null; - } - - return consumerState; - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void afterReceive( - @Advice.Enter final MessageConsumerState consumerState, - @Advice.This final MessageConsumer consumer, - @Advice.Return final Message message, - @Advice.Thrown final Throwable throwable) { - - if (consumerState == null) { - // either we're not tracking the consumer or this is a nested receive - return; - } - - // outermost receive call - make sure we reset call-depth before returning - CallDepthThreadLocalMap.reset(MessageConsumer.class); - - if (message == null) { - // don't create spans (traces) for each poll if the queue is empty - return; - } - - AgentSpan span; - AgentSpan.Context propagatedContext = null; - if (!consumerState.isPropagationDisabled()) { - propagatedContext = propagate().extract(message, GETTER); - } - long startMillis = GETTER.extractTimeInQueueStart(message); - if (startMillis == 0 || !TIME_IN_QUEUE_ENABLED) { - span = startSpan(JMS_CONSUME, propagatedContext); - } else { - long batchId = GETTER.extractMessageBatchId(message); - AgentSpan timeInQueue = consumerState.getTimeInQueueSpan(batchId); - if (null == timeInQueue) { - timeInQueue = - startSpan(JMS_DELIVER, propagatedContext, MILLISECONDS.toMicros(startMillis)); - BROKER_DECORATE.afterStart(timeInQueue); - BROKER_DECORATE.onTimeInQueue( - timeInQueue, - consumerState.getBrokerResourceName(), - consumerState.getBrokerServiceName()); - consumerState.setTimeInQueueSpan(batchId, timeInQueue); - } - span = startSpan(JMS_CONSUME, timeInQueue.context()); - } - - CONSUMER_DECORATE.afterStart(span); - CONSUMER_DECORATE.onConsume(span, message, consumerState.getConsumerResourceName()); - CONSUMER_DECORATE.onError(span, throwable); - - activateNext(span); // scope is left open until next message or it times out - - SessionState sessionState = consumerState.getSessionState(); - if (sessionState.isClientAcknowledge()) { - // consumed spans will be finished by a call to Message.acknowledge - sessionState.finishOnAcknowledge(span); - InstrumentationContext.get(Message.class, SessionState.class).put(message, sessionState); - } else if (sessionState.isTransactedSession()) { - // span will be finished by Session.commit/rollback/close - sessionState.finishOnCommit(span); - } - // for AUTO_ACKNOWLEDGE, span is not finished until next call to receive, or close - } - } - - public static class Close { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void beforeClose(@Advice.This final MessageConsumer consumer) { - MessageConsumerState consumerState = - InstrumentationContext.get(MessageConsumer.class, MessageConsumerState.class) - .get(consumer); - if (null != consumerState) { - boolean finishSpan = consumerState.getSessionState().isAutoAcknowledge(); - closePrevious(finishSpan); - if (finishSpan) { - consumerState.finishTimeInQueueSpan(true); - } - } - } - } - - public static class DecorateMessageListener { - @Advice.OnMethodEnter - public static void setMessageListener( - @Advice.This MessageConsumer messageConsumer, - @Advice.Argument(value = 0, readOnly = false) MessageListener listener) { - if (null != listener && !(listener instanceof DatadogMessageListener)) { - MessageConsumerState consumerState = - InstrumentationContext.get(MessageConsumer.class, MessageConsumerState.class) - .get(messageConsumer); - if (null != consumerState) { - listener = - new DatadogMessageListener( - InstrumentationContext.get(Message.class, SessionState.class), - consumerState, - listener); - } - } - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageProducerInstrumentation.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageProducerInstrumentation.java deleted file mode 100644 index bfaa0141f73..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/JMSMessageProducerInstrumentation.java +++ /dev/null @@ -1,185 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_PRODUCE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.PRODUCER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.TIME_IN_QUEUE_ENABLED; -import static datadog.trace.instrumentation.jakarta.jms.MessageInjectAdapter.SETTER; -import static java.util.Collections.singletonMap; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.api.Config; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; -import datadog.trace.bootstrap.InstrumentationContext; -import datadog.trace.bootstrap.instrumentation.api.AgentScope; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.jms.MessageProducerState; -import jakarta.jms.Destination; -import jakarta.jms.Message; -import jakarta.jms.MessageProducer; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; - -@AutoService(InstrumenterModule.class) -public final class JMSMessageProducerInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { - - public JMSMessageProducerInstrumentation() { - super("jakarta-jms"); - } - - @Override - public String hierarchyMarkerType() { - return "jakarta.jms.MessageProducer"; - } - - @Override - public ElementMatcher hierarchyMatcher() { - return implementsInterface(named(hierarchyMarkerType())); - } - - @Override - public String[] helperClassNames() { - return new String[] {packageName + ".JMSDecorator", packageName + ".MessageInjectAdapter"}; - } - - @Override - public Map contextStore() { - return singletonMap("jakarta.jms.MessageProducer", MessageProducerState.class.getName()); - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - named("send").and(takesArgument(0, named("jakarta.jms.Message"))).and(isPublic()), - JMSMessageProducerInstrumentation.class.getName() + "$ProducerAdvice"); - transformer.applyAdvice( - named("send") - .and(takesArgument(0, hasInterface(named("jakarta.jms.Destination")))) - .and(takesArgument(1, named("jakarta.jms.Message"))) - .and(isPublic()), - JMSMessageProducerInstrumentation.class.getName() + "$ProducerWithDestinationAdvice"); - } - - public static class ProducerAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope beforeSend( - @Advice.Argument(0) final Message message, @Advice.This final MessageProducer producer) { - final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(MessageProducer.class); - if (callDepth > 0) { - return null; - } - - MessageProducerState producerState = - InstrumentationContext.get(MessageProducer.class, MessageProducerState.class) - .get(producer); - - CharSequence resourceName; - - if (null != producerState) { - resourceName = producerState.getResourceName(); - } else { - try { - // fall-back when producer wasn't created via standard Session.createProducer API - Destination destination = producer.getDestination(); - boolean isQueue = PRODUCER_DECORATE.isQueue(destination); - String destinationName = PRODUCER_DECORATE.getDestinationName(destination); - resourceName = PRODUCER_DECORATE.toResourceName(destinationName, isQueue); - } catch (Exception ignored) { - resourceName = "Unknown Destination"; - } - } - - final AgentSpan span = startSpan(JMS_PRODUCE); - PRODUCER_DECORATE.afterStart(span); - PRODUCER_DECORATE.onProduce(span, resourceName); - if (JMSDecorator.canInject(message)) { - if (Config.get().isJmsPropagationEnabled() - && (null == producerState || !producerState.isPropagationDisabled())) { - propagate().inject(span, message, SETTER); - } - if (TIME_IN_QUEUE_ENABLED) { - if (null != producerState) { - SETTER.injectTimeInQueue(message, producerState); - } - } - } - return activateSpan(span); - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void afterSend( - @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { - if (scope == null) { - return; - } - PRODUCER_DECORATE.onError(scope, throwable); - PRODUCER_DECORATE.beforeFinish(scope); - scope.close(); - scope.span().finish(); - CallDepthThreadLocalMap.reset(MessageProducer.class); - } - } - - public static class ProducerWithDestinationAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope beforeSend( - @Advice.Argument(0) final Destination destination, - @Advice.Argument(1) final Message message, - @Advice.This final MessageProducer producer) { - final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(MessageProducer.class); - if (callDepth > 0) { - return null; - } - - boolean isQueue = PRODUCER_DECORATE.isQueue(destination); - String destinationName = PRODUCER_DECORATE.getDestinationName(destination); - CharSequence resourceName = PRODUCER_DECORATE.toResourceName(destinationName, isQueue); - - final AgentSpan span = startSpan(JMS_PRODUCE); - PRODUCER_DECORATE.afterStart(span); - PRODUCER_DECORATE.onProduce(span, resourceName); - if (JMSDecorator.canInject(message)) { - if (Config.get().isJmsPropagationEnabled() - && !Config.get().isJmsPropagationDisabledForDestination(destinationName)) { - propagate().inject(span, message, SETTER); - } - if (TIME_IN_QUEUE_ENABLED) { - MessageProducerState producerState = - InstrumentationContext.get(MessageProducer.class, MessageProducerState.class) - .get(producer); - if (null != producerState) { - SETTER.injectTimeInQueue(message, producerState); - } - } - } - return activateSpan(span); - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void afterSend( - @Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) { - if (scope == null) { - return; - } - PRODUCER_DECORATE.onError(scope, throwable); - PRODUCER_DECORATE.beforeFinish(scope); - scope.close(); - scope.span().finish(); - CallDepthThreadLocalMap.reset(MessageProducer.class); - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MDBMessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MDBMessageConsumerInstrumentation.java deleted file mode 100644 index da82334e3d4..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MDBMessageConsumerInstrumentation.java +++ /dev/null @@ -1,108 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresAnnotation; -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.hasSuperType; -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.CONSUMER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_CONSUME; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.logJMSException; -import static datadog.trace.instrumentation.jakarta.jms.MessageExtractAdapter.GETTER; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; -import datadog.trace.bootstrap.instrumentation.api.AgentScope; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import jakarta.jms.Destination; -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import jakarta.jms.MessageListener; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; - -@AutoService(InstrumenterModule.class) -public final class MDBMessageConsumerInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { - - public MDBMessageConsumerInstrumentation() { - super("jakarta-jms", "jakarta-mdb"); - } - - @Override - public String[] helperClassNames() { - return new String[] { - packageName + ".JMSDecorator", - packageName + ".MessageExtractAdapter", - packageName + ".MessageExtractAdapter$1" - }; - } - - @Override - public String hierarchyMarkerType() { - return "jakarta.jms.MessageListener"; - } - - @Override - public ElementMatcher hierarchyMatcher() { - return implementsInterface(named(hierarchyMarkerType())) - .and( - hasSuperType(declaresAnnotation(named("jakarta.ejb.MessageDriven"))) - .or(implementsInterface(named("jakarta.ejb.MessageDrivenBean")))); - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - isMethod() - .and(isPublic()) - .and(named("onMessage")) - .and(takesArguments(1)) - .and(takesArgument(0, (named("jakarta.jms.Message")))), - getClass().getName() + "$MDBAdvice"); - } - - public static class MDBAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope methodEnter(@Advice.Argument(0) final Message message) { - if (CallDepthThreadLocalMap.incrementCallDepth(MessageListener.class) > 0) { - return null; - } - AgentSpan.Context propagatedContext = propagate().extract(message, GETTER); - AgentSpan span = startSpan(JMS_CONSUME, propagatedContext); - CONSUMER_DECORATE.afterStart(span); - CharSequence consumerResourceName; - try { - Destination destination = message.getJMSDestination(); - boolean isQueue = CONSUMER_DECORATE.isQueue(destination); - String destinationName = CONSUMER_DECORATE.getDestinationName(destination); - consumerResourceName = CONSUMER_DECORATE.toResourceName(destinationName, isQueue); - } catch (JMSException e) { - logJMSException(e); - consumerResourceName = "unknown JMS destination"; - } - CONSUMER_DECORATE.onConsume(span, message, consumerResourceName); - return activateSpan(span); - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void methodExit( - @Advice.Enter AgentScope scope, @Advice.Thrown final Throwable throwable) { - if (null != scope) { - CallDepthThreadLocalMap.reset(MessageListener.class); - CONSUMER_DECORATE.onError(scope, throwable); - scope.close(); - scope.span().finish(); - } - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageExtractAdapter.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageExtractAdapter.java deleted file mode 100644 index 456ad0a063b..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageExtractAdapter.java +++ /dev/null @@ -1,73 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_BATCH_ID_KEY; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_PRODUCED_KEY; - -import datadog.trace.api.cache.DDCache; -import datadog.trace.api.cache.DDCaches; -import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; -import de.thetaphi.forbiddenapis.SuppressForbidden; -import jakarta.jms.JMSException; -import jakarta.jms.Message; -import java.util.Enumeration; -import java.util.Locale; -import java.util.function.Function; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class MessageExtractAdapter implements AgentPropagation.ContextVisitor { - private static final Logger log = LoggerFactory.getLogger(MessageExtractAdapter.class); - private static final Function KEY_MAPPER = - new Function() { - @SuppressForbidden - @Override - public String apply(String key) { - return key.replace("__dash__", "-").replace('$', '-').toLowerCase(Locale.ROOT); - } - }; - - private final DDCache cache = DDCaches.newFixedSizeCache(32); - - public static final MessageExtractAdapter GETTER = new MessageExtractAdapter(); - - @Override - public void forEachKey(Message carrier, AgentPropagation.KeyClassifier classifier) { - try { - final Enumeration enumeration = carrier.getPropertyNames(); - if (null != enumeration) { - while (enumeration.hasMoreElements()) { - String key = ((String) enumeration.nextElement()); - String lowerCaseKey = cache.computeIfAbsent(key, KEY_MAPPER); - Object value = carrier.getObjectProperty(key); - if (value instanceof String && !classifier.accept(lowerCaseKey, (String) value)) { - return; - } - } - } - } catch (JMSException e) { - throw new RuntimeException(e); - } - } - - public long extractTimeInQueueStart(final Message carrier) { - try { - if (carrier.propertyExists(JMS_PRODUCED_KEY)) { - return carrier.getLongProperty(JMS_PRODUCED_KEY); - } - } catch (Exception e) { - log.debug("Unable to get jms produced time", e); - } - return 0; - } - - public long extractMessageBatchId(final Message carrier) { - try { - if (carrier.propertyExists(JMS_BATCH_ID_KEY)) { - return carrier.getLongProperty(JMS_BATCH_ID_KEY); - } - } catch (Exception e) { - log.debug("Unable to get jms batch id", e); - } - return 0; - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInjectAdapter.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInjectAdapter.java deleted file mode 100644 index b5c3b5800c0..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInjectAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_BATCH_ID_KEY; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_PRODUCED_KEY; - -import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; -import datadog.trace.bootstrap.instrumentation.jms.MessageBatchState; -import datadog.trace.bootstrap.instrumentation.jms.MessageProducerState; -import de.thetaphi.forbiddenapis.SuppressForbidden; -import jakarta.jms.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageInjectAdapter implements AgentPropagation.Setter { - private static final Logger log = LoggerFactory.getLogger(MessageInjectAdapter.class); - - public static final MessageInjectAdapter SETTER = new MessageInjectAdapter(); - - @SuppressForbidden - @Override - public void set(final Message carrier, final String key, final String value) { - final String propName = key.replace("-", "__dash__"); - try { - carrier.setStringProperty(propName, value); - } catch (Exception e) { - log.debug("Failure setting jms property: {}", propName, e); - } - } - - public void injectTimeInQueue(final Message carrier, final MessageProducerState producerState) { - try { - if (producerState.getSessionState().isTransactedSession()) { - MessageBatchState batchState = producerState.currentBatchState(); - carrier.setLongProperty(JMS_BATCH_ID_KEY, batchState.getBatchId()); - carrier.setLongProperty(JMS_PRODUCED_KEY, batchState.getStartMillis()); - } else { - carrier.setLongProperty(JMS_PRODUCED_KEY, System.currentTimeMillis()); - } - } catch (Exception e) { - log.debug("Failure setting jms batch details", e); - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInstrumentation.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInstrumentation.java deleted file mode 100644 index 9576a5cfc0e..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/MessageInstrumentation.java +++ /dev/null @@ -1,61 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static java.util.Collections.singletonMap; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.bootstrap.InstrumentationContext; -import datadog.trace.bootstrap.instrumentation.jms.SessionState; -import jakarta.jms.Message; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; - -@AutoService(InstrumenterModule.class) -public class MessageInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { - public MessageInstrumentation() { - super("jakarta-jms"); - } - - @Override - public String hierarchyMarkerType() { - return "jakarta.jms.Message"; - } - - @Override - public ElementMatcher hierarchyMatcher() { - return implementsInterface(named(hierarchyMarkerType())); - } - - @Override - public Map contextStore() { - return singletonMap("jakarta.jms.Message", SessionState.class.getName()); - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - nameStartsWith("acknowledge").and(isMethod()).and(isPublic()).and(takesNoArguments()), - getClass().getName() + "$Acknowledge"); - } - - public static final class Acknowledge { - @Advice.OnMethodExit - public static void acknowledge(@Advice.This Message message) { - SessionState sessionState = - InstrumentationContext.get(Message.class, SessionState.class).get(message); - if (null != sessionState && sessionState.isClientAcknowledge()) { - sessionState.onAcknowledgeOrRecover(); - } - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/SessionInstrumentation.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/SessionInstrumentation.java deleted file mode 100644 index 875ba7a4cbb..00000000000 --- a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jakarta/jms/SessionInstrumentation.java +++ /dev/null @@ -1,233 +0,0 @@ -package datadog.trace.instrumentation.jakarta.jms; - -import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.implementsInterface; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.BROKER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.CONSUMER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.JMS_LEGACY_TRACING; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.PRODUCER_DECORATE; -import static datadog.trace.instrumentation.jakarta.jms.JMSDecorator.TIME_IN_QUEUE_ENABLED; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.api.Config; -import datadog.trace.bootstrap.ContextStore; -import datadog.trace.bootstrap.InstrumentationContext; -import datadog.trace.bootstrap.instrumentation.jms.MessageConsumerState; -import datadog.trace.bootstrap.instrumentation.jms.MessageProducerState; -import datadog.trace.bootstrap.instrumentation.jms.SessionState; -import jakarta.jms.Destination; -import jakarta.jms.MessageConsumer; -import jakarta.jms.MessageProducer; -import jakarta.jms.Session; -import java.util.HashMap; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; - -@AutoService(InstrumenterModule.class) -public class SessionInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { - public SessionInstrumentation() { - super("jakarta-jms"); - } - - @Override - public String hierarchyMarkerType() { - return "jakarta.jms.Session"; - } - - @Override - public ElementMatcher hierarchyMatcher() { - return implementsInterface(named(hierarchyMarkerType())); - } - - @Override - public String[] helperClassNames() { - return new String[] {packageName + ".JMSDecorator"}; - } - - @Override - public Map contextStore() { - Map contextStore = new HashMap<>(4); - contextStore.put("jakarta.jms.MessageConsumer", MessageConsumerState.class.getName()); - contextStore.put("jakarta.jms.MessageProducer", MessageProducerState.class.getName()); - contextStore.put("jakarta.jms.Session", SessionState.class.getName()); - return contextStore; - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - isMethod() - .and(named("createProducer")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Destination"))), - getClass().getName() + "$CreateProducer"); - transformer.applyAdvice( - isMethod() - .and(named("createSender")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Queue"))), - getClass().getName() + "$CreateProducer"); - transformer.applyAdvice( - isMethod() - .and(named("createPublisher")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Topic"))), - getClass().getName() + "$CreateProducer"); - - transformer.applyAdvice( - isMethod() - .and(named("createConsumer")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Destination"))), - getClass().getName() + "$CreateConsumer"); - transformer.applyAdvice( - isMethod() - .and(named("createReceiver")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Queue"))), - getClass().getName() + "$CreateConsumer"); - transformer.applyAdvice( - isMethod() - .and(namedOneOf("createSubscriber", "createDurableSubscriber")) - .and(isPublic()) - .and(takesArgument(0, named("jakarta.jms.Topic"))), - getClass().getName() + "$CreateConsumer"); - - transformer.applyAdvice( - namedOneOf("recover").and(takesNoArguments()), getClass().getName() + "$Recover"); - transformer.applyAdvice( - namedOneOf("commit", "rollback").and(takesNoArguments()), getClass().getName() + "$Commit"); - transformer.applyAdvice( - named("close").and(takesNoArguments()), getClass().getName() + "$Close"); - } - - public static final class CreateProducer { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void bindProducerState( - @Advice.This Session session, - @Advice.Argument(0) Destination destination, - @Advice.Return MessageProducer producer) { - - ContextStore producerStateStore = - InstrumentationContext.get(MessageProducer.class, MessageProducerState.class); - - // avoid doing the same thing more than once when there is delegation to overloads - if (producerStateStore.get(producer) == null) { - ContextStore sessionStateStore = - InstrumentationContext.get(Session.class, SessionState.class); - - SessionState sessionState = sessionStateStore.get(session); - if (null == sessionState) { - int ackMode; - try { - ackMode = session.getAcknowledgeMode(); - } catch (Exception ignored) { - ackMode = Session.AUTO_ACKNOWLEDGE; - } - sessionState = - sessionStateStore.putIfAbsent( - session, new SessionState(ackMode, TIME_IN_QUEUE_ENABLED)); - } - - boolean isQueue = PRODUCER_DECORATE.isQueue(destination); - String destinationName = PRODUCER_DECORATE.getDestinationName(destination); - CharSequence resourceName = PRODUCER_DECORATE.toResourceName(destinationName, isQueue); - - boolean propagationDisabled = - Config.get().isJmsPropagationDisabledForDestination(destinationName); - - producerStateStore.put( - producer, new MessageProducerState(sessionState, resourceName, propagationDisabled)); - } - } - } - - public static final class CreateConsumer { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void bindConsumerState( - @Advice.This Session session, - @Advice.Argument(0) Destination destination, - @Advice.Return MessageConsumer consumer) { - - ContextStore consumerStateStore = - InstrumentationContext.get(MessageConsumer.class, MessageConsumerState.class); - - // avoid doing the same thing more than once when there is delegation to overloads - if (consumerStateStore.get(consumer) == null) { - ContextStore sessionStateStore = - InstrumentationContext.get(Session.class, SessionState.class); - - SessionState sessionState = sessionStateStore.get(session); - if (null == sessionState) { - int ackMode; - try { - ackMode = session.getAcknowledgeMode(); - } catch (Exception ignored) { - ackMode = Session.AUTO_ACKNOWLEDGE; - } - sessionState = - sessionStateStore.putIfAbsent( - session, new SessionState(ackMode, TIME_IN_QUEUE_ENABLED)); - } - - boolean isQueue = CONSUMER_DECORATE.isQueue(destination); - String destinationName = CONSUMER_DECORATE.getDestinationName(destination); - CharSequence brokerResourceName = - JMS_LEGACY_TRACING ? "jms" : BROKER_DECORATE.toResourceName(destinationName, isQueue); - CharSequence consumerResourceName = - CONSUMER_DECORATE.toResourceName(destinationName, isQueue); - - boolean propagationDisabled = - Config.get().isJmsPropagationDisabledForDestination(destinationName); - - consumerStateStore.put( - consumer, - new MessageConsumerState( - sessionState, brokerResourceName, consumerResourceName, propagationDisabled)); - } - } - } - - public static final class Recover { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void recover(@Advice.This Session session) { - SessionState sessionState = - InstrumentationContext.get(Session.class, SessionState.class).get(session); - if (null != sessionState && sessionState.isClientAcknowledge()) { - sessionState.onAcknowledgeOrRecover(); - } - } - } - - public static final class Commit { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void commit(@Advice.This Session session) { - SessionState sessionState = - InstrumentationContext.get(Session.class, SessionState.class).get(session); - if (null != sessionState && sessionState.isTransactedSession()) { - sessionState.onCommitOrRollback(); - } - } - } - - public static final class Close { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void close(@Advice.This Session session) { - SessionState sessionState = - InstrumentationContext.get(Session.class, SessionState.class).get(session); - if (null != sessionState) { - sessionState.onClose(); - } - } - } -} diff --git a/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jms/JakartaJmsModule.java b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jms/JakartaJmsModule.java new file mode 100644 index 00000000000..4c09fa76324 --- /dev/null +++ b/dd-java-agent/instrumentation/jakarta-jms/src/main/java/datadog/trace/instrumentation/jms/JakartaJmsModule.java @@ -0,0 +1,24 @@ +package datadog.trace.instrumentation.jms; + +import static java.util.Collections.singletonMap; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.Map; + +@AutoService(InstrumenterModule.class) +public class JakartaJmsModule extends JavaxJmsModule { + public JakartaJmsModule() { + super("jakarta", "jakarta-jms"); + } + + @Override + public String muzzleDirective() { + return "jakarta.jms"; + } + + @Override + public Map adviceShading() { + return singletonMap("javax", "jakarta"); + } +} diff --git a/dd-java-agent/instrumentation/jms/build.gradle b/dd-java-agent/instrumentation/jms/build.gradle index 94ec1963f5f..9a8b6035e45 100644 --- a/dd-java-agent/instrumentation/jms/build.gradle +++ b/dd-java-agent/instrumentation/jms/build.gradle @@ -1,10 +1,12 @@ muzzle { pass { + name = "javax.jms" group = "javax.jms" module = "jms-api" versions = "[,]" } pass { + name = "javax.jms" group = "javax.jms" module = "javax.jms-api" versions = "[,]" diff --git a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java index a1a3627dc3a..cc126c31cfc 100644 --- a/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java +++ b/dd-java-agent/instrumentation/jms/src/main/java/datadog/trace/instrumentation/jms/JavaxJmsModule.java @@ -24,6 +24,11 @@ public JavaxJmsModule(String namespace, String instrumentationName, String... ad this.namespace = namespace; } + @Override + public String muzzleDirective() { + return "javax.jms"; + } + @Override public String[] helperClassNames() { return new String[] {