summaryrefslogtreecommitdiffstats
path: root/profiling/src/lib.rs
blob: 8fc58fb841e6ca2ca26b23ea1c686b9ede4b1e3a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::time::Duration;
use tracing_subscriber::prelude::*;
use tracing_subscriber::Registry;

#[cfg(feature = "tracing-chrome")]
use {
    tracing_chrome::FlushGuard,
    tracing_subscriber::fmt::{format::DefaultFields, FormattedFields},
};

pub use tracing::{info_span, instrument};

/// Profiler state. This will likely need to be updated or reworked when adding new tracing backends.
pub struct Profiler {
    #[cfg(feature = "tracing-chrome")]
    /// [`FlushGuard`] must not be dropped until the application scope is dropped for accurate tracing.
    _guard: FlushGuard,
}

pub fn init() -> Profiler {
    // Registry stores the spans & generates unique span IDs
    let subscriber = Registry::default();

    #[cfg(feature = "tracing-chrome")]
    let (chrome_layer, guard) = {
        let mut layer = tracing_chrome::ChromeLayerBuilder::new();

        // Optional configurable env var: CHROME_TRACE_FILE=/path/to/trace_file/file.json,
        // for uploading to chrome://tracing (old) or ui.perfetto.dev (new).
        if let Ok(path) = std::env::var("CHROME_TRACE_FILE") {
            layer = layer.file(path);
        } else if let Ok(current_dir) = std::env::current_dir() {
            let time = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap_or(Duration::from_millis(0))
                .as_millis();

            let trace_file_name = current_dir
                .file_name()
                .map(|file_dir| {
                    format!(
                        "{}_trace_{}.json",
                        file_dir.to_str().unwrap_or("trace"),
                        time
                    )
                })
                .unwrap_or_else(|| "trace.json".to_string());

            let path = format!(
                "{}/{}",
                current_dir.to_str().expect("Invalid path"),
                trace_file_name
            );

            layer = layer.file(path);
        } else {
            layer = layer.file(env!("CARGO_MANIFEST_DIR"))
        }

        let (chrome_layer, guard) = layer
            .name_fn(Box::new(|event_or_span| match event_or_span {
                tracing_chrome::EventOrSpan::Event(event) => {
                    event.metadata().name().into()
                }
                tracing_chrome::EventOrSpan::Span(span) => {
                    if let Some(fields) = span
                        .extensions()
                        .get::<FormattedFields<DefaultFields>>()
                    {
                        format!(
                            "{}: {}",
                            span.metadata().name(),
                            fields.fields.as_str()
                        )
                    } else {
                        span.metadata().name().into()
                    }
                }
            }))
            .build();

        (chrome_layer, guard)
    };

    let fmt_layer = tracing_subscriber::fmt::Layer::default();
    let subscriber = subscriber.with(fmt_layer);

    #[cfg(feature = "tracing-chrome")]
    let subscriber = subscriber.with(chrome_layer);

    // create dispatcher which will forward span events to the subscriber
    // this can only be set once or will panic
    tracing::subscriber::set_global_default(subscriber)
        .expect("Profiler could not set the global default subscriber.");

    Profiler {
        #[cfg(feature = "tracing-chrome")]
        _guard: guard,
    }
}