If an error such as an interface timeout occurs when your asynchronous task is running, you can query traces to check the upstream and downstream services of the asynchronous task. This helps you troubleshoot the error at the earliest opportunity. By default, Application Real-Time Monitoring Service (ARMS) allows you to monitor asynchronous tasks that are created by using the @Async annotation in Spring Boot. In addition, ARMS allows you to monitor custom asynchronous tasks by adding scan packages for asynchronous pass-through or using ARMS SDKs for manual pass-through.
Prerequisites
The ARMS agent is V2.7.1.3 or later. For more information about how to update an ARMS agent, see Update the ARMS agent.
ARMS agents V4.x and later can be used to monitor asynchronous tasks described in this topic without the need of additional settings. To pass the context in other asynchronous scenarios, refer to Use the OpenTelemetry SDK for Java to asynchronously pass the process-specific context.
Monitor asynchronous tasks created by using the @Async annotation
By default, ARMS automatically monitors asynchronous tasks that are created by using the @Async annotation. ARMS automatically enhances the following default executors and tasks in Spring:
Executors:
org.springframework.scheduling.concurrent.ConcurrentTaskExecutor
org.springframework.core.task.SimpleAsyncTaskExecutor
org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor
org.springframework.core.task.support.TaskExecutorAdapter
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
org.springframework.jca.work.WorkManagerTaskExecutor
org.springframework.scheduling.commonj.WorkManagerTaskExecutor
Tasks:
org.springframework.aop.interceptor.AsyncExecutionInterceptor$1
org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$
Monitor custom asynchronous tasks
Add a scan package for asynchronous pass-through
You can add a scan package for asynchronous pass-through to the configurations of your application to monitor asynchronous tasks. After a Runnable object, a Callable object, or a Supplier object is created, the corresponding method in the scan package for asynchronous pass-through automatically captures the trace context of the current thread. Then, when threads are used in asynchronous mode, the method passes the captured trace context to the threads.
Log on to the ARMS console. In the left-side navigation pane, choose .
On the Application List page, select a region in the top navigation bar and click the name of the application that you want to manage.
NoteIcons displayed in the Language column indicate languages in which applications are written.
: Java application
: Go application
: Python applicationHyphen (-): application monitored in Managed Service for OpenTelemetry.
In the left-side navigation pane, click Application Settings. On the page that appears, click the Custom Configuration tab.
In the Advanced Settings section of the tab, enter the name of a scan package for asynchronous pass-through in the Asynchronous transparent transmission scan package name field.

Click Save.
You must restart the application to make the configuration take effect. To add multiple scan packages for asynchronous pass-through, separate the package names with commas (,).
For example, you can use a scan package for asynchronous pass-through to monitor the asynchronous task that is created by using the following sample code. The full name of the sample scan package for asynchronous pass-through is com.alibaba.arms.brightroar.console.service.
When you add the scan package for asynchronous pass-through to your application configurations, you can adjust the specified package name as needed. If you want to monitor a large number of asynchronous tasks, you can specify the prefix of the package name instead of the full package name. In this example, in the Asynchronous transparent transmission scan package name field, you can enter the full package name com.alibaba.arms.brightroar.console.service or the prefix com.alibaba.arms. If you enter the prefix com.alibaba.arms, all scan packages for asynchronous pass-through in this directory are used for monitoring. However, a short prefix may involve an excessively large number of scan packages for asynchronous pass-through, which results in performance deterioration. Proceed with caution when you specify the package name.
If you submit a lambda expression as a Runnable object to a thread pool, duplicate trace IDs may be generated. We recommend that you create an anonymous object to submit the expression. The ARMS agent V4.0 and later has fixed duplicate trace IDs.
package com.alibaba.arms.brightroar.console.service;
@Service
public class NameService {
private ExecutorService es = Executors.newFixedThreadPool(5);
public void name() {
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(System.currentTimeMillis()+ ": my name is john, " + Thread.currentThread().getId());
}
});
}
}Use an ARMS SDK to implement manual pass-through
If the preceding methods cannot meet your requirements in a complex monitoring scenario, you can use an ARMS SDK to implement manual pass-through. You can enhance the Runnable and Callable methods and executors to concatenate the traces in asynchronous threads to monitor asynchronous tasks.
Add the following dependency to the pom.xml file in your Maven project:
<dependency> <groupId>com.alibaba.arms.apm</groupId> <version>1.7.5</version> <artifactId>arms-sdk</artifactId> </dependency>Enhance the Runnable and Callable methods and executors as required. You can use one of the following enhancement methods as needed:
Method 1: Enhance the Runnable and Callable methods
Call the
TraceRunnable.asyncEntry()method to enhance the Runnable method. Call theTraceCallable.asyncEntry()method to enhance the Callable method. The following code provides an example on how to enhance the Runnable and Callable methods:public class AsyncEntryExample { private final ExecutorService executor = Executors.newSingleThreadExecutor(); @GetMapping(value = "/sdk-async-plugin/asyncEntry-propagation") public String asyncEntryAndExecute() throws Exception { CompletableFuture<String> future = new CompletableFuture<>(); Runnable command = TraceRunnable.asyncEntry(() -> future.complete("asyncEntry-execute")); executor.execute(command); Thread.sleep(1000); return future.get(); } }Method 2: Enhance the executors
Call the
TraceExecutors.wrapExecutorService(executor, true)method to enhance the executors. The following code provides an example on how to enhance the executors:public class AutoExample { private final ExecutorService contextPropagationExecutor = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor(), true); @GetMapping(value = "/sdk-async-plugin/auto-context-propagation") public String autoWrapAndExecute() throws Exception { CompletableFuture<String> future = new CompletableFuture<>(); contextPropagationExecutor.execute(() -> future.complete("auto-execute")); Thread.sleep(1000); return future.get(); } }Method 3: Enhance the Runnable and Callable methods and executors
The following code provides an example on how to enhance the Runnable and Callable methods and executors:
public class ManualExample { private final ExecutorService traceExecutor = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor()); @GetMapping(value = "/sdk-async-plugin/manual-context-propagation") public String manualWrapAndExecute() throws Exception { CompletableFuture<String> future = new CompletableFuture<>(); traceExecutor.execute(TraceRunnable.wrap(() -> future.complete("manual-execute"))); traceExecutor.execute(() -> "Not captured"); Thread.sleep(1000); return future.get(); } }
Result
After the configurations are complete, you can view the trace details of your asynchronous tasks on the trace details page. For more information, see Trace query.