1extern crate log;
2
3use log::Log;
4use log::Metadata;
5use log::Record;
6use std::cell::RefCell;
7use std::mem;
8use std::rc::Rc;
9use std::sync::Arc;
10
11pub struct Logger {
12 underlying: Box<dyn Log>,
13}
14
15impl Logger {
16 pub fn new(underlying: Box<dyn Log>) -> Logger {
17 Logger { underlying }
18 }
19}
20
21impl Log for Logger {
22 fn enabled(&self, metadata: &Metadata) -> bool {
23 self.underlying.enabled(metadata)
24 }
25
26 fn log(&self, record: &Record) {
27 get(|ndc| {
28 if ndc.is_empty() {
29 self.underlying.log(record)
30 } else {
31 self.underlying.log(
32 &Record::builder()
33 .metadata(record.metadata().clone())
34 .args(format_args!("[{}] {}", ndc, record.args()))
35 .module_path(record.module_path())
36 .file(record.file())
37 .line(record.line())
38 .build(),
39 );
40 }
41 })
42 }
43
44 fn flush(&self) {
45 self.underlying.flush()
46 }
47}
48
49pub fn set_boxed_logger(logger: Box<dyn Log>) -> Result<(), log::SetLoggerError> {
50 log::set_boxed_logger(Box::new(Logger::new(logger)))
51}
52
53#[derive(Debug, Clone)]
55pub enum Ndc {
56 Str(&'static str),
57 String(String),
58 Rc(Rc<String>),
59 Arc(Arc<String>),
60}
61
62impl Default for Ndc {
63 fn default() -> Self {
64 Ndc::Str("")
65 }
66}
67
68impl Ndc {
69 fn to_str(&self) -> &str {
71 match self {
72 Ndc::Str(s) => s,
73 Ndc::String(s) => &s,
74 Ndc::Rc(s) => &**s,
75 Ndc::Arc(s) => &**s,
76 }
77 }
78}
79
80impl From<&'static str> for Ndc {
81 fn from(s: &'static str) -> Self {
82 Ndc::Str(s)
83 }
84}
85
86impl From<String> for Ndc {
87 fn from(s: String) -> Self {
88 Ndc::String(s)
89 }
90}
91
92impl From<Rc<String>> for Ndc {
93 fn from(s: Rc<String>) -> Self {
94 Ndc::Rc(s)
95 }
96}
97
98impl From<Arc<String>> for Ndc {
99 fn from(s: Arc<String>) -> Self {
100 Ndc::Arc(s)
101 }
102}
103
104thread_local! {
105 static NDC: RefCell<Ndc> = RefCell::new(Ndc::Str(""));
106}
107
108#[must_use]
109pub struct SetGuard {
110 ndc: Ndc,
111}
112
113impl Drop for SetGuard {
114 fn drop(&mut self) {
115 set(mem::replace(&mut self.ndc, Default::default()));
116 }
117}
118
119pub fn set<S: Into<Ndc>>(ndc: S) {
121 replace(ndc);
122}
123
124pub fn replace<S: Into<Ndc>>(ndc: S) -> Ndc {
126 NDC.with(|r| mem::replace(&mut *r.borrow_mut(), ndc.into()))
127}
128
129pub fn clear() {
131 set("");
132}
133
134pub fn push<S: Into<Ndc>>(ndc: S) -> SetGuard {
136 SetGuard { ndc: replace(ndc) }
137}
138
139pub fn get_raw() -> Ndc {
142 NDC.with(|r| r.borrow().clone())
143}
144
145pub fn get_copy() -> String {
148 get(|s| s.to_owned())
149}
150
151pub fn get<R, F>(f: F) -> R
153where
154 F: FnOnce(&str) -> R,
155{
156 NDC.with(|r| f(r.borrow().to_str()))
157}
158
159#[cfg(test)]
160mod test {
161 mod log_ndc {
162 pub use super::super::*;
163 }
164
165 #[test]
166 fn set_get() {
167 let _g = log_ndc::push("");
168
169 log_ndc::set("aa");
170 assert_eq!("aa", log_ndc::get_copy());
171 }
172
173 #[test]
174 fn push() {
175 let _g = log_ndc::push("");
176
177 let _g1 = log_ndc::push("aa");
178 assert_eq!("aa", log_ndc::get_copy());
179
180 let g2 = log_ndc::push("bb");
181 assert_eq!("bb", log_ndc::get_copy());
182 drop(g2);
183
184 assert_eq!("aa", log_ndc::get_copy());
185 }
186}