android_logcat/
android_log.rs

1use std::{
2    ffi::CString,
3    sync::{
4        atomic::{AtomicBool, Ordering},
5        OnceLock,
6    },
7};
8
9use crate::sys::{LogPriority, __android_log_print, __android_log_write};
10
11/// Default tag used when no tag is specified
12static DEFAULT_TAG: &str = "RustLog";
13
14/// Global static instance of Log tag
15static GLOBAL_LOG_TAG: OnceLock<String> = OnceLock::new();
16
17/// Global static instance to enable or disable logging
18static GLOBAL_LOG_ENABLED: AtomicBool = AtomicBool::new(true);
19
20/// A struct for Android logging
21pub struct Log;
22
23impl Log {
24    /// Print formatted log message
25    pub fn print(prio: LogPriority, tag: &str, msg: &str) {
26        if !Self::is_enabled() {
27            return;
28        }
29
30        unsafe {
31            __android_log_print(
32                prio as i32,
33                CString::new(tag).unwrap().as_ptr(),
34                CString::new("%s").unwrap().as_ptr(),
35                CString::new(msg).unwrap().as_ptr(),
36            );
37        }
38    }
39
40    /// Write simple log message
41    pub fn write<T>(prio: LogPriority, tag: &str, msg: &str) {
42        if !Self::is_enabled() {
43            return;
44        }
45
46        unsafe {
47            __android_log_write(
48                prio as i32,
49                CString::new(tag).unwrap().as_ptr(),
50                CString::new(msg).unwrap().as_ptr(),
51            );
52        }
53    }
54
55    /// Log verbose message
56    /// # Arguments
57    /// * `tag` - The tag of the log message
58    /// * `msg` - The log message
59    pub fn verbose<T>(tag: T, msg: T)
60    where
61        T: AsRef<str>,
62    {
63        Self::print(LogPriority::VERBOSE, tag.as_ref(), msg.as_ref());
64    }
65
66    /// Log debug message
67    /// # Arguments
68    /// * `tag` - The tag of the log message
69    /// * `msg` - The log message
70    pub fn debug<T>(tag: T, msg: T)
71    where
72        T: AsRef<str>,
73    {
74        Self::print(LogPriority::DEBUG, tag.as_ref(), msg.as_ref());
75    }
76
77    /// Log debug message
78    /// # Arguments
79    /// * `tag` - The tag of the log message
80    /// * `msg` - The log message
81    pub fn info<T>(tag: T, msg: T)
82    where
83        T: AsRef<str>,
84    {
85        Self::print(LogPriority::INFO, tag.as_ref(), msg.as_ref());
86    }
87
88    /// Log warn message
89    /// # Arguments
90    /// * `tag` - The tag of the log message
91    /// * `msg` - The log message
92    pub fn warn<T>(tag: T, msg: T)
93    where
94        T: AsRef<str>,
95    {
96        Self::print(LogPriority::WARN, tag.as_ref(), msg.as_ref());
97    }
98
99    /// Log error message
100    /// # Arguments
101    /// * `tag` - The tag of the log message
102    /// * `msg` - The log message
103    pub fn error<T>(tag: T, msg: T)
104    where
105        T: AsRef<str>,
106    {
107        Self::print(LogPriority::ERROR, tag.as_ref(), msg.as_ref());
108    }
109
110    /// Log verbose message using the global tag
111    /// If the global Log instance is not initialized, use the default tag [DEFAULT_TAG]
112    /// # Arguments
113    /// * `msg` - The log message
114    pub fn v<T>(msg: T)
115    where
116        T: AsRef<str>,
117    {
118        if let Some(tag) = GLOBAL_LOG_TAG.get() {
119            Self::verbose(tag.as_ref(), msg.as_ref());
120        } else {
121            Self::verbose(DEFAULT_TAG, msg.as_ref());
122        }
123    }
124
125    /// Log debug message using the global tag
126    /// If the global Log instance is not initialized, use the default tag [DEFAULT_TAG]
127    /// # Arguments
128    /// * `msg` - The log message
129    pub fn d<T>(msg: T)
130    where
131        T: AsRef<str>,
132    {
133        if let Some(tag) = GLOBAL_LOG_TAG.get() {
134            Self::debug(tag.as_ref(), msg.as_ref());
135        } else {
136            Self::debug(DEFAULT_TAG, msg.as_ref());
137        }
138    }
139
140    /// Log info message using the global tag
141    /// If the global Log instance is not initialized, use the default tag [DEFAULT_TAG]
142    /// # Arguments
143    /// * `msg` - The log message
144    pub fn i<T>(msg: T)
145    where
146        T: AsRef<str>,
147    {
148        if let Some(tag) = GLOBAL_LOG_TAG.get() {
149            Self::info(tag.as_ref(), msg.as_ref());
150        } else {
151            Self::info(DEFAULT_TAG, msg.as_ref());
152        }
153    }
154
155    /// Log warn message using the global tag
156    /// If the global Log instance is not initialized, use the default tag [DEFAULT_TAG]
157    /// # Arguments
158    /// * `msg` - The log message
159    pub fn w<T>(msg: T)
160    where
161        T: AsRef<str>,
162    {
163        if let Some(tag) = GLOBAL_LOG_TAG.get() {
164            Self::warn(tag.as_ref(), msg.as_ref());
165        } else {
166            Self::warn(DEFAULT_TAG, msg.as_ref());
167        }
168    }
169
170    /// Log error message using the global tag
171    /// If the global Log instance is not initialized, use the default tag [DEFAULT_TAG]
172    /// # Arguments
173    /// * `msg` - The log message
174    pub fn e<T>(msg: T)
175    where
176        T: AsRef<str>,
177    {
178        if let Some(tag) = GLOBAL_LOG_TAG.get() {
179            Self::error(tag.as_ref(), msg.as_ref());
180        } else {
181            Self::error(DEFAULT_TAG, msg.as_ref());
182        }
183    }
184
185    /// Initialize the global Log instance with a specific tag
186    /// # Arguments
187    /// * `tag` - The tag to be used for the global Log instance
188    /// * `mixinlog` - If true, set the Rust log crate to use this Log instance as the logger. e.g. log::info!("message")
189    pub fn init(tag: &str, mixinlog: bool) {
190        let _tag = GLOBAL_LOG_TAG.get_or_init(|| tag.to_string());
191        let _enable = GLOBAL_LOG_ENABLED.store(true, Ordering::Relaxed);
192
193        if mixinlog {
194            log::set_max_level(log::LevelFilter::max());
195            log::set_logger(&Self).unwrap();
196        }
197    }
198
199    /// Get the tag of the Log instance
200    pub fn tag() -> &'static str {
201        if let Some(tag) = GLOBAL_LOG_TAG.get() {
202            tag.as_ref()
203        } else {
204            DEFAULT_TAG
205        }
206    }
207
208    /// Enable or disable logging
209    /// # Arguments
210    /// * `enabled` - If true, enable logging; if false, disable logging
211    pub fn enabled(enabled: bool) {
212        GLOBAL_LOG_ENABLED.store(enabled, Ordering::Relaxed);
213    }
214
215    /// Check if logging is enabled
216    pub fn is_enabled() -> bool {
217        GLOBAL_LOG_ENABLED.load(Ordering::Relaxed)
218    }
219}