tracing_shared/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use crate::helper::FakeSubscriber;
4#[cfg(feature = "tokio")]
5pub use crate::tokio::TokioEnterGuard;
6use std::fmt::{Debug, Formatter};
7use tracing::subscriber::{set_global_default, NoSubscriber};
8
9mod helper;
10#[cfg(feature = "tokio")]
11mod tokio;
12fn get_data() -> u64 {
13    set_global_default::<NoSubscriber> as *const () as u64
14}
15pub const FEATURE_LOG: u16 = (cfg!(feature = "log") as u16) << 0;
16pub const FEATURE_TOKIO: u16 = (cfg!(feature = "tokio") as u16) << 1;
17fn get_features() -> u16 {
18    let mut feature = 0;
19    feature |= FEATURE_LOG;
20    feature |= FEATURE_TOKIO;
21    feature
22}
23fn check_features(features: u16) {
24    let log_enabled = features & (1 << 0);
25    if log_enabled ^ FEATURE_LOG != 0 {
26        panic!(
27            "`feature = log` mismatch: executable = {}, dylib = {}",
28            log_enabled != 0,
29            FEATURE_LOG != 0
30        );
31    }
32    let tokio_enabled = features & (1 << 1);
33    if tokio_enabled ^ FEATURE_TOKIO != 0 {
34        panic!(
35            "`feature = tokio` mismatch: executable = {}, dylib = {}",
36            tokio_enabled != 0,
37            FEATURE_TOKIO != 0
38        );
39    }
40}
41#[derive(Clone)]
42#[repr(C)]
43pub struct SharedLogger {
44    data: u64,
45    features: u16,
46    dispatch: tracing::Dispatch,
47    tracing_level: tracing::level_filters::LevelFilter,
48    #[cfg(feature = "log")]
49    logger: &'static dyn log::Log,
50    #[cfg(feature = "log")]
51    level: log::LevelFilter,
52    #[cfg(feature = "tokio")]
53    handle: Option<::tokio::runtime::Handle>,
54}
55impl Debug for SharedLogger {
56    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57        let mut dbg = f.debug_struct("SharedLogger");
58
59        dbg.field("data", &format_args!("{:p}", self.data as *const ()))
60            .field("features", &self.features)
61            .field("tracing_dispatch", &self.dispatch)
62            .field("tracing_level", &self.tracing_level);
63        #[cfg(feature = "log")]
64        dbg.field("log_logger", &format_args!("{:p}", self.logger))
65            .field("log_level", &self.level);
66        dbg.finish()
67    }
68}
69impl SharedLogger {
70    pub fn new() -> Self {
71        SharedLogger {
72            data: get_data(),
73            features: get_features(),
74            dispatch: tracing::dispatcher::get_default(|dispatch| dispatch.clone()),
75            tracing_level: tracing::level_filters::LevelFilter::current(),
76            #[cfg(feature = "log")]
77            logger: log::logger(),
78            #[cfg(feature = "log")]
79            level: log::max_level(),
80            #[cfg(feature = "tokio")]
81            handle: ::tokio::runtime::Handle::try_current().ok(),
82        }
83    }
84    #[inline(never)]
85    pub fn install(&self) {
86        if std::ptr::addr_eq(&self.data, get_data() as *const ()) {
87            panic!("SharedLogger can only be installed in dynamically linked modules, don't call it here");
88        }
89        check_features(self.features);
90        // set tracing level
91        // see FakeSubscriber's doc
92        tracing::Dispatch::new(FakeSubscriber {
93            level: self.tracing_level,
94        });
95        // it fails for dylib, but it's not necessary to setup for tracing
96        let _ = tracing::dispatcher::set_global_default(self.dispatch.clone());
97
98        #[cfg(feature = "log")]
99        {
100            // it fails for dylib, but it's not necessary to setup for tracing
101            let _ = log::set_logger(self.logger);
102            log::set_max_level(self.level);
103        }
104    }
105
106    /// the tokio function is unstable
107    /// may be removed soon for sake of another crate
108    #[cfg(feature = "tokio")]
109    pub fn install_tokio(&self) -> Option<TokioEnterGuard> {
110        match &self.handle {
111            Some(handle) => Some(TokioEnterGuard::new(handle.enter())),
112            None => None,
113        }
114    }
115}
116#[deprecated = "use SharedLogger::new() instead"]
117pub fn build_shared_logger() -> SharedLogger {
118    SharedLogger::new()
119}
120
121#[deprecated = "use setup_shared_logger_ref(&logger) instead. this is to prevent FFI boundary error"]
122pub fn setup_shared_logger(logger: SharedLogger) {
123    logger.install()
124}
125/// helper function to install the SharedLogger
126/// public re-export to make it globally accessible
127/// Usage:
128/// ```rust,no_run
129///   pub use tracing_shared::setup_shared_logger_ref;
130///   use tracing_shared::SharedLogger;
131///   let dylib = "library.so";
132///   let dylib = unsafe { libloading::Library::new(dylib) }.expect("error loading dylib");
133///   let setup_logger: extern "Rust" fn(&SharedLogger) =
134///         unsafe { *dylib.get(b"setup_shared_logger_ref").unwrap() };
135/// ```
136#[no_mangle]
137#[inline(never)]
138pub fn setup_shared_logger_ref(logger: &SharedLogger) {
139    logger.install()
140}
141
142#[cfg(feature = "tokio")]
143#[no_mangle]
144#[inline(never)]
145pub fn setup_shared_tokio_ref(logger: &SharedLogger) -> Option<TokioEnterGuard> {
146    logger.install_tokio()
147}