79611642

Date: 2025-05-08 02:27:21
Score: 3
Natty:
Report link

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.

Reasons:
  • Blacklisted phrase (0.5): I need
  • RegEx Blacklisted phrase (2.5): please let me know
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: ALVIN