Observability

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 of live in-flight operations plus saturating counters for admission rejections, dropped observer events, and observer/telemetry failures:

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):

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, with no reflection and no auto-magic:

// sh.repost:repost-client-micrometer
ClientOptions.builder()
        .observer(MicrometerRepostObserver.create(meterRegistry))
        .build();

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