如何在Spring核心应用中附加Elastic APM(非SpringBoot)
Got it, let's work through this together. Since you're dealing with a vanilla Spring app (not Spring Boot) that uses JAX-RS for REST endpoints, the main challenge is finding the right lifecycle hook to trigger ElasticApmAttacher.attach() before your app starts handling requests. Here's a step-by-step solution:
1. Double-Check Your Dependencies
First, confirm your pom.xml includes the correct attach API dependency (match the version to your Elastic APM server):
<dependency> <groupId>co.elastic.apm</groupId> <artifactId>apm-agent-attach</artifactId> <version>your-agent-version</version> <scope>runtime</scope> </dependency>
2. Choose a Lifecycle Hook to Trigger Attachment
Vanilla Spring doesn't have Spring Boot's ApplicationRunner or CommandLineRunner, but you have two reliable options to run the attacher:
Option 1: Spring Context Refreshed Listener
Use a Spring ApplicationListener to listen for the ContextRefreshedEvent (this fires when the Spring context is fully loaded). This ensures the agent is attached right before your app is ready to handle requests.
Create a component class:
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; import co.elastic.apm.attach.ElasticApmAttacher; @Component public class ApmAgentInitializer implements ApplicationListener<ContextRefreshedEvent> { // Prevent duplicate attachment (even if the event fires multiple times) private boolean agentAttached = false; @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (!agentAttached) { // Configure your APM settings here (adjust to your environment) System.setProperty("elastic.apm.service_name", "your-spring-core-rest-app"); System.setProperty("elastic.apm.server_url", "http://your-apm-server:8200"); System.setProperty("elastic.apm.environment", "staging"); // Attach the agent ElasticApmAttacher.attach(); agentAttached = true; System.out.println("Elastic APM agent successfully attached"); } } }
Option 2: Servlet Context Listener (For Web Apps)
If your app is deployed to a servlet container (like Tomcat), using a ServletContextListener is another solid choice. This runs early in the web app initialization process, before the Spring context loads.
Create a listener class with @WebListener (or register it in your web.xml):
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import co.elastic.apm.attach.ElasticApmAttacher; @WebListener public class ApmServletInitializer implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // Set APM configuration properties System.setProperty("elastic.apm.service_name", "your-spring-core-rest-app"); System.setProperty("elastic.apm.server_url", "http://your-apm-server:8200"); // Attach the agent ElasticApmAttacher.attach(); } @Override public void contextDestroyed(ServletContextEvent sce) { // No cleanup needed—APM agent stops automatically when the JVM shuts down } }
3. Verify the Setup
- Start your app and check the logs for messages from the Elastic APM agent (you should see lines like
Elastic APM agent started). - Send test requests to your JAX-RS endpoints and confirm traces are appearing in your Elastic APM dashboard.
Key Notes
- Idempotency: While
ElasticApmAttacher.attach()is designed to be idempotent, adding a flag to prevent duplicate calls is still a good practice (especially if the context refresh event fires multiple times). - Configuration: Instead of hardcoding system properties, you can load config from a properties file or environment variables for flexibility.
- JAX-RS Auto-Instrumentation: The Elastic APM Java agent automatically instruments JAX-RS endpoints once attached—no extra code needed for basic tracing.
内容的提问来源于stack exchange,提问作者Neer1009

