Skip to main content

rskit_logging/masking/
writer.rs

1//! [`MakeWriter`] adapters that mask log lines before they reach the sink.
2
3use std::io;
4use std::sync::Arc;
5
6use tracing_subscriber::fmt::MakeWriter;
7
8use super::masker::Masker;
9
10/// A [`MakeWriter`] wrapper that masks sensitive data in log output.
11///
12/// Wraps an inner writer
13/// and applies masking via the supplied [`Masker`] to every log line before it reaches the underlying output.
14///
15/// # Examples
16///
17/// ```ignore
18/// use rskit_logging::masking::{DefaultMasker, MaskingMakeWriter, Masker};
19/// use std::sync::Arc;
20///
21/// let masker: Arc<dyn Masker> = Arc::new(DefaultMasker::default());
22/// let writer = MaskingMakeWriter::new(std::io::stdout, masker);
23/// ```
24pub struct MaskingMakeWriter<W> {
25    inner: W,
26    masker: Arc<dyn Masker>,
27}
28
29impl<W> MaskingMakeWriter<W> {
30    /// Create a new masking writer wrapper.
31    ///
32    /// `inner` is the underlying [`MakeWriter`] (e.g., `std::io::stdout`).
33    /// `masker` is the masking engine wrapped in an [`Arc`].
34    pub fn new(inner: W, masker: Arc<dyn Masker>) -> Self {
35        Self { inner, masker }
36    }
37}
38
39impl<'a, W: MakeWriter<'a>> MakeWriter<'a> for MaskingMakeWriter<W> {
40    type Writer = MaskingWriter<W::Writer>;
41
42    fn make_writer(&'a self) -> Self::Writer {
43        MaskingWriter {
44            inner: self.inner.make_writer(),
45            masker: Arc::clone(&self.masker),
46            buffer: Vec::with_capacity(256),
47        }
48    }
49
50    fn make_writer_for(&'a self, meta: &tracing::Metadata<'_>) -> Self::Writer {
51        MaskingWriter {
52            inner: self.inner.make_writer_for(meta),
53            masker: Arc::clone(&self.masker),
54            buffer: Vec::with_capacity(256),
55        }
56    }
57}
58
59/// A writer that buffers output and applies masking on flush / drop.
60///
61/// Created by [`MaskingMakeWriter`]. Buffers all `write` calls
62/// and applies masking when the writer is flushed or dropped (at the end of each log event).
63pub struct MaskingWriter<W: io::Write> {
64    inner: W,
65    masker: Arc<dyn Masker>,
66    buffer: Vec<u8>,
67}
68
69impl<W: io::Write> io::Write for MaskingWriter<W> {
70    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
71        self.buffer.extend_from_slice(buf);
72        Ok(buf.len())
73    }
74
75    fn flush(&mut self) -> io::Result<()> {
76        if !self.buffer.is_empty() {
77            let output = String::from_utf8_lossy(&self.buffer);
78            let masked = self.masker.mask_output(&output);
79            self.inner.write_all(masked.as_bytes())?;
80            self.buffer.clear();
81        }
82        self.inner.flush()
83    }
84}
85
86impl<W: io::Write> Drop for MaskingWriter<W> {
87    fn drop(&mut self) {
88        if !self.buffer.is_empty() {
89            // Best-effort flush;
90            // errors are silently ignored as is standard practice for log writers.
91            let output = String::from_utf8_lossy(&self.buffer);
92            let masked = self.masker.mask_output(&output);
93            let _ = self.inner.write_all(masked.as_bytes());
94            self.buffer.clear();
95            let _ = self.inner.flush();
96        }
97    }
98}