rust_analyzer/tracing/
json.rs

1//! A [tracing_subscriber::layer::Layer] that exports new-line delinated JSON.
2//!
3//! Usage:
4//!
5//! ```ignore
6//! # use tracing_subscriber::Registry;
7//! let layer = json::TimingLayer::new(std::io::stderr);
8//! Registry::default().with(layer).init();
9//! ```
10
11use std::{io::Write as _, marker::PhantomData, time::Instant};
12
13use ide_db::FxHashSet;
14use tracing::{
15    Event, Subscriber,
16    span::{Attributes, Id},
17};
18use tracing_subscriber::{Layer, fmt::MakeWriter, layer::Context, registry::LookupSpan};
19
20struct JsonData {
21    name: &'static str,
22    start: std::time::Instant,
23}
24
25impl JsonData {
26    fn new(name: &'static str) -> Self {
27        Self { name, start: Instant::now() }
28    }
29}
30
31#[derive(Debug)]
32pub(crate) struct TimingLayer<S, W> {
33    writer: W,
34    _inner: PhantomData<fn(S)>,
35}
36
37impl<S, W> TimingLayer<S, W> {
38    pub(crate) fn new(writer: W) -> Self {
39        Self { writer, _inner: PhantomData }
40    }
41}
42
43impl<S, W> Layer<S> for TimingLayer<S, W>
44where
45    S: Subscriber + for<'span> LookupSpan<'span>,
46    W: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
47{
48    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
49        let span = ctx.span(id).unwrap();
50
51        let data = JsonData::new(attrs.metadata().name());
52        span.extensions_mut().insert(data);
53    }
54
55    fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {}
56
57    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
58        #[derive(serde_derive::Serialize)]
59        struct JsonDataInner {
60            name: &'static str,
61            elapsed_ms: u128,
62        }
63
64        let span = ctx.span(&id).unwrap();
65        let Some(data) = span.extensions_mut().remove::<JsonData>() else {
66            return;
67        };
68
69        let data = JsonDataInner { name: data.name, elapsed_ms: data.start.elapsed().as_millis() };
70        let mut out = serde_json::to_string(&data).expect("Unable to serialize data");
71        out.push('\n');
72        self.writer.make_writer().write_all(out.as_bytes()).expect("Unable to write data");
73    }
74}
75
76#[derive(Default, Clone, Debug)]
77pub(crate) struct JsonFilter {
78    pub(crate) allowed_names: Option<FxHashSet<String>>,
79}
80
81impl JsonFilter {
82    pub(crate) fn from_spec(spec: &str) -> Self {
83        let allowed_names = if spec == "*" {
84            None
85        } else {
86            Some(FxHashSet::from_iter(spec.split('|').map(String::from)))
87        };
88
89        Self { allowed_names }
90    }
91}