# Elastic APM Java for async Spring events

**URL:** <https://discuss.elastic.co/t/elastic-apm-java-for-async-spring-events/359528>\
**Category:** APM\
**Tags:** java\
**Created:** [May 15, 2024, 9:20am UTC](https://discuss.elastic.co/t/elastic-apm-java-for-async-spring-events/359528 "2024-05-15T09:20:37Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Wolfram\_Haussig](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/wolfram_haussig/32/70528_2.png) [@Wolfram\_Haussig](https://discuss.elastic.co/u/Wolfram_Haussig)\
**Post date:** [May 15, 2024, 9:20am UTC](https://discuss.elastic.co/t/elastic-apm-java-for-async-spring-events/359528/1 "2024-05-15T09:20:37Z")

</div>

Hi all,

I hope someone can point me in the right direction here. I am using Spring Boot 3 in combination with Spring Modulith in my application. I get APM data in Kibana, so the configuration is fine. But, when I use an async event to communicate with the next module, APM does not collect data in the other modules.

Module 1 sending code (simplified):

```java
@Service
public class Modul1Service {
private ApplicationEventPublisher events;

    @Scheduled(fixedRate = 5000)
    @Transactional
    public void poll() {
      events.publishEvent(new JobCreatedEvent());
   }
}

```

Module 2 receiving code (simplified):

```java
@Service
public class Modul2Service {
    @CaptureSpan
    @ApplicationModuleListener
    void on(JobCreatedEvent event) {
      //process
   }
}

```

When enabling debug logs for the agent, I find the following log repeatedly:

> Not creating span for Modul2Service#on because there is no currently active span.

As this is related to async processing, I checked your docs about asynchronous frameworks. As you support `ExecutorService` I tried to force Spring to use this next:

```java
	@Bean
	@Primary
	ExecutorService asyncExecutor() {
		return Executors.newVirtualThreadPerTaskExecutor();
	}

	/**
	 * required to use the custom async executor with virtual threads
	 * 
	 * @return
	 */
        @Bean
	ApplicationEventMulticaster applicationEventMulticaster() {
		SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();

		eventMulticaster.setTaskExecutor(asyncExecutor());
		return eventMulticaster;
	}

```

Still, APM does not collect spans for the other modules. Any ideas how I can fix this?

**Kibana version** : 8.13.3

**Elasticsearch version** : 8.13

**APM Server version** : 8.13.3

**APM Agent language and version** : Java 1.49

---

<div class="post-metadata">

**Author:** ![Jonas\_Kunz](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jonas_kunz/32/110740_2.png) [@Jonas\_Kunz](https://discuss.elastic.co/u/Jonas_Kunz)\
**Post date:** [May 15, 2024, 11:26am UTC](https://discuss.elastic.co/t/elastic-apm-java-for-async-spring-events/359528/2 "2024-05-15T11:26:07Z")

</div>

> [@Wolfram\_Haussig](#):
>
> As this is related to async processing, I checked your docs about asynchronous frameworks. As you support `ExecutorService` I tried to force Spring to use this next:

I think the problem here is that `ExecutorService.submit` is likely not invoked synchronously when you call `ApplicationEventPublisher.publish`. Therefore the trace context is already lost before the built-in support of the elastic apm agent for executors can take effect.

One solution here would be to manually propagate the trace context, ideally via the OpenTelemtry-API:

When you call `ApplicationEventPublisher.publishEvent`, you'll need to somehow attach the current trace context to your event:

- Get the current context via [Context.current()](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context/latest/io/opentelemetry/context/Context.html)
- Attach it to your event: Either by storing it in a field or using a global [IdentityHashMap](https://docs.oracle.com/javase/8/docs/api/java/util/IdentityHashMap.html) for example

Then, in your `@EventListener`, you'll need to extract the stored context from the event and activate it ([Context.makeCurrent()](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context/latest/io/opentelemetry/context/Context.html)). When you now start a span while the context is activated, it will continue the previous trace.

You could make that more ergonomic using [spring-aop](https://www.baeldung.com/spring-aop-annotation) if you want to.  
If you don't want to use the OpentelemetryApi you could also use our public API (e.g. [Span.current()](https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html#api-current-span)), though OpenTelemetry is the more futue-proof way.

EDIT:  
You could alternatively also use a custom `ApplicationEventMulticaster` implementation which is responsible for activating the trace context. I think that would be the cleanest and simplest solution instead of trying to enhance the `@EventListener` callsites.

---

<div class="post-metadata">

**Author:** ![Wolfram\_Haussig](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/wolfram_haussig/32/70528_2.png) [@Wolfram\_Haussig](https://discuss.elastic.co/u/Wolfram_Haussig)\
**Post date:** [May 15, 2024, 12:22pm UTC](https://discuss.elastic.co/t/elastic-apm-java-for-async-spring-events/359528/3 "2024-05-15T12:22:27Z")

</div>

Hello Jonas,

Thank you so much! It works like a charm - it seems that I was thinking too complicated.

I am sure that OpenTelemetry is the future proof way, but I am still missing some functionality there (like getting the current root span - Elastic allows easy access to the Transaction.
