auths_telemetry/sinks/stdout.rs
1//! Synchronous stdout telemetry sink.
2//!
3//! Writes newline-delimited JSON directly to a `Write` impl under a mutex.
4//! Blocking I/O here is intentional — telemetry writes are rare relative to
5//! application throughput. Callers that need non-blocking MPSC buffering
6//! should build a `TokioMpscSink` in their binary crate and pass it to
7//! `init_telemetry_with_sink`.
8
9use std::io::{BufWriter, Write};
10use std::sync::Mutex;
11
12use crate::ports::EventSink;
13
14/// Telemetry sink that writes newline-delimited JSON to any `Write` impl.
15///
16/// Thread-safe via `Mutex`. Suitable for stdout, files, or in-memory buffers.
17pub struct WriterSink<W: Write + Send> {
18 writer: Mutex<BufWriter<W>>,
19}
20
21impl<W: Write + Send> WriterSink<W> {
22 /// Create a sink wrapping `writer`.
23 pub fn new(writer: W) -> Self {
24 Self {
25 writer: Mutex::new(BufWriter::new(writer)),
26 }
27 }
28}
29
30/// Construct a `WriterSink` that writes to stdout.
31pub fn new_stdout_sink() -> WriterSink<std::io::Stdout> {
32 WriterSink::new(std::io::stdout())
33}
34
35impl<W: Write + Send + Sync + 'static> EventSink for WriterSink<W> {
36 fn emit(&self, payload: &str) {
37 if let Ok(mut w) = self.writer.lock() {
38 let _ = writeln!(w, "{payload}");
39 let _ = w.flush();
40 }
41 }
42
43 fn flush(&self) {
44 if let Ok(mut w) = self.writer.lock() {
45 let _ = w.flush();
46 }
47 }
48}