pipette/
trace.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! [`tracing`] support.
5
6use parking_lot::Mutex;
7use std::sync::Arc;
8use tracing_subscriber::filter::Targets;
9use tracing_subscriber::fmt::format::FmtSpan;
10use tracing_subscriber::layer::SubscriberExt;
11use tracing_subscriber::util::SubscriberInitExt;
12
13/// Global writer shared with the tracing subscriber so that the underlying
14/// pipe can be swapped on reconnect (e.g. after save/restore).
15static TRACING_WRITER: Mutex<Option<Arc<TracingWriter>>> = Mutex::new(None);
16
17/// Initialize tracing, returning a mesh pipe to read logs from.
18///
19/// On the first call this installs the global tracing subscriber. On
20/// subsequent calls (after a save/restore reconnect) it swaps the
21/// underlying write pipe so that logs flow to the new host connection.
22pub fn init_tracing() -> mesh::pipe::ReadPipe {
23    let (log_read, log_write) = mesh::pipe::pipe();
24
25    let mut global = TRACING_WRITER.lock();
26    if let Some(writer) = global.as_ref() {
27        // Already initialized — swap to the new pipe so logs flow
28        // to the new host connection after a save/restore reconnect.
29        *writer.0.lock() = log_write;
30    } else {
31        let writer = Arc::new(TracingWriter(Mutex::new(log_write)));
32        *global = Some(writer.clone());
33        drop(global);
34
35        let targets = Targets::new()
36            .with_default(tracing::level_filters::LevelFilter::DEBUG)
37            .with_target("mesh_remote", tracing::level_filters::LevelFilter::INFO);
38
39        tracing_subscriber::fmt()
40            .compact()
41            .with_ansi(false)
42            .with_timer(tracing_subscriber::fmt::time::uptime())
43            .with_writer(writer)
44            .with_max_level(tracing::level_filters::LevelFilter::DEBUG)
45            .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
46            .log_internal_errors(true)
47            .finish()
48            .with(targets)
49            .init();
50    }
51
52    tracing::info!("tracing initialized");
53    log_read
54}
55
56struct TracingWriter(Mutex<mesh::pipe::WritePipe>);
57
58impl std::io::Write for &TracingWriter {
59    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
60        // Note that this will fail if the pipe fills up. This is probably fine
61        // for this use case.
62        self.0.lock().write_nonblocking(buf)
63    }
64
65    fn flush(&mut self) -> std::io::Result<()> {
66        Ok(())
67    }
68}