prima_tracing/
subscriber.rs

1use tracing::{Event, Subscriber};
2use tracing_log::LogTracer;
3use tracing_subscriber::{
4    layer::{Context, SubscriberExt},
5    EnvFilter,
6};
7
8use crate::config::SubscriberConfig;
9
10pub struct Tracing;
11
12impl Tracing {}
13
14/// Configure a subscriber using [`SubscriberConfig`].
15/// The configuration is behind feature flags
16/// - `default`: uses the [`tracing_subscriber::fmt::layer()`]
17/// - `json-logger`: activate the json logger
18/// - `traces`: activate spans export via `opentelemetry-otlp`
19pub fn configure_subscriber<T: EventFormatter + Send + Sync + 'static>(
20    _config: SubscriberConfig<T>,
21) -> impl Subscriber + Send + Sync {
22    let subscriber = tracing_subscriber::Registry::default();
23    let subscriber = subscriber.with(EnvFilter::from_default_env());
24
25    #[cfg(feature = "traces")]
26    let subscriber = {
27        let tracer = crate::telemetry::configure(&_config);
28        subscriber
29            .with(tracing_opentelemetry::layer().with_tracer(tracer))
30            .with(crate::layer::ErrorLayer)
31    };
32
33    #[cfg(not(feature = "json-logger"))]
34    let subscriber = subscriber.with(tracing_subscriber::fmt::layer());
35    #[cfg(feature = "json-logger")]
36    let subscriber = {
37        use crate::json::formatter::PrimaFormattingLayer;
38        use crate::json::storage::PrimaJsonStorage;
39        subscriber
40            .with(PrimaJsonStorage)
41            .with(PrimaFormattingLayer::new(
42                _config.service.clone(),
43                _config.country.to_string(),
44                _config.env.to_string(),
45                &std::io::stdout,
46                _config.json_formatter,
47            ))
48    };
49
50    subscriber
51}
52/// Initialize the subscriber and return the [`Uninstall`] guard
53pub fn init_subscriber(subscriber: impl Subscriber + Sync + Send) -> Uninstall {
54    LogTracer::init().expect("Failed to set logger");
55    tracing::subscriber::set_global_default(subscriber).expect("Setting default subscriber failed");
56
57    #[cfg(feature = "traces")]
58    {
59        use opentelemetry::global;
60        use opentelemetry_sdk::propagation::TraceContextPropagator;
61        global::set_text_map_propagator(TraceContextPropagator::new());
62    };
63    Uninstall
64}
65/// `EventFormatter` allows you to customise the format of [`tracing::Event`] if the `json-logger` feature is active
66pub trait EventFormatter {
67    fn format_event<S>(
68        &self,
69        event: &Event<'_>,
70        ctx: Context<'_, S>,
71        info: ContextInfo<'_>,
72    ) -> Result<Vec<u8>, std::io::Error>
73    where
74        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>;
75}
76/// Uninstall guard for doing works in shutdown
77#[must_use = "Uninstall guard shuts down the trace provider when dropped. By not using it/assigning it to a variable the tracing provider will be immediately removed."]
78pub struct Uninstall;
79
80impl Drop for Uninstall {
81    fn drop(&mut self) {
82        #[cfg(feature = "traces")]
83        crate::telemetry::shutdown_tracer_provider();
84    }
85}
86/// Information about the current app context like name or environment
87pub struct ContextInfo<'a> {
88    pub(crate) app_name: &'a str,
89    pub(crate) country: &'a str,
90    pub(crate) environment: &'a str,
91}
92
93impl<'a> ContextInfo<'a> {
94    pub fn app_name(&self) -> &'a str {
95        self.app_name
96    }
97
98    pub fn country(&self) -> &'a str {
99        self.country
100    }
101
102    pub fn environment(&self) -> &'a str {
103        self.environment
104    }
105}
106
107pub struct NopEventFormatter;
108
109impl EventFormatter for NopEventFormatter {
110    fn format_event<S>(
111        &self,
112        _event: &Event<'_>,
113        _ctx: Context<'_, S>,
114        _info: ContextInfo<'_>,
115    ) -> Result<Vec<u8>, std::io::Error>
116    where
117        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
118    {
119        Ok(vec![])
120    }
121}