Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

OtlpExporter unstable

Shared batch exporter for OTLP/HTTP observability modules.

Signal modules use this exporter to buffer already-encoded telemetry and post it to a configured OTLP endpoint. It owns the scoped transport loop, batching, retry behavior, temporary disabling after repeated failures, and final flush during shutdown.

3 exports Since v4.0.0 Source

Constructors

make

unstable
Since v4.0.0

Creates a scoped OTLP batch exporter.

Details

The exporter buffers pushed data, periodically posts encoded batches to the configured URL, retries transient failures, temporarily disables exporting after unhandled failures, and flushes during scope finalization up to shutdownTimeout.

Optional gzip compression uses HttpClientRequest.compressBody before the retry loop, so retries reuse the compressed bytes. Bodies smaller than 1KB remain uncompressed. Compression requires runtime CompressionStream support; compression failures follow the same temporary-disable behavior as transport failures and do not fail flushing or shutdown.

Signature

declare const make: (options: {
readonly body: (data: Array<any>) => readonly [body: HttpBody, onSuccess: Effect.Effect<void>];
readonly compression?: "gzip" | "none";
readonly exportEmpty?: boolean;
readonly exportInterval: Duration.Input;
readonly headers: Headers.Input | undefined;
readonly label: string;
readonly maxBatchSize: number | "disabled";
readonly shutdownTimeout: Duration.Input;
readonly url: string;
}) => Effect.Effect<{
readonly push: (data: unknown) => void;
}, never, Flusher | HttpClient.HttpClient | Scope.Scope>

Layers

Since v4.0.0

Provides a Flusher backed by a fresh registry.

Details

This is intentionally a single module-level constant rather than a factory: layer memoization is keyed by layer instance, so every signal layer referencing this same constant shares one registry per layer build, and one flush drains traces, logs and metrics together. A factory returning a new layer per call would silently create one registry per signal.

Registration is scoped โ€” an exporter is removed from the registry when its own scope closes. Flushing with an empty registry is a no-op.

Note that flush cannot await an export that was already in flight when it was called (for example one started by the export interval); it only waits for the exports it initiates.

Signature

declare const layerFlusher: Layer.Layer<Flusher>

Services

Flusher

unstable
Since v4.0.0

Registry of exporter flush operations, used to manually drain buffered telemetry before the surrounding scope closes.

Details

Every exporter created by make registers its export operation here, so a single flush drains all signals sharing the registry. flush returns only after the exports it initiated have settled, cannot fail, and respects each exporter's temporary-disable window. Wrap it with Effect.timeoutOption to bound its duration at the call site.

Signature

declare class Flusher extends Shape<"effect/observability/OtlpExporter/Flusher", {
readonly flush: Effect<void>;
readonly register: (run: Effect<void>) => Effect<void, never, Scope>;
}, this> {
constructor(_: never);
}