Interface MetricsRecorder

All Known Implementing Classes:
MicrometerMetricsRecorder, NoOpMetricsRecorder
Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

SPI for receiving per-request metric events from juneau-rest-server.

One record(...) call fires after every @RestOp handler invocation — including exception paths. Bridge implementations translate the event into the format their downstream registry expects (Micrometer io.micrometer.core.instrument.Timer, OpenTelemetry metric instruments, Dropwizard MetricRegistry, etc.).

Off-by-default contract

juneau-rest-server resolves the MetricsRecorder via RestContext.getBeanStore().getBean(MetricsRecorder.class). When no bean is supplied, NoOpMetricsRecorder.INSTANCE is used and the framework never allocates a downstream registry, never reaches Micrometer / OpenTelemetry on its own, and never adds any per-request cost beyond a single static-field read. To opt in, the consumer registers a MetricsRecorder bean — typically via @Bean MetricsRecorder on the resource or its parent.

Usage

@Rest public class MyResource extends RestServlet { @Bean public MetricsRecorder recorder(MeterRegistry registry) { return new MicrometerMetricsRecorder(registry); } }

Threading

Implementations must be thread-safe — the same MetricsRecorder bean is invoked concurrently from every request thread for the lifetime of the RestContext. The framework calls record(...) synchronously inside the request thread immediately after the response status is known, so implementations should keep the call cheap (constant-time tag computation, non-blocking registry writes).

Event ordering

For a given @RestOp invocation, record(...) fires exactly once after the operation completes — whether the handler returned normally, threw a BasicHttpException, or triggered an internal-server-error. The elapsed parameter measures wall-clock time spent inside the framework's per-op invocation boundary (from just before parameter resolution to just after the handler returns or throws). It does not include response serialization performed later in the request pipeline.

See Also:
Since:
10.0.0
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    record(String opName, String httpMethod, String uriTemplate, int statusCode, Duration elapsed, Throwable error, String metricName, String metricTags)
    Records one per-request metric event.
    default void
    record(String metricName, String metricTags, Duration elapsed, Throwable error)
    Records one custom (non-request) observation event.
  • Method Details

    • record

      void record(String opName, String httpMethod, String uriTemplate, int statusCode, Duration elapsed, Throwable error, String metricName, String metricTags)
      Records one per-request metric event.
      Parameters:
      opName - The fully-qualified @RestOp method name (e.g. "com.example.OrderResource.create(java.lang.String,com.example.Order)"). Never null; never blank.
      httpMethod - The uppercased HTTP method of the request (e.g. "GET", "POST"). Never null; never blank.
      uriTemplate - The @RestOp path template (e.g. "/users/{id}"), not the raw concrete URI. Bridges should use this as the uri tag to keep cardinality bounded. May be empty for resource-level operations that match /*; never null.
      statusCode - The HTTP response status code (e.g. 200, 404, 500). If the handler threw before the status was set, this is the framework's inferred status (typically 500 for an unmapped exception, or the exception's status for a BasicHttpException).
      elapsed - Wall-clock duration of the @RestOp invocation. Never null; never negative.
      error - The exception thrown by the handler, or null if the call completed normally. Bridges typically derive an exception tag from the throwable's simple class name, or "None" when null (mirrors Spring Boot's WebMvcMetricsFilter convention).
      metricName - Per-op metric name override from RestOp.metricName(). Empty string (default) means the implementation should use its own default name derivation. Never null.
      metricTags - Per-op additional metric tags from RestOp.metricTags(). Format: comma-separated key=value pairs (e.g. "team=payments,region=us-east"). Empty string (default) means no additional tags. Never null.
    • record

      default void record(String metricName, String metricTags, Duration elapsed, Throwable error)
      Records one custom (non-request) observation event.

      Unlike record(String, String, String, int, Duration, Throwable, String, String), this entry point is not tied to an HTTP request — it is the substrate for the explicit programmatic observation API (Observations) so application code can time an arbitrary block of work. Bridge implementations record a timer named metricName carrying metricTags (plus an exception tag derived from error).

      The default implementation does nothing — a bridge that does not override it simply does not record custom observations, and the no-backend path stays zero-allocation. The shipped Micrometer bridge overrides it.

      Parameters:
      metricName - The timer name (e.g. "order.load"). Never null; never blank.
      metricTags - Additional tags as comma-separated key=value pairs (e.g. "team=payments,region=us-east"). Empty string means no additional tags. Never null.
      elapsed - Wall-clock duration of the observed block. Never null; never negative.
      error - The exception thrown by the observed block, or null if it completed normally.