skip to content

What do the @Timed and @Counted annotations do in a Micrometer/Spring Boot application?

level: juniorimportance: should knowfreq 40%

answer

  1. @Timed -> Timer (duration+count), @Counted -> Counter
  2. auto tags: class, method, exception, (result)
  3. default names method.timed / method.counted
  4. handled by TimedAspect / CountedAspect via AOP
  5. @Timed on a class times every method

basics

~20 s

@Timed records how long a method takes (a Timer metric); @Counted counts how many times it runs (a Counter). You put them on a method and Micrometer publishes the metric automatically, without writing timing code by hand.

solid answer

~40 s

@Timed (io.micrometer.core.annotation.Timed) and @Counted are declarative Micrometer annotations that let you emit metrics without hand-writing instrumentation. @Timed creates a Timer that records the duration and count of a method's executions; @Counted creates a Counter that increments per invocation. Both automatically tag the metric with the class and method names, and add an 'exception' tag (and, for @Counted, a 'result' = success/failure tag). They are handled at runtime by Spring AOP aspects — TimedAspect and CountedAspect — which intercept the annotated method through a proxy, so the annotations only work on Spring-managed beans whose methods are invoked through the proxy. You can also put @Timed on a class to time every method. The metric name comes from the annotation's value attribute (defaults: method.timed / method.counted).

code

java · 18 lines
java
import io.micrometer.core.annotation.Counted;
import io.micrometer.core.annotation.Timed;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Timed(value = "orders.place", description = "Time to place an order")
    public Order place(OrderRequest req) {
        // ... work ...
        return new Order();
    }

    @Counted(value = "orders.cancelled", recordFailuresOnly = false)
    public void cancel(String orderId) {
        // increments a counter tagged class/method/result/exception
    }
}

go deeper

for a junior

Know that @Timed measures duration, @Counted counts calls, and both publish Micrometer meters automatically.

for a middle

Know the automatic tags (class/method/exception/result) and the default metric names, plus that an aspect does the work.

for a senior

Be able to explain the AOP proxy mechanism, when declarative beats imperative, and distribution attributes.

for a principal

Frame declarative metrics within an observability strategy: cardinality, aggregation across instances, and consistency of naming/tagging conventions.

## What these annotations are Micrometer is the metrics facade used by Spring Boot Actuator (like SLF4J but for metrics). Normally you instrument code imperatively: obtain a `MeterRegistry`, build a `Timer`, and wrap the code in `timer.record(() -> ...)`. The **declarative** alternative is to annotate a method and let an aspect do the wrapping for you. - **`@Timed`** — `io.micrometer.core.annotation.Timed`. Produces a **`Timer`** meter: it records both the **count** of executions and the **total/max duration**. Timers expose `_count`, `_sum` (total time) and `_max`. - **`@Counted`** — `io.micrometer.core.annotation.Counted`. Produces a **`Counter`** meter: a monotonically increasing number incremented once per method call. ## Key attributes `@Timed`: - `value` — the metric name. If omitted, the default is **`method.timed`**. - `extraTags` — additional key/value tags as a flat array, e.g. `{"region", "eu"}`. - `description` — human-readable meter description. - `longTask` (boolean, default false) — switch from a `Timer` to a `LongTaskTimer`. - `histogram` (boolean) and `percentiles` (double[]) — distribution statistics (covered in the senior questions). `@Counted`: - `value` — metric name, default **`method.counted`**. - `recordFailuresOnly` (boolean, default false) — only count invocations that threw. - `extraTags`, `description`. ## Automatic tags The aspects add dimensional tags automatically: - `class` — the fully-qualified declaring class. - `method` — the method name. - `exception` — the simple name of the thrown exception, or `none` on success. - (`@Counted` only) `result` — `success` or `failure`. This is what makes them useful: one annotation yields a metric you can slice by class, method, and exception in Prometheus/Grafana. ## How it actually runs Both annotations are inert on their own. At runtime, **Spring AOP** aspects — `TimedAspect` and `CountedAspect` (from `io.micrometer.core.aspect`) — intercept calls to annotated methods, start timing/count before the call, and record after it returns or throws. Because this is proxy-based AOP, the annotations only take effect on **Spring beans** invoked **through their proxy** (external calls), and the aspect beans must exist. In modern Spring Boot (2.5+, with `spring-boot-starter-aop` on the classpath) `MetricsAspectsAutoConfiguration` registers those aspects for you. ## When to use Reach for declarative metrics for coarse, method-level timing/counting of service or repository methods where you want quick observability with minimal code. Prefer **imperative** `Timer`/`Counter` when you need custom/dynamic tag values, to time a sub-section of a method, or to avoid the AOP proxy constraints.

  • What tags does @Counted add automatically that @Timed does not?
    A `result` tag with value `success` or `failure`. Both add `class`, `method`, and `exception`, but only @Counted has the `result` dimension distinguishing normal returns from thrown exceptions.
  • If you annotate a method but see no metric published, what is the first thing to check?
    That the corresponding aspect (TimedAspect/CountedAspect) is registered as a bean and Spring AOP is active, and that the call goes through the Spring proxy (not a self-invocation).

saying these in an interview costs you the question

  • Thinking @Timed produces a Counter or @Counted produces a Timer (they are swapped).
  • Believing the annotations work anywhere, including on plain non-Spring objects.
  • Assuming a metric appears with zero configuration even without the aspect beans.

context