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 tracing::Dispatch::new(FakeSubscriber {
93 level: self.tracing_level,
94 });
95 let _ = tracing::dispatcher::set_global_default(self.dispatch.clone());
97
98 #[cfg(feature = "log")]
99 {
100 let _ = log::set_logger(self.logger);
102 log::set_max_level(self.level);
103 }
104 }
105
106 #[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#[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}