Skip to main content

osproxy_observe/
export.rs

1//! The span-export seam: where assembled OTLP payloads go.
2//!
3//! Export is **read-only and off the request's critical path** (`docs/05`): the
4//! pipeline hands a finished payload to [`SpanExporter::export`], which returns
5//! immediately, a concrete exporter ships it in the background and an export
6//! failure never affects the request. When no exporter is configured the default
7//! [`NoopExporter`] reports [`SpanExporter::enabled`] `false`, so the pipeline
8//! skips even *encoding* the payload, "Off" is near-zero cost.
9
10use serde_json::Value;
11
12/// Receives finished OTLP span payloads for delivery to a collector.
13///
14/// Implementations MUST NOT block: `export` is called inline on the request path,
15/// so any network I/O belongs in a spawned task. They MUST NOT panic.
16pub trait SpanExporter: Send + Sync + 'static {
17    /// Whether this exporter will do anything. The pipeline checks this before
18    /// building a payload, so a disabled exporter costs only this call.
19    fn enabled(&self) -> bool {
20        true
21    }
22
23    /// Hands off a finished OTLP `ResourceSpans` payload (from
24    /// [`resource_spans`](crate::resource_spans)) for background delivery.
25    /// Returns immediately; delivery is best-effort.
26    fn export(&self, payload: Value);
27}
28
29/// The default exporter: export is disabled, so the pipeline never encodes a
30/// payload and nothing is shipped.
31#[derive(Clone, Copy, Debug, Default)]
32pub struct NoopExporter;
33
34impl SpanExporter for NoopExporter {
35    fn enabled(&self) -> bool {
36        false
37    }
38
39    fn export(&self, _payload: Value) {}
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn the_noop_exporter_is_disabled() {
48        assert!(!NoopExporter.enabled());
49        NoopExporter.export(serde_json::json!({})); // no panic, no effect
50    }
51}