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

Support for Jakarta JMS specification #1353

Merged
merged 1 commit into from
Jan 25, 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
113 changes: 113 additions & 0 deletions instrumentation/jms-jakarta/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Brave JMS (Jakarta) instrumentation
This module provides instrumentation for Jakarta JMS 3.0+ consumers,
producers and listeners. It works by wrapping connections or connection
factories.

Under the scenes:
* `TracingMessageProducer` - completes a producer span per message and propagates it via headers.
* `TracingMessageConsumer` - completes a consumer span on `receive`, resuming a trace in headers if present.
* The message returned has the current span encoded as the property "b3".
* `TracingMessageListener` - does the same as `TracingMessageConsumer`, and times the user-supplied listener.
* The message passed to the listener has no "b3" header. Similar to a server, use `Tracer.currentSpan()`.


## Setup
First, setup the generic Jms component like this:
```java
jmsTracing = JmsTracing.newBuilder(messagingTracing)
.remoteServiceName("my-broker")
.build();
```

Now, just wrap your connection or connection factory
```java
ConnectionFactory tracingConnectionFactory = jmsTracing.connectionFactory(connectionFactory);

// When you later send a message, you'll notice "b3" as a message property
producer.send(message);
```

*NOTE* `JMSConsumer.receiveBodyXXX()` methods are not traced due to lack
of hooks. `JMSConsumer.receive()` followed by `Message.getBody()` is the
most compatible alternative. If you desire other routes, please raise an
issue or join https://gitter.im/openzipkin/zipkin

## Sampling Policy
The default sampling policy is to use the default (trace ID) sampler for
producer and consumer requests.

You can use an [MessagingRuleSampler](../messaging/README.md) to override this
based on JMS destination names.

Ex. Here's a sampler that traces 100 consumer requests per second, except for
the "alerts" topic. Other requests will use a global rate provided by the
`Tracing` component.

```java
import brave.sampler.Matchers;

import static brave.messaging.MessagingRequestMatchers.channelNameEquals;

messagingTracingBuilder.consumerSampler(MessagingRuleSampler.newBuilder()
.putRule(channelNameEquals("alerts"), Sampler.NEVER_SAMPLE)
.putRule(Matchers.alwaysMatch(), RateLimitingSampler.create(100))
.build());

jmsTracing = JmsTracing.create(messagingTracing);
```

## What's happening?
Typically, there are three spans involved in message tracing:
* If a message producer is traced, it completes a PRODUCER span per message
* If a consumer is traced, receive completes a CONSUMER span based on the incoming queue or topic
* Message listener timing is in a child of the CONSUMER span.

## Custom Message Processing

If you are using Jms `MessageListener`, the following is automatic. If
you have custom code, or are using a different processing abstraction,
read below:

When ready for processing use `JmsTracing.nextSpan` to continue the trace.

```java
// Typically, poll is in a loop. the consumer is wrapped
while (running) {
Message message = tracingMessageConsumer.receive();
// either automatically or manually wrap your real process() method to use jmsTracing.nextSpan()
messages.forEach(message -> process(message));
}
```

If you are in a position where you have a custom processing loop, you can do something like this
to trace manually or you can do similar via automatic instrumentation like AspectJ.
```java
void process(Message message) {
// Grab any span from the queue. The queue or topic is automatically tagged
Span span = jmsTracing.nextSpan(message).name("process").start();

// Below is the same setup as any synchronous tracing
try (SpanInScope ws = tracer.withSpanInScope(span)) { // so logging can see trace ID
return doProcess(message); // do the actual work
} catch (RuntimeException | Error e) {
span.error(e); // make sure any error gets into the span before it is finished
throw e;
} finally {
span.finish(); // ensure the span representing this processing completes.
}
}
```

## Compatibility issues

There are known issues with ActiveMQ Artemis Client 2.x:
- the message property names have to be valid Java identifiers, as such properties like `X-B3-TraceId`, `X-B3-SpanId` will be rejected and not propagated
- the message property of type `Object` only supports `String`, primitive wrappers (`Integer`, `Byte`, ...) and `byte[]`, other types be rejected and not set

## Troubleshooting
If you have problems with a JMS provider, such as broken traces, please capture the "FINE" output of
the Java logger: `brave.jakarta.jms.JmsTracing` and ask on [gitter](https://gitter.im/openzipkin/zipkin).

## Notes
* This instrumentation library works with Jakarta JMS 3.0+
* More information about "Message Tracing" [here](https://github.com/openzipkin/openzipkin.github.io/blob/master/pages/instrumenting.md#message-tracing)
7 changes: 7 additions & 0 deletions instrumentation/jms-jakarta/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# We use need to import to support brave.internal.Throwables
# brave.internal.Nullable is not used at runtime.
Import-Package: \
brave.internal;braveinternal=true,\
*
Export-Package: \
brave.jakarta.jms
107 changes: 107 additions & 0 deletions instrumentation/jms-jakarta/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0"?>
<!--

Copyright 2013-2022 The OpenZipkin Authors

Licensed 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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-parent</artifactId>
<version>5.14.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>brave-instrumentation-jakarta-jms</artifactId>
<name>Brave Instrumentation: JMS (Jakarta)</name>

<properties>
<!-- Matches Export-Package in bnd.bnd -->
<module.name>brave.jakarta.jms</module.name>

<main.basedir>${project.basedir}/../..</main.basedir>
<main.java.version>1.6</main.java.version>
<main.signature.artifact>java16</main.signature.artifact>

<!-- disable errorprone override warning as we do this intentionally to allow JMS 1.1 -->
<errorprone.args>-Xep:MissingOverride:OFF</errorprone.args>
</properties>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-instrumentation-messaging</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>jakarta.jms</groupId>
<artifactId>jakarta.jms-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jakarta-server</artifactId>
<version>${activemq.artemis.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-junit</artifactId>
<version>${activemq.artemis.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/PropertyFilterTest.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<!-- PropertyFilterTest can't run in the same JVM as others as it can't use log4j2 -->
<id>PropertyFilterTest</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/PropertyFilterTest.class</include>
</includes>
<excludes combine.self="override" />
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2 changes: 2 additions & 0 deletions instrumentation/jms-jakarta/src/it/jms30/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Jakarta JMS 3.0
This tests that JmsTracing does work on Jakarta JMS 3.0 APIs
128 changes: 128 additions & 0 deletions instrumentation/jms-jakarta/src/it/jms30/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright 2013-2022 The OpenZipkin Authors

Licensed 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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>@project.groupId@</groupId>
<artifactId>jms40</artifactId>
<version>@project.version@</version>
<name>jms40</name>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>jakarta.jms</groupId>
<artifactId>jakarta.jms-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-instrumentation-jakarta-jms</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-tests</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jakarta-server</artifactId>
<version>@activemq.artemis.version@</version>
</dependency>

<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-junit</artifactId>
<version>@activemq.artemis.version@</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>@junit.version@</version>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>@assertj.version@</version>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>@mockito.version@</version>
</dependency>

<!-- Route jul over log4j2 during integration tests -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
<version>@log4j.version@</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>@log4j.version@</version>
</dependency>
</dependencies>

<build>
<sourceDirectory>@project.build.testSourceDirectory@</sourceDirectory>
<testResources>
<testResource>
<directory>@project.basedir@/src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<!-- Use surefire to run the ITs until someone figures out how to get invoker to run
failsafe -->
<artifactId>maven-surefire-plugin</artifactId>
<version>@maven-surefire-plugin.version@</version>
<configuration>
<failIfNoTests>true</failIfNoTests>
<includes>
<include>**/IT*.java</include>
</includes>
<!-- Try to prevent flakes in CI -->
<reuseForks>false</reuseForks>
<!-- workaround to SUREFIRE-1831 -->
<useModulePath>false</useModulePath>
<!-- Brave and OkHttp MockWebServer log with java.util.logging. Adding the bridge allows
allows us to change scope and format via log4j2.properties -->
<systemPropertyVariables>
<java.util.logging.manager>org.apache.logging.log4j.jul.LogManager</java.util.logging.manager>
</systemPropertyVariables>
<!-- Ensure scope leak cause ends up in the console -->
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2013-2022 The OpenZipkin Authors
*
* Licensed 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 brave.jakarta.jms;

public class ITJmsTracingMessageConsumer extends ITTracingMessageConsumer {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2013-2022 The OpenZipkin Authors
*
* Licensed 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 brave.jakarta.jms;

public class ITJmsTracingMessageProducer extends ITTracingMessageProducer {
}
Loading