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 the spans export via `opentelemetry-zipkin`
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![])
    }
}