captains_log/
log_filter.rs

1use std::{
2    fmt, str,
3    sync::atomic::{AtomicUsize, Ordering},
4};
5
6use log::{kv::*, *};
7
8/// A LogFilter supports concurrent control the log level.
9/// Use in combine with macros logger_XXX
10///
11/// # Example
12/// ```
13/// use captains_log::*;
14/// let logger = LogFilter::new();
15/// logger.set_level(log::Level::Error);
16/// // info will be filtered
17/// logger_info!(logger, "using LogFilter {}", "ok");
18/// logger_error!(logger, "error occur");
19/// ```
20pub struct LogFilter {
21    max_level: AtomicUsize,
22}
23
24impl Clone for LogFilter {
25    fn clone(&self) -> Self {
26        Self { max_level: AtomicUsize::new(self.get_level()) }
27    }
28}
29
30impl LogFilter {
31    pub fn new() -> Self {
32        Self { max_level: AtomicUsize::new(Level::Trace as usize) }
33    }
34
35    /// When LogFilter is shared in Arc, allows concurrently changing log level filter
36    #[inline]
37    pub fn set_level(&self, level: Level) {
38        self.max_level.store(level as usize, Ordering::Relaxed);
39    }
40
41    #[inline]
42    pub fn get_level(&self) -> usize {
43        self.max_level.load(Ordering::Relaxed)
44    }
45
46    /// for macros logger_XXX
47    #[doc(hidden)]
48    #[inline(always)]
49    pub fn _private_api_log(
50        &self, args: fmt::Arguments, level: Level,
51        &(target, module_path, file, line): &(&str, &str, &str, u32),
52    ) {
53        let record = RecordBuilder::new()
54            .level(level)
55            .target(target)
56            .module_path(Some(module_path))
57            .file(Some(file))
58            .line(Some(line))
59            .args(args)
60            .build();
61        logger().log(&record);
62    }
63}
64
65impl log::kv::Source for LogFilter {
66    #[inline(always)]
67    fn visit<'kvs>(&'kvs self, _visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> {
68        Ok(())
69    }
70
71    #[inline(always)]
72    fn get<'a>(&'a self, _key: Key) -> Option<Value<'a>> {
73        return None;
74    }
75
76    #[inline(always)]
77    fn count(&self) -> usize {
78        0
79    }
80}
81
82/// LogFilter that carries one additional value into log format
83///
84/// Example with a key as "req_id":
85/// ``` rust
86/// use captains_log::*;
87/// fn debug_format_req_id_f(r: FormatRecord) -> String {
88///     let time = r.time();
89///     let level = r.level();
90///     let file = r.file();
91///     let line = r.line();
92///     let msg = r.msg();
93///     let req_id = r.key("req_id");
94///     format!("[{time}][{level}][{file}:{line}] {msg}{req_id}\n").to_string()
95/// }
96/// let mut builder = recipe::raw_file_logger_custom("/tmp", "log_filter", log::Level::Debug,
97///     recipe::DEFAULT_TIME, debug_format_req_id_f);
98/// builder.dynamic = true;
99///
100/// builder.build().expect("setup_log");
101/// let logger = LogFilterKV::new("req_id", format!("{:016x}", 123).to_string());
102/// logger_debug!(logger, "captain's log");
103/// ```
104
105#[derive(Clone)]
106pub struct LogFilterKV {
107    inner: LogFilter,
108    key: &'static str,
109    value: String,
110}
111
112impl LogFilterKV {
113    pub fn new(key: &'static str, value: String) -> Self {
114        Self { inner: LogFilter::new(), key, value }
115    }
116
117    /// When LogFilter is shared in Arc, allows concurrently changing log level filter
118    #[inline]
119    pub fn set_level(&self, level: Level) {
120        self.inner.set_level(level)
121    }
122
123    #[inline]
124    pub fn get_level(&self) -> usize {
125        self.inner.get_level()
126    }
127
128    /// for macros logger_XXX
129    #[doc(hidden)]
130    #[inline(always)]
131    pub fn _private_api_log(
132        &self, args: fmt::Arguments, level: Level,
133        &(target, module_path, file, line): &(&str, &str, &str, u32),
134    ) {
135        let record = RecordBuilder::new()
136            .level(level)
137            .target(target)
138            .module_path(Some(module_path))
139            .file(Some(file))
140            .line(Some(line))
141            .key_values(&self)
142            .args(args)
143            .build();
144        logger().log(&record);
145    }
146}
147
148impl log::kv::Source for LogFilterKV {
149    #[inline(always)]
150    fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> {
151        visitor.visit_pair(self.key.to_key(), self.value.as_str().into())
152    }
153
154    #[inline(always)]
155    fn get<'a>(&'a self, key: Key) -> Option<Value<'a>> {
156        if key.as_ref() == self.key {
157            return Some(self.value.as_str().into());
158        }
159        return None;
160    }
161
162    #[inline(always)]
163    fn count(&self) -> usize {
164        1
165    }
166}