SpanProcessor
SpanProcessor is the extension point exporter authors implement to do something with finished spans — ship them to a backend, buffer them, or collect them for tests. A Tracer calls onStart when a recorded span begins — RecordOnly as well as RecordAndSample — and onEnd with the immutable SpanData snapshot when it finishes. You rarely implement this directly: SpanProcessor.noop and the built-in in-memory collector cover development and testing, so you write one only to bridge spans to an export target such as OTLP.
To capture finished spans in memory and assert on them in tests, see How to Test Code That Logs, Traces and Records Metrics.
trait SpanProcessor extends AutoCloseable {
def onStart(span: Span): Unit // span still mutable; RecordOnly + RecordAndSample
def onEnd(spanData: SpanData): Unit // immutable snapshot — export from here
def shutdown(): Unit // flush and release; called by TracerProvider.shutdown()
def forceFlush(): Unit // flush buffered data now
override def close(): Unit = shutdown()
}
object SpanProcessor {
val noop: SpanProcessor // ignores every event
}
Implementing a Processor
A custom processor forwards each SpanData to an export target. Register it on the provider with addSpanProcessor; when several are registered, they receive callbacks in insertion order.
onEnd runs synchronously, on the thread that just ended the span — so whatever it does lands in that request's latency. Keep it to handing the snapshot off to a queue or buffer, and let a background thread do the network call:
import zio.blocks.telemetry._
val exporter = new SpanProcessor {
def onStart(span: Span): Unit = ()
def onEnd(spanData: SpanData): Unit = println(s"[EXPORT] ${spanData.name}")
def shutdown(): Unit = ()
def forceFlush(): Unit = ()
}
val provider = TracerProvider.builder.addSpanProcessor(exporter).build()
trace.install(provider)
trace.span("checkout") { _ => () } // prints: [EXPORT] checkout
provider.shutdown()
Integration
The global trace object's default provider already registers an InMemorySpanProcessor, so trace.collectedSpans and trace.clearSpans are available for test assertions without any additional setup.