> ## Documentation Index
> Fetch the complete documentation index at: https://repost.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Observe the Java Client

> Runtime diagnostics, lifecycle observers, and the Micrometer and OpenTelemetry bridges for the Repost Java client.

The runtime exposes three credential- and payload-free windows into a running client: a diagnostics snapshot, a bounded observer stream, and optional metrics and tracing bridges.

## Diagnostics

`diagnostics()` returns a credential- and payload-free snapshot — live in-flight operations plus saturating counters for admission rejections, dropped observer events, and observer/telemetry failures:

```java theme={"languages":{"custom":["/languages/repost.json"]}}
RuntimeDiagnostics diagnostics = repost.diagnostics();
System.out.println("in flight: " + diagnostics.getInFlightOperations());
System.out.println("dropped observer events: " + diagnostics.getDroppedObserverEvents());
System.out.println("observer failures: " + diagnostics.getObserverFailures());
```

## Observers

Register a `RepostObserver` to receive bounded, credential-free lifecycle events (operation start/end, attempt start/end, retry-delay, cancel):

```java theme={"languages":{"custom":["/languages/repost.json"]}}
ClientOptions options = ClientOptions.builder()
        .apiKey(System.getenv("REPOST_SEND_API_KEY"))
        .observer(event -> {
            // Runs on a dedicated dispatch thread, never the transport thread. Keep it
            // fast: if the bounded queue overflows, events are dropped and counted
            // (RuntimeDiagnostics.getDroppedObserverEvents), never blocked. A throwing
            // observer is isolated and never changes the send result.
            if (event.getKind() == ObserverEventKind.OPERATION_END) {
                recordEnd(event.getOperationId(), event.getDeliveryState());
            }
        })
        .build();
return RepostClient.create(options);
```

The observer is intentionally **lossy**: its queue is bounded (1,024 events), and when it fills, the runtime drops and counts events rather than slowing a send. Observers run on a dedicated dispatch thread (or a borrowed `observerExecutor`), never on the transport thread, so a slow or throwing observer can never change or block an application send.

## Micrometer and OpenTelemetry

For metrics and traces, add a bridge module and wire it as an observer or telemetry sink — no reflection, no auto-magic:

<CodeGroup>
  ```java Micrometer theme={"languages":{"custom":["/languages/repost.json"]}}
  // sh.repost:repost-client-micrometer
  ClientOptions.builder()
          .observer(MicrometerRepostObserver.create(meterRegistry))
          .build();
  ```

  ```java OpenTelemetry theme={"languages":{"custom":["/languages/repost.json"]}}
  // sh.repost:repost-client-opentelemetry
  ClientOptions.builder()
          .telemetry(OpenTelemetryRepostTelemetry.create(openTelemetry))
          .build();
  ```
</CodeGroup>

Micrometer records `repost.client.operations`, `repost.client.operation.duration`, `repost.client.attempts`, `repost.client.attempt.duration`, and `repost.client.retry.delay`, tagged with `outcome`, `error.code`, `delivery.state`, and `http.status.class`. OpenTelemetry emits one `repost.send` span with a `repost.send.attempt` child per attempt. Both carry only low-cardinality metadata — never payloads or credentials.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Testing" icon="flask-conical" href="/docs/send/java/testing" cta="Test" arrow="true">
    A recording transport, deterministic clocks, and a no-network guard.
  </Card>

  <Card title="Frameworks" icon="boxes" href="/docs/send/java/frameworks" cta="Inject" arrow="true">
    Spring Boot auto-wires these bridges when a registry bean is present.
  </Card>

  <Card title="Reliability" icon="shield-check" href="/docs/send/java/reliability" cta="Outcomes" arrow="true">
    The delivery states and error codes these signals report.
  </Card>
</Columns>
