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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use tracing::{Event, Subscriber};
use tracing_log::LogTracer;
use tracing_subscriber::{
    layer::{Context, SubscriberExt},
    EnvFilter,
};

use crate::config::SubscriberConfig;

pub struct Tracing;

impl Tracing {}

/// Configure a subscriber using [`SubscriberConfig`].
/// The configuration is behind feature flags
/// - `default`: uses the [`tracing_subscriber::fmt::layer()`]
/// - `prima-logger-json`: activate the json logger
/// - `prima-telemetry`: activate spans export via `opentelemetry-otlp`
pub fn configure_subscriber<T: EventFormatter + Send + Sync + 'static>(
    _config: SubscriberConfig<T>,
) -> impl Subscriber + Send + Sync {
    let subscriber = tracing_subscriber::Registry::default();
    let subscriber = subscriber.with(EnvFilter::from_default_env());

    #[cfg(feature = "prima-telemetry")]
    let subscriber = {
        let tracer = crate::telemetry::configure(&_config);
        subscriber
            .with(tracing_opentelemetry::layer().with_tracer(tracer))
            .with(crate::telemetry::VersionLayer {
                version: _config.version.clone(),
            })
    };

    #[cfg(not(feature = "prima-logger-json"))]
    let subscriber = subscriber.with(tracing_subscriber::fmt::layer());
    #[cfg(feature = "prima-logger-json")]
    let subscriber = {
        use crate::json::formatter::PrimaFormattingLayer;
        use crate::json::storage::PrimaJsonStorage;
        subscriber
            .with(PrimaJsonStorage::default())
            .with(PrimaFormattingLayer::new(
                _config.service.clone(),
                _config.env.clone(),
                &std::io::stdout,
                _config.json_formatter,
            ))
    };

    subscriber
}
/// Initialize the subscriber and return the [`Uninstall`] guard
pub fn init_subscriber(subscriber: impl Subscriber + Sync + Send) -> Uninstall {
    LogTracer::init().expect("Failed to set logger");
    tracing::subscriber::set_global_default(subscriber).expect("Setting default subscriber failed");

    #[cfg(feature = "prima-telemetry")]
    let _ = {
        use opentelemetry::{global, sdk::propagation::TraceContextPropagator};
        global::set_text_map_propagator(TraceContextPropagator::new());
    };
    Uninstall
}
/// `EventFormatter` allows you to customise the format of [`tracing::Event`] if the `prima-json-logger` feature is active
pub trait EventFormatter {
    fn format_event<S>(
        &self,
        event: &Event<'_>,
        ctx: Context<'_, S>,
        info: ContextInfo<'_>,
    ) -> Result<Vec<u8>, std::io::Error>
    where
        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>;
}
/// Uninstall guard for doing works in shutdown
pub struct Uninstall;

impl Drop for Uninstall {
    fn drop(&mut self) {
        #[cfg(feature = "prima-telemetry")]
        opentelemetry::global::shutdown_tracer_provider();
    }
}
/// Information about the current app context like name or environment
pub struct ContextInfo<'a> {
    pub(crate) app_name: &'a str,
    pub(crate) environment: &'a str,
}

impl<'a> ContextInfo<'a> {
    pub fn app_name(&self) -> &'a str {
        self.app_name
    }

    pub fn environment(&self) -> &'a str {
        self.environment
    }
}

pub struct NopEventFormatter {}

impl EventFormatter for NopEventFormatter {
    fn format_event<S>(
        &self,
        _event: &Event<'_>,
        _ctx: Context<'_, S>,
        _info: ContextInfo<'_>,
    ) -> Result<Vec<u8>, std::io::Error>
    where
        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
    {
        Ok(vec![])
    }
}