From 7bee0d8923ae2bd2ed6bafd51e870ee27208cdfa Mon Sep 17 00:00:00 2001 From: nbrendah Date: Wed, 5 Jan 2022 17:52:38 +0300 Subject: [PATCH] ARTEMIS-3686: Adding example showing how to do telemetry support This is adding an example intercepting and sending messages using opentelemetry to either Jaeger, zipkin or OTLP exporter --- .../activemq/artemis/api/core/Message.java | 6 + .../artemis/api/core/RefCountMessage.java | 18 ++ .../message/impl/MessageInternalImpl.java | 9 + .../protocol/openwire/OpenwireMessage.java | 10 + .../impl/ScheduledDeliveryHandlerTest.java | 10 + .../features/standard/opentracing/pom.xml | 201 ++++++++++++++++++ .../features/standard/opentracing/readme.md | 68 ++++++ .../jms/example/OpenTracingPlugin.java | 113 ++++++++++ .../jms/example/OpenTracingPluginExample.java | 121 +++++++++++ .../resources/activemq/server0/broker.xml | 199 +++++++++++++++++ .../src/main/resources/tracing.properties | 37 ++++ .../jms/example/OpenTracingPluginTest.java | 134 ++++++++++++ examples/features/standard/pom.xml | 2 + pom.xml | 21 ++ .../integration/client/AcknowledgeTest.java | 10 + 15 files changed, 959 insertions(+) create mode 100644 examples/features/standard/opentracing/pom.xml create mode 100644 examples/features/standard/opentracing/readme.md create mode 100644 examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPlugin.java create mode 100644 examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginExample.java create mode 100644 examples/features/standard/opentracing/src/main/resources/activemq/server0/broker.xml create mode 100644 examples/features/standard/opentracing/src/main/resources/tracing.properties create mode 100644 examples/features/standard/opentracing/src/test/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginTest.java diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java index 88f303f9ab..8975159a37 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java @@ -801,4 +801,10 @@ public interface Message { default String getStringBody() { return null; } + + /** Used for user context data. Useful on interceptors. */ + Object getUserContext(Object key); + + /** Used for user context data. Useful on interceptors. */ + void setUserContext(Object key, Object value); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/RefCountMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/RefCountMessage.java index 35d591c808..0665a78f2c 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/RefCountMessage.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/RefCountMessage.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.api.core; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; // import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet; -- #ifdef DEBUG @@ -27,6 +28,8 @@ public class RefCountMessage { private static final AtomicIntegerFieldUpdater REF_COUNT_UPDATER = AtomicIntegerFieldUpdater.newUpdater(RefCountMessage.class, "refCount"); private static final AtomicIntegerFieldUpdater REF_USAGE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(RefCountMessage.class, "usageCount"); + private volatile HashMap userContext; + private volatile int durableRefCount = 0; private volatile int refCount = 0; @@ -170,4 +173,19 @@ public class RefCountMessage { return count; } + public Object getUserContext(Object key) { + if (userContext == null) { + return null; + } else { + return userContext.get(key); + } + } + + public void setUserContext(Object key, Object value) { + if (userContext == null) { + userContext = new HashMap(); + } + userContext.put(key, value); + } + } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/MessageInternalImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/MessageInternalImpl.java index f7495a9d6d..0f990f0260 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/MessageInternalImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/MessageInternalImpl.java @@ -723,4 +723,13 @@ public class MessageInternalImpl implements MessageInternal { message.setOwner(object); } + @Override + public Object getUserContext(Object key) { + return message.getUserContext(key); + } + + @Override + public void setUserContext(Object key, Object value) { + message.setUserContext(key, value); + } } diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenwireMessage.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenwireMessage.java index 96efff84be..47eb4e5ac2 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenwireMessage.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenwireMessage.java @@ -516,4 +516,14 @@ public class OpenwireMessage implements Message { public int usageDown() { return 0; } + + @Override + public Object getUserContext(Object key) { + return null; + } + + @Override + public void setUserContext(Object key, Object value) { + + } } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java index 6532749b66..9204f49030 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java @@ -296,6 +296,16 @@ public class ScheduledDeliveryHandlerTest extends Assert { class FakeMessage implements Message { + @Override + public Object getUserContext(Object key) { + return null; + } + + @Override + public void setUserContext(Object key, Object value) { + + } + @Override public String getProtocolName() { // should normally not be visible in GUI diff --git a/examples/features/standard/opentracing/pom.xml b/examples/features/standard/opentracing/pom.xml new file mode 100644 index 0000000000..68d519ac2a --- /dev/null +++ b/examples/features/standard/opentracing/pom.xml @@ -0,0 +1,201 @@ + + + + + 4.0.0 + + + org.apache.activemq.examples.broker + jms-examples + 2.21.0-SNAPSHOT + + + opentracing + jar + ActiveMQ Artemis Broker Plugin Example + + + ${project.basedir}/../../../.. + + + + + org.apache.activemq + artemis-jms-client-all + ${project.version} + + + org.apache.activemq + artemis-server + ${project.version} + + + org.apache.activemq + artemis-amqp-protocol + ${project.version} + + + org.apache.qpid + qpid-jms-client + + + io.opentelemetry + opentelemetry-api + 1.10.0 + + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-semconv + + + io.opentelemetry + opentelemetry-sdk-extension-autoconfigure + + + io.opentelemetry + opentelemetry-exporter-logging + + + io.opentelemetry + opentelemetry-exporter-otlp + + + io.opentelemetry + opentelemetry-exporter-jaeger + + + io.opentelemetry + opentelemetry-exporter-zipkin + + + io.opentelemetry + opentelemetry-sdk-testing + + + junit + junit + test + + + + org.assertj + assertj-core + 3.22.0 + test + + + org.mockito + mockito-core + test + + + + + + + org.apache.activemq + artemis-maven-plugin + + + create + verify + + + + org.apache.activemq.examples.broker:opentracing:${project.version} + + + io.opentelemetry:opentelemetry-api:${opentelemetry.version} + io.opentelemetry:opentelemetry-sdk:${opentelemetry.version} + io.opentelemetry:opentelemetry-semconv:${opentelemetry-alpha.version} + io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${opentelemetry-alpha.version} + + io.opentelemetry:opentelemetry-exporter-logging:${opentelemetry.version} + io.opentelemetry:opentelemetry-exporter-otlp:${opentelemetry.version} + io.opentelemetry:opentelemetry-exporter-jaeger:${opentelemetry.version} + io.opentelemetry:opentelemetry-exporter-zipkin:${opentelemetry.version} + io.opentelemetry:opentelemetry-sdk:${opentelemetry.version} + io.opentelemetry:opentelemetry-sdk-testing:${opentelemetry.version} + junit:junit:${junit.version} + org.assertj:assertj-core:3.22.0 + org.mockito:mockito-core:${mockito.version} + + ${noServer} + + + create + + + + start + + cli + + + true + ${noServer} + tcp://localhost:61616 + + run + + + + + runClient + + runClient + + + org.apache.activemq.artemis.jms.example.OpenTracingPluginExample + + + + stop + + cli + + + ${noServer} + + stop + + + + + + + org.apache.activemq.examples.broker + opentracing + ${project.version} + + + + + org.apache.maven.plugins + maven-clean-plugin + + + + diff --git a/examples/features/standard/opentracing/readme.md b/examples/features/standard/opentracing/readme.md new file mode 100644 index 0000000000..7687166f94 --- /dev/null +++ b/examples/features/standard/opentracing/readme.md @@ -0,0 +1,68 @@ +# Opentracing Plugin Example + +This plugin +embraces [OpenTelemetry Autoconfiguration](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure) +using environment-based properties to configure OpenTelemetry SDK. + +## Run Opentracing Plugin Example + +[![Running the Example Demo](https://img.youtube.com/vi/MVGx7QrztZQ/0.jpg)](https://www.youtube.com/watch?v=MVGx7QrztZQ) + +To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start +and create the broker manually. +> **_NOTE:_** You must have [jeager](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#jaeger-exporter) running at `http://localhost:16686`. You can learn more about Jeager [here](https://www.jaegertracing.io/) + +> command to start your jeager instance `docker run -p 16686:16686 -p 14250:14250 jaegertracing/all-in-one:` + +After seeing a **`Build Success`**, open the browser, connect to your Jeager running instance and check for spans. + +## Customise Opentracing Plugin Example + +The [`tracing.properties`](./src/main/resources/tracing.properties) has configuration properties that +autoconfigure [Opentelemetry Exporter](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#exporters) +. We reconfigured it and used Jeager as the default exporter, sending data through at `http://localhost:14250` +You can change this by choosing to use: + +- [otlp exporter](https://github.com/open-telemetry/opentelemetry-java/tree/1e073fcff20697fd5f2eb39bd6246d06a1231089/sdk-extensions/autoconfigure#otlp-exporter-both-span-and-metric-exporters) + , by uncommenting (removing `#`) the following + - otlp enabler: `otel.traces.exporter=otlp` + - otlp endpoint: `otel.exporter.otlp.endpoint=http://localhost:4317` Change port and host to match your running + instance. + - otlp traces-endpoint: `otel.exporter.otlp.traces.endpoint=http://localhost:4317` Change port and host to match + your running instance. + + +- [Zipkin Exporter](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#zipkin-exporter) + , by uncommenting (removing `#`) the following + - Zipkin enabler: `otel.traces.exporter=zipkin` + - Zipkin endpoint: `otel.exporter.zipkin.endpoint=http://localhost:9411/api/v2/spans`. Change port and host to match your + running instance. + > **Note:** command to start Zipkin instance `docker run -p 9411:9411 openzipkin/zipkin` + + +You can also change the default service name from `opentracing_plugin` to any string by changing the value +of `otel.service.name` + +## How to start exporters +- [Zipkin](https://zipkin.io/pages/quickstart): The quickest way is by use of docker. + - Open the terminal, copy, paste and run the command `docker run -d -p 9411:9411 openzipkin/zipkin` + - open the browser, enter the url `http://localhost:9411` and on the page that appears, click the **Run Queries** button. + + +- [Jeager](https://www.jaegertracing.io/docs/1.30/getting-started/): The quickest way is by use of docker. + - open the terminal and paste the command below + ``` + docker run -d --name jaeger \ + e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ + p 5775:5775/udp \ + p 6831:6831/udp \ + p 6832:6832/udp \ + p 5778:5778 \ + p 16686:16686 \ + p 14250:14250 \ + p 14268:14268 \ + p 14269:14269 \ + p 9411:9411 \ + jaegertracing/all-in-one:1.30 + ``` + - open the browser, enter the url `http://localhost:16686/search`, click **Search**, select your service-name from the dropdown below the service name and finally click **Find Traces** Button. diff --git a/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPlugin.java b/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPlugin.java new file mode 100644 index 0000000000..6668e9dd38 --- /dev/null +++ b/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPlugin.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.jms.example; + +import java.io.InputStream; +import java.util.Properties; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.core.postoffice.RoutingStatus; +import org.apache.activemq.artemis.core.server.MessageReference; +import org.apache.activemq.artemis.core.server.ServerConsumer; +import org.apache.activemq.artemis.core.server.ServerSession; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; +import org.apache.activemq.artemis.core.transaction.Transaction; + +public class OpenTracingPlugin implements ActiveMQServerPlugin { + + private static final String OPERATION_NAME = "ArtemisMessageDelivery"; + private static OpenTelemetrySdk sdk = initOpenTracing(); + private static Tracer tracer = GlobalOpenTelemetry.getTracer(OpenTracingPlugin.class.getName()); + + public static OpenTelemetrySdk initOpenTracing() { + try { + InputStream input = OpenTracingPlugin.class.getClassLoader().getResourceAsStream("tracing.properties"); + if (input == null) { + throw new NullPointerException("Unable to find tracing.properties file"); + } + Properties prop = new Properties(System.getProperties()); + prop.load(input); + System.setProperties(prop); + + sdk = AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(); + + } catch (Throwable t) { + t.printStackTrace(); + } + return sdk; + } + + @Override + public void beforeSend(ServerSession session, + Transaction tx, + Message message, + boolean direct, + boolean noAutoCreateQueue) throws ActiveMQException { + SpanBuilder spanBuilder = getTracer().spanBuilder(OPERATION_NAME).setAttribute("message", message.toString()).setSpanKind(SpanKind.SERVER); + Span span = spanBuilder.startSpan(); + message.setUserContext(Span.class, span); + } + + @Override + public void afterSend(Transaction tx, + Message message, + boolean direct, + boolean noAutoCreateQueue, + RoutingStatus result) throws ActiveMQException { + Span span = getSpan(message); + span.addEvent("send " + result.name()); + } + + @Override + public void afterDeliver(ServerConsumer consumer, MessageReference reference) throws ActiveMQException { + Span span = (Span) reference.getMessage().getUserContext(Span.class); + span.addEvent("deliver " + consumer.getSessionName()); + span.end(); + } + + @Override + public void onSendException(ServerSession session, + Transaction tx, + Message message, + boolean direct, + boolean noAutoCreateQueue, + Exception e) throws ActiveMQException { + getSpan(message).setStatus(StatusCode.ERROR).recordException(e); + } + + public Tracer getTracer() { + return tracer; + } + + public void setTracer(Tracer myTracer) { + tracer = myTracer; + } + + private Span getSpan(Message message) { + Span span = (Span) message.getUserContext(Span.class); + return span; + } +} \ No newline at end of file diff --git a/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginExample.java b/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginExample.java new file mode 100644 index 0000000000..9690d32ac8 --- /dev/null +++ b/examples/features/standard/opentracing/src/main/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginExample.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.jms.example; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.jms.client.ActiveMQQueue; +import org.apache.qpid.jms.JmsConnectionFactory; + +/** + * A simple example which shows how to use a QueueBrowser to look at messages of a queue without removing them from the queue + */ +public class OpenTracingPluginExample { + + public static void main(final String[] args) throws Exception { + + // This example will send and receive an AMQP message + sendConsumeAMQP(); + + // And it will also send and receive a Core message + sendConsumeCore(); + } + + private static void sendConsumeAMQP() throws JMSException { + Connection connection = null; + ConnectionFactory connectionFactory = new JmsConnectionFactory("amqp://localhost:5672"); + + try { + + // Create an amqp qpid 1.0 connection + connection = connectionFactory.createConnection(); + + // Create a session + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a sender + Queue queue = session.createQueue("exampleQueue"); + MessageProducer sender = session.createProducer(queue); + + // send a few simple message + sender.send(session.createTextMessage("Hello world ")); + + connection.start(); + + // create a moving receiver, this means the message will be removed from the queue + MessageConsumer consumer = session.createConsumer(queue); + + // receive the simple message + consumer.receive(5000); + + } finally { + if (connection != null) { + // close the connection + connection.close(); + } + } + } + + + private static void sendConsumeCore() throws JMSException { + Connection connection = null; + try { + // Perform a lookup on the Connection Factory + ConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616"); + + Queue queue = new ActiveMQQueue("exampleQueue"); + + // Create a JMS Connection + connection = cf.createConnection(); + + // Create a JMS Session + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a JMS Message Producer + MessageProducer producer = session.createProducer(queue); + + // Create a Text Message + TextMessage message = session.createTextMessage("This is a text message"); + + // Send the Message + producer.send(message); + + // Create a JMS Message Consumer + MessageConsumer messageConsumer = session.createConsumer(queue); + + // Start the Connection + connection.start(); + + // Receive the message + messageConsumer.receive(5000); + + } finally { + if (connection != null) { + connection.close(); + } + } + + } +} diff --git a/examples/features/standard/opentracing/src/main/resources/activemq/server0/broker.xml b/examples/features/standard/opentracing/src/main/resources/activemq/server0/broker.xml new file mode 100644 index 0000000000..667ce90bf4 --- /dev/null +++ b/examples/features/standard/opentracing/src/main/resources/activemq/server0/broker.xml @@ -0,0 +1,199 @@ + + + + + + + + 0.0.0.0 + + true + + + NIO + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + true + + 2 + + -1 + + 10M + + + + + + + + + + + + + + + + + + + + + 5000 + + + 90 + + + true + + 120000 + + 60000 + + HALT + + + + + + + + + + + + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300 + + + tcp://0.0.0.0:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpLowCredits=300 + + + tcp://0.0.0.0:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true + + + tcp://0.0.0.0:5445?protocols=HORNETQ,STOMP;useEpoll=true + + + tcp://0.0.0.0:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true + + + + + + + + + + + + + + + + + + + + + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + +
+ + + +
+
+ + + +
+ +
+ +
+
diff --git a/examples/features/standard/opentracing/src/main/resources/tracing.properties b/examples/features/standard/opentracing/src/main/resources/tracing.properties new file mode 100644 index 0000000000..f4eaee5f62 --- /dev/null +++ b/examples/features/standard/opentracing/src/main/resources/tracing.properties @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#activemq + +# using otlp exporter +#otel.traces.exporter=otlp +#otel.exporter.otlp.endpoint=http://localhost:4317 +#otel.exporter.otlp.traces.endpoint=http://localhost:4317 +#otel.exporter.otlp.timeout=10000 +#otel.exporter.otlp.traces.timeout=10000 +#otel.exporter.otlp.protocol=grpc +#otel.exporter.otlp.traces.protocol=grpc + +# service name +otel.service.name=artemis_opentracing + +# Jaeger exporter +otel.traces.exporter=jaeger +otel.exporter.jaeger.endpoint=http://localhost:14250 +otel.exporter.jaeger.timeout=10000 + +# Zipkin exporter (default) +#otel.traces.exporter=zipkin +#otel.exporter.zipkin.endpoint=http://localhost:9411/api/v2/spans diff --git a/examples/features/standard/opentracing/src/test/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginTest.java b/examples/features/standard/opentracing/src/test/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginTest.java new file mode 100644 index 0000000000..da25745230 --- /dev/null +++ b/examples/features/standard/opentracing/src/test/java/org/apache/activemq/artemis/jms/example/OpenTracingPluginTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.jms.example; + +import javax.jms.JMSException; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.core.postoffice.RoutingStatus; +import org.apache.activemq.artemis.core.server.MessageReference; +import org.apache.activemq.artemis.core.server.ServerConsumer; +import org.apache.activemq.artemis.core.server.ServerSession; +import org.apache.activemq.artemis.core.transaction.Transaction; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.validateMockitoUsage; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class OpenTracingPluginTest { + + private final InMemorySpanExporter exporter = InMemorySpanExporter.create(); + @InjectMocks + private OpenTracingPlugin plugin; + + @Mock + private SpanBuilder spanBuilder; + + @Mock + private Transaction tx; + + @Mock + private Message message; + + @Mock + private Span span; + + @Test + public void assertBeforeMessage() throws JMSException, ActiveMQException { + Tracer tracer = mock(Tracer.class); + ServerSession session = mock(ServerSession.class); + + Mockito.doReturn(spanBuilder).when(tracer).spanBuilder(anyString()); + Mockito.doReturn(spanBuilder).when(spanBuilder).setAttribute(anyString(), anyString()); + Mockito.doReturn(spanBuilder).when(spanBuilder).setSpanKind(any(SpanKind.class)); + Mockito.doReturn(span).when(spanBuilder).startSpan(); + + plugin.setTracer(tracer); + plugin.beforeSend(session, tx, message, true, true); + + verify(tracer, atLeastOnce()).spanBuilder(anyString()); + verify(spanBuilder, atLeastOnce()).setAttribute(anyString(), anyString()); + verify(spanBuilder, atLeastOnce()).setSpanKind(any(SpanKind.class)); + verify(spanBuilder, atLeastOnce()).startSpan(); + verify(message, atLeastOnce()).setUserContext(anyObject(), anyObject()); + } + + @Test + public void assetAfterSend() throws ActiveMQException { + when(message.getUserContext(Span.class)).thenReturn(span); + + plugin.afterSend(tx, message, true, true, RoutingStatus.OK); + verify(span, atLeastOnce()).addEvent(anyString()); + } + + @Test + public void assertOnSendException() throws ActiveMQException { + ServerSession session = mock(ServerSession.class); + Exception exception = mock(Exception.class); + + when(message.getUserContext(Span.class)).thenReturn(span); + when(span.setStatus(any(StatusCode.class))).thenReturn(span); + when(span.recordException(any(Exception.class))).thenReturn(span); + + plugin.onSendException(session, tx, message, true, true, exception); + + verify(span, atLeastOnce()).setStatus(any(StatusCode.class)); + verify(span, atLeastOnce()).setStatus(any(StatusCode.class)); + verify(span, atLeastOnce()).recordException(any(Exception.class)); + } + + @Test + public void assertAfterDeliver() throws ActiveMQException { + ServerConsumer consumer = mock(ServerConsumer.class); + MessageReference reference = mock(MessageReference.class); + + when(reference.getMessage().getUserContext(Span.class)).thenReturn(span); + + plugin.afterDeliver(consumer, reference); + + verify(span, atLeastOnce()).addEvent(anyString()); + verify(span, atLeastOnce()).end(); + } + + @After + public void cleanUp() { + exporter.reset(); + GlobalOpenTelemetry.resetForTest(); + validateMockitoUsage(); + } +} diff --git a/examples/features/standard/pom.xml b/examples/features/standard/pom.xml index a830d2d57f..89f839500f 100644 --- a/examples/features/standard/pom.xml +++ b/examples/features/standard/pom.xml @@ -80,6 +80,7 @@ under the License. message-priority netty-openssl no-consumer-buffering + opentracing paging pre-acknowledge producer-rate-limit @@ -156,6 +157,7 @@ under the License. message-priority netty-openssl no-consumer-buffering + opentracing paging pre-acknowledge producer-rate-limit diff --git a/pom.xml b/pom.xml index 06bf38a3f3..f4e6385941 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,10 @@ 5.2.0 3.6.3 + + 1.10.0 + 1.10.0-alpha + 2.0.48.Final 0.33.10 @@ -549,6 +553,23 @@ + + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import + + + io.opentelemetry + opentelemetry-bom-alpha + ${opentelemetry-alpha.version} + pom + import + + + org.jgroups diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AcknowledgeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AcknowledgeTest.java index 1803321020..f56a738f1e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AcknowledgeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AcknowledgeTest.java @@ -844,5 +844,15 @@ public class AcknowledgeTest extends ActiveMQTestBase { @Override public void setOwner(Object object) { } + + @Override + public Object getUserContext(Object key) { + return null; + } + + @Override + public void setUserContext(Object key, Object value) { + + } } }