Interface TracerHook

All Known Implementing Classes:
NoOpTracerHook, OtelTracerHook
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 tracing events from juneau-rest-server.

startSpan(RestRequest) fires immediately before every @RestOp handler runs — the returned Scope is closed by the framework after the handler completes (including the exception path). Bridge implementations create whatever the downstream tracer expects (an OpenTelemetry io.opentelemetry.api.trace.Span, a Brave Span, an application tracing object, etc.) and surface the close / status / error transitions through the Scope.

Off-by-default contract

juneau-rest-server resolves the TracerHook via RestContext.getBeanStore().getBean(TracerHook.class). When no bean is supplied, NoOpTracerHook.INSTANCE is used — the framework never reaches for a tracer, never inspects headers for W3C trace context, and adds no per-request cost beyond a single static-field read plus a single AutoCloseable.close() on a singleton noop scope. To opt in, the consumer registers a TracerHook bean — typically via @Bean TracerHook on the resource or its parent.

Usage

@Rest public class MyResource extends RestServlet { @Bean public TracerHook tracer(OpenTelemetry otel) { return new OtelTracerHook(otel); } }

Lifecycle

For each @RestOp invocation, the framework:

  1. Calls startSpan(RestRequest) just before parameter resolution, passing the in-flight RestRequest so the bridge can read trace-context headers (traceparent / tracestate) and set request-derived attributes.
  2. Runs the handler (which may throw).
  3. Calls Scope.setStatusCode(int) with the resolved response status, then Scope.setError(Throwable) if the handler threw, then Scope.close() — always in a finally block so the span closes even on exception.

Implementations must be thread-safe — the same TracerHook bean is invoked concurrently from every request thread for the lifetime of the RestContext.

See Also:
Since:
10.0.0
  • Method Details

    • startSpan

      Opens a new span for the in-flight request.
      Parameters:
      request - The in-flight RestRequest. Never null. Bridges may read headers for distributed-tracing context propagation (e.g. W3C traceparent / tracestate), the HTTP method, the path, etc. Bridges must not mutate the request.
      Returns:
      The opened Scope. Never null. Returning null is a contract violation — bridges that cannot open a span (e.g. because the trace context is invalid) should return NoOpTracerHook.NoOpScope.INSTANCE so the framework's close-in-finally contract still holds.
    • startSpan

      default Scope startSpan(String spanName)
      Opens a new span for a custom (non-request) observation.

      Unlike startSpan(RestRequest), this entry point is not tied to an in-flight RestRequest — it is the substrate for the explicit programmatic observation API (Observations) so application code can trace an arbitrary block of work that has no associated HTTP request. Bridge implementations typically open a span of kind INTERNAL (rather than SERVER) named spanName.

      The default implementation returns NoOpTracerHook.NoOpScope.INSTANCE — a bridge that does not override this method simply does not trace custom observations, and the no-backend path stays zero-allocation. Both shipped bridges (the OpenTelemetry TracerHook) override it.

      Parameters:
      spanName - The span name (e.g. "loadOrder"). Never null; never blank.
      Returns:
      The opened Scope. Never null — implementations that cannot open a span must return NoOpTracerHook.NoOpScope.INSTANCE so the close-in-finally contract holds.
    • startSpan

      default Scope startSpan(RestRequest request, TraceContextCarrier carrier, TraceOperation operation)
      Opens a new span for the in-flight request, given a pre-extracted non-HTTP TraceContextCarrier and derived TraceOperation.

      Added as a source-compatible default overload so every existing TracerHook implementation — including a bare lambda implementing only startSpan(RestRequest) — keeps compiling unchanged. The default implementation simply delegates to startSpan(RestRequest), ignoring carrier and operation; a bridge only needs to override this overload if it wants to honor a non-HTTP carrier (for example MCP's params._meta) or name the span after operation instead of its own HTTP-derived default.

      Parameters:
      request - The in-flight RestRequest. Never null.
      carrier - The TraceContextCarrier an active TraceContextExtractor recognized among the resolved operation arguments, or null if none was recognized (the bridge should then fall back to HTTP headers only).
      operation - The TraceOperation an active TraceContextExtractor derived for this invocation. Never null; TraceOperation.DEFAULT when the extractor supplied no override.
      Returns:
      The opened Scope. Never null.
    • inject

      default void inject(TraceContextCarrier carrier)
      Renders this hook's active trace context (if any) into carrier.

      The substrate for TraceContexts.inject(TracerHook, TraceContextCarrier). The default implementation is a no-op — NoOpTracerHook and any bridge that does not override this method write nothing. A bridge with an actual trace context to render (for example the OpenTelemetry bridge in juneau-rest-server-tracing-otel) overrides this to write traceparent / tracestate / baggage keys via TraceContextCarrier.set(String, String).

      Parameters:
      carrier - The TraceContextCarrier to write into. Never null.