Monitor Spring Boot API Latency with Actuator, AOP, and Prometheus

This tutorial shows how to instrument Spring Boot APIs using Actuator, a custom @Monitor annotation with AOP, and Prometheus to collect and visualize method execution times, providing a complete end‑to‑end monitoring solution.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Monitor Spring Boot API Latency with Actuator, AOP, and Prometheus

1. Introduction

This article explains how to monitor arbitrary API call latency in a Spring Boot application by combining Spring Boot Actuator, a custom @Monitor annotation, AOP, and Prometheus.

2. Environment Setup

Required dependencies:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
  <scope>runtime</scope>
</dependency>

Expose all actuator endpoints:

management:
  endpoints:
    web:
      base-path: /ac
      exposure:
        include: '*'

3. Practical Example

3.1 Define the @Monitor annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Monitor {
  String[] tags() default {};
}

3.2 Implement the monitoring aspect

@Component
@Aspect
public class MonitorAspect {
  private final MeterRegistry meterRegistry;
  private static final String API_TIMER_METER_NAME = "myapp.api.timer";

  public MonitorAspect(MeterRegistry meterRegistry) {
    this.meterRegistry = meterRegistry;
  }

  @Pointcut("@annotation(monitor)")
  private void pcMonitor(Monitor monitor) {}

  @Around("pcMonitor(monitor)")
  public Object around(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {
    Timer.Sample sample = Timer.start(this.meterRegistry);
    String[] tags = monitor.tags();
    Object ret = null;
    Throwable ex = null;
    try {
      ret = pjp.proceed();
    } catch (Throwable th) {
      ex = th;
      throw th;
    } finally {
      List<String> listTags = new ArrayList<>();
      listTags.addAll(Arrays.asList(tags));
      if (Objects.nonNull(ex)) {
        listTags.add(ex.getClass().getSimpleName());
      }
      Timer timer = meterRegistry.timer(API_TIMER_METER_NAME, listTags.toArray(new String[0]));
      sample.stop(timer);
    }
    return ret;
  }
}

3.3 Service layer

@Service
public class UserService {
  private static final List<User> DATAS = List.of(
      new User(1L, "张三", "男", 22),
      new User(2L, "李四", "男", 23),
      new User(3L, "王五", "女", 22),
      new User(4L, "赵六", "男", 32));

  public List<User> queryUsers() {
    sleep(2000);
    return DATAS;
  }

  public User queryById(Long id) {
    sleep(1000);
    return DATAS.stream().filter(user -> user.getId() == id).findFirst().orElse(null);
  }

  private void sleep(int time) {
    try {
      TimeUnit.MILLISECONDS.sleep(new Random().nextInt(time));
    } catch (InterruptedException e) {}
  }
}

3.4 Controller layer

@Monitor(tags = {"UserController", "list"})
@GetMapping("")
public List<User> list() {
  return this.userService.queryUsers();
}

@Monitor(tags = {"UserController", "ById"})
@GetMapping("/{id}")
public User queryById(@PathVariable Long id) {
  return this.userService.queryById(id);
}

Note: The tags attribute must contain an even number of strings because they are paired as key/value tags.

4. Testing and Visualization

Invoke the two endpoints repeatedly and access /ac/metrics/myapp.api.timer to view raw metrics. Prometheus scrapes the /actuator/prometheus endpoint, and you can visualize latency trends in Grafana or the Prometheus UI.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

monitoringBackend DevelopmentPrometheusSpring BootActuator
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.