After a little bit more of looking around, I think I managed to solve my own queries. I will post this answer here for sharing & advice. I hope it is useful for someone else. If there is another out-of-the-box way to achieve my goals without using the Java Agent, please let me know.
To instrument my IBM MQ Producer service without using Java Agent:
Since I am using io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter
, I realised that using @WithSpan
on my IBM MQ Producer service's publish/put function allows it to be traced, as long as the function calling it is also instrumented. The Spans created in this way are "empty", so I looked at how to populate the Spans like how they would have been if it was instrumented by the Java Agent.
There are a few attributes that I needed to include in the span:
It seemed simple enough to include thread.id
and thread.name
- I just had to use Thread.currentThread().getXXX()
. It also seemed simple to hardcode Strings for most of the messaging.*
attributes.
However, since I implemented my IBM MQ Producer service to send its JMS Messages using org.springframework.jms.core.JmsTemplate$send
, the messaging.message.id
is only generated after the send
method is called - I did not know how to obtain the messaging.message.id
before calling the send
method.
To populate the JMS Message Spans with messaging.message.id
attributes without using Java Agent:
Turns out, I can use org.springframework.jms.core.JmsTemplate$doInJms
to manually publish the JMS Message in my IBM MQ Producer service. This allowed me to use org.springframework.jms.core.SessionCallback
to manually create the jakarta.jms.Message
, send it, and eventually return the JMSMessageID as a String so that I can set it into my Span attributes.
This way, I can instrument my IBM MQ Producer service's put/publish methods while not propagating context downstream. A sample of my implementation is below:
@WithSpan(value = "publish", kind = SpanKind.PRODUCER)
public void publish(String payload) {
String jmsMessageID = jmsTemplate.execute(new SessionCallback<>() {
@Override
@NonNull
public String doInJms(@NonNull Session session) throws JMSException {
Message message = session.createTextMessage(payload);
Destination destination = jmsTemplate.getDefaultDestination();
MessageProducer producer = session.createProducer(destination);
producer.send(message);
return message.getJMSMessageID();
}
});
Span currentSpan = Span.current();
currentSpan.setAttribute("messaging.destination.name", jmsTemplate.getDefaultDestination().toString());
currentSpan.setAttribute("messaging.message.id", jmsMessageID);
currentSpan.setAttribute("messaging.operation", "publish");
currentSpan.setAttribute("messaging.system", "jms");
currentSpan.setAttribute("thread.id", String.valueOf(Thread.currentThread().getId()));
currentSpan.setAttribute("thread.name", Thread.currentThread().getName());
}
Note: The jmsTemplate
is configured separately as a Bean and injected into my IBM MQ Producer service.