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
77pub struct Uninstall;
78
79impl Drop for Uninstall {
80    fn drop(&mut self) {
81        #[cfg(feature = "traces")]
82        crate::telemetry::shutdown_tracer_provider();
83    }
84}
85/// Information about the current app context like name or environment
86pub struct ContextInfo<'a> {
87    pub(crate) app_name: &'a str,
88    pub(crate) country: &'a str,
89    pub(crate) environment: &'a str,
90}
91
92impl<'a> ContextInfo<'a> {
93    pub fn app_name(&self) -> &'a str {
94        self.app_name
95    }
96
97    pub fn country(&self) -> &'a str {
98        self.country
99    }
100
101    pub fn environment(&self) -> &'a str {
102        self.environment
103    }
104}
105
106pub struct NopEventFormatter;
107
108impl EventFormatter for NopEventFormatter {
109    fn format_event<S>(
110        &self,
111        _event: &Event<'_>,
112        _ctx: Context<'_, S>,
113        _info: ContextInfo<'_>,
114    ) -> Result<Vec<u8>, std::io::Error>
115    where
116        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
117    {
118        Ok(vec![])
119    }
120}