Backend Development 7 min read

Master Spring Boot Observability with @Timed, @Counted, and @MeterTag

Learn how to enable comprehensive observability in Spring Boot 3.2.5 by leveraging Micrometer’s @Timed, @Counted, and @MeterTag annotations, configuring Actuator endpoints, and customizing aspects to monitor method execution time, request counts, and parameters, complete with practical code examples and Prometheus integration.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot Observability with @Timed, @Counted, and @MeterTag

1. Introduction

Observability is the ability to view the internal state of a running system from the outside, consisting of logging, metrics, and tracing. Spring Boot uses Micrometer Observation for metrics and tracing.

From Spring Boot 3.2.0 you can use the annotations @Timed , @Counted , and @MeterTag to instrument endpoints.

2. Practical Example

2.1 Environment Setup

Add the following dependencies:

<code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- Enable Prometheus endpoint --&gt;
&lt;dependency&gt;
  &lt;groupId&gt;io.micrometer&lt;/groupId&gt;
  &lt;artifactId&gt;micrometer-registry-prometheus&lt;/artifactId&gt;
&lt;/dependency&gt;</code>

Configure Actuator to expose all endpoints:

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

Enable observation annotations:

<code>management:
  observations:
    annotations:
      enabled: true</code>

2.2 @Timed Annotation

The @Timed annotation records execution time of a method.

<code>@Timed("MeterDemoController.pp")
@GetMapping("/pp")
public Object pp() {
    try {
        TimeUnit.MILLISECONDS.sleep(new Random().nextInt(500));
    } catch (InterruptedException e) {}
    return "MeterDemoController pp";
}</code>

Access the endpoint and view metrics via the /actuator/prometheus endpoint. Example screenshots illustrate the generated metrics such as MeterDemoController_pp_seconds_count , MeterDemoController_pp_seconds_sum , and MeterDemoController_pp_seconds_max .

2.3 @MeterTag Annotation

Use @MeterTag to add custom parameters to metrics.

<code>@Bean
public MeterTagAnnotationHandler meterTagAnnotationHandler() {
    return new MeterTagAnnotationHandler(valueResolverClass -> {
        return new ValueResolver() {
            public String resolve(Object parameter) {
                System.out.printf("parameter: %s%n", parameter);
                return parameter.toString();
            }
        };
    }, valueExpressionResolverClass -> {
        return new ValueExpressionResolver() {
            public String resolve(String expression, Object parameter) {
                System.out.printf("expression: %s, parameter: %s%n", expression, parameter);
                return parameter.toString();
            }
        };
    });
}
</code>

Define an endpoint:

<code>@Timed("MeterDemoController.tag")
@GetMapping("/tag")
public Object tag(@MeterTag(key = "MeterDemoController#tag.name", expression = "#name") String name) {
    return "@MeterTag Annotation";
}
</code>

Calling the endpoint with different name values produces distinct metric entries, as shown in the screenshot.

2.4 @Counted Annotation

The @Counted annotation counts successful and failed method invocations.

<code>@Counted("MeterDemoController.cc")
@GetMapping("/{id}")
public Long get(@PathVariable("id") Long id) {
    if (id % 2 == 0) {
        throw new RuntimeException("参数错误");
    }
    return id;
}
</code>

Requests to /67 and /68 demonstrate counting of successes and failures.

2.5 Controlling Which Methods Are Monitored

Custom aspects can limit monitoring to classes annotated with @Controller :

<code>@Bean
public CountedAspect countedAspect(MeterRegistry meterRegistry) {
    return new CountedAspect(meterRegistry, this::skipNonControllers);
}
private boolean skipNonControllers(ProceedingJoinPoint pjp) {
    Class<?> targetClass = pjp.getTarget().getClass();
    return AnnotationUtils.findAnnotation(targetClass, Controller.class) == null;
}
</code>

With this configuration, only beans marked as controllers are instrumented.

Images illustrating the Prometheus metrics and endpoint responses are included above.

backendJavaObservabilityPrometheusSpring BootMicrometer
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

login 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.