Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument JMS 3.0 (Jakarta) #7418

Merged
merged 3 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions instrumentation/jms/jms-1.1/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ muzzle {
module.set("javax.jms-api")
versions.set("(,)")
}
pass {
group.set("jakarta.jms")
module.set("jakarta.jms-api")
versions.set("(,3)")
assertInverse.set(true)
}
Comment on lines +17 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

testSets {
Expand All @@ -36,9 +42,6 @@ tasks {
}

dependencies {
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")

implementation(project(":instrumentation:jms:jms-common:javaagent"))

compileOnly("javax.jms:jms-api:1.1-rev-1")
Expand Down
42 changes: 42 additions & 0 deletions instrumentation/jms/jms-3.0/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("jakarta.jms")
module.set("jakarta.jms-api")
versions.set("[3.0.0,)")
assertInverse.set(true)
}
fail {
group.set("javax.jms")
module.set("jms-api")
versions.set("(,)")
}
fail {
group.set("javax.jms")
module.set("javax.jms-api")
versions.set("(,)")
}
}

dependencies {
implementation(project(":instrumentation:jms:jms-common:javaagent"))

library("jakarta.jms:jakarta.jms-api:3.0.0")

testImplementation("org.apache.activemq:artemis-jakarta-client:2.27.1")
}

otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_11)
}

tasks {
test {
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)

jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jms.v3_0;

import io.opentelemetry.javaagent.instrumentation.jms.DestinationAdapter;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.Queue;
import jakarta.jms.TemporaryQueue;
import jakarta.jms.TemporaryTopic;
import jakarta.jms.Topic;

public final class JakartaDestinationAdapter implements DestinationAdapter {

public static DestinationAdapter create(Destination destination) {
return new JakartaDestinationAdapter(destination);
}

private final Destination destination;

private JakartaDestinationAdapter(Destination destination) {
this.destination = destination;
}

@Override
public boolean isQueue() {
return destination instanceof Queue;
}

@Override
public boolean isTopic() {
return destination instanceof Topic;
}

@Override
public String getQueueName() throws JMSException {
if (!(destination instanceof Queue)) {
throw new IllegalStateException(
"This destination is not a Queue; make sure to call isQueue() before");
}
return ((Queue) destination).getQueueName();
}

@Override
public String getTopicName() throws JMSException {
if (!(destination instanceof Topic)) {
throw new IllegalStateException(
"This destination is not a Topic; make sure to call isTopic() before");
}
return ((Topic) destination).getTopicName();
}

@Override
public boolean isTemporaryQueue() {
return destination instanceof TemporaryQueue;
}

@Override
public boolean isTemporaryTopic() {
return destination instanceof TemporaryTopic;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jms.v3_0;

import io.opentelemetry.javaagent.instrumentation.jms.DestinationAdapter;
import io.opentelemetry.javaagent.instrumentation.jms.MessageAdapter;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;

public final class JakartaMessageAdapter implements MessageAdapter {

public static MessageAdapter create(Message message) {
return new JakartaMessageAdapter(message);
}

private final Message message;

private JakartaMessageAdapter(Message message) {
this.message = message;
}

@Nullable
@Override
public DestinationAdapter getJmsDestination() throws JMSException {
Destination destination = message.getJMSDestination();
if (destination == null) {
return null;
}
return JakartaDestinationAdapter.create(destination);
}

@Override
@SuppressWarnings("unchecked")
public List<String> getPropertyNames() throws JMSException {
return Collections.list(message.getPropertyNames());
}

@Nullable
@Override
public Object getObjectProperty(String key) throws JMSException {
return message.getObjectProperty(key);
}

@Nullable
@Override
public String getStringProperty(String key) throws JMSException {
return message.getStringProperty(key);
}

@Override
public void setStringProperty(String key, String value) throws JMSException {
message.setStringProperty(key, value);
}

@Nullable
@Override
public String getJmsCorrelationId() throws JMSException {
return message.getJMSCorrelationID();
}

@Nullable
@Override
public String getJmsMessageId() throws JMSException {
return message.getJMSMessageID();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jms.v3_0;

import static java.util.Arrays.asList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class JmsInstrumentationModule extends InstrumentationModule {
public JmsInstrumentationModule() {
super("jms", "jms-3.0");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new JmsMessageConsumerInstrumentation(),
new JmsMessageListenerInstrumentation(),
new JmsMessageProducerInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jms.v3_0;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.jms.v3_0.JmsSingletons.consumerReceiveInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.jms.MessageWithDestination;
import io.opentelemetry.javaagent.instrumentation.jms.Timer;
import jakarta.jms.Message;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class JmsMessageConsumerInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return hasClassesNamed("jakarta.jms.MessageConsumer");
}

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("jakarta.jms.MessageConsumer"));
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("receive")
.and(takesArguments(0).or(takesArguments(1)))
.and(returns(named("jakarta.jms.Message")))
.and(isPublic()),
JmsMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice");
transformer.applyAdviceToMethod(
named("receiveNoWait")
.and(takesArguments(0))
.and(returns(named("jakarta.jms.Message")))
.and(isPublic()),
JmsMessageConsumerInstrumentation.class.getName() + "$ConsumerAdvice");
}

@SuppressWarnings("unused")
public static class ConsumerAdvice {

@Advice.OnMethodEnter
public static Timer onEnter() {
return Timer.start();
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter Timer timer,
@Advice.Return Message message,
@Advice.Thrown Throwable throwable) {
if (message == null) {
// Do not create span when no message is received
return;
}

Context parentContext = Java8BytecodeBridge.currentContext();
MessageWithDestination request =
MessageWithDestination.create(JakartaMessageAdapter.create(message), null);

if (consumerReceiveInstrumenter().shouldStart(parentContext, request)) {
InstrumenterUtil.startAndEnd(
consumerReceiveInstrumenter(),
parentContext,
request,
null,
throwable,
timer.startTime(),
timer.now());
}
}
}
}
Loading