pub fn subscriber<W>(
filter: EnvFilter,
writer: W,
) -> impl Subscriber + Send + Sync + 'staticExpand description
The subscriber init installs, writing to writer under filter.
It is the one place the log’s format is decided: no target, no colour, and every field
redacted. init hands it stderr; a test hands it a file and reads back what was
written.
use std::sync::{Arc, Mutex};
use majordomus_cli::logging;
use tracing_subscriber::EnvFilter;
let written = Arc::new(Mutex::new(Vec::new()));
let sink = written.clone();
let to_memory = move || Sink(sink.clone());
// assembled at run time, so that no committed file carries a credential's shape
let token = format!("{}{}", "ghp_", "a".repeat(24));
let log = logging::subscriber(EnvFilter::new("info"), to_memory);
tracing::subscriber::with_default(log, || {
tracing::info!(token = token.as_str(), "pushing with {token}");
});
let text = String::from_utf8(written.lock().unwrap().clone()).unwrap();
assert!(!text.contains(&token), "{text}");
assert!(text.contains("pushing with [redacted:github-token]"), "{text}");
assert!(text.contains(r#"token="[redacted:github-token]""#), "{text}");
struct Sink(Arc<Mutex<Vec<u8>>>);
impl std::io::Write for Sink {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}