tracing_shared/
lib.rs

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