android_log/
lib.rs

1//! A logger which writes to the Android logging subsystem. It must be compiled
2//! with the Android NDK in order to link to `liblog`.
3//!
4//! ## Example
5//!
6//! ```
7//! #[macro_use] extern crate log;
8//! extern crate android_log;
9//!
10//! fn main() {
11//!     android_log::init("MyApp").unwrap();
12//!
13//!     trace!("Initialized Rust");
14//!     debug!("Address is {:p}", main as *const ());
15//!     info!("Did you know? {} = {}", "1 + 1", 2);
16//!     warn!("Don't log sensitive information!");
17//!     error!("Nothing more to say");
18//! }
19//! ```
20//!
21//! ```{.bash}
22//! $ logcat
23//! 12-25 12:00:00.000  1234  1234 V MyApp: Initialized Rust
24//! 12-25 12:00:00.000  1234  1234 D MyApp: Address is 0xdeadbeef
25//! 12-25 12:00:00.000  1234  1234 I MyApp: Did you know? 1 + 1 = 2
26//! 12-25 12:00:00.000  1234  1234 W MyApp: Don't log sensitive information!
27//! 12-25 12:00:00.000  1234  1234 E MyApp: Nothing more to say
28
29extern crate log;
30extern crate android_liblog_sys;
31
32use std::ffi::CString;
33
34use log::{Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
35use android_liblog_sys::{__android_log_write, LogPriority};
36
37/// `AndroidLogger` is the implementation of the logger.
38///
39/// It should not be used from Rust libraries which should only use the facade.
40pub struct AndroidLogger {
41    tag: CString,
42    format: Box<Fn(&LogRecord) -> String + Sync + Send>,
43}
44
45/// `LogBuilder` acts as builder for initializing the `AndroidLogger`. It can be
46/// used to customize the log format.
47///
48/// ## Example
49///
50/// ```
51/// #[macro_use] extern crate log;
52/// extern crate android_log;
53///
54/// use log::{LogRecord, LogLevelFilter};
55/// use android_log::LogBuilder;
56///
57/// fn main() {
58///     let format = |record: &LogRecord| {
59///         format!("{} - {}", record.target(), record.args())
60///     };
61///
62///     let mut builder = LogBuilder::new();
63///     builder.format(format);
64///
65///     builder.init().unwrap();
66///
67/// 	warn!("Warning message");
68///     error!("Error message");
69/// }
70/// ```
71pub struct LogBuilder {
72    tag: CString,
73    format: Box<Fn(&LogRecord) -> String + Sync + Send>,
74}
75
76/// Initializes the global logger with an `AndroidLogger`
77///
78/// This should be called early in the execution of a Rust program and the
79/// global logger may only be initialized once. Future attempts will return an
80/// error.
81pub fn init<S: Into<String>>(tag: S) -> Result<(), SetLoggerError> {
82    AndroidLogger::new(tag).init()
83}
84
85impl AndroidLogger {
86    /// Initializes the logger with defaults
87    pub fn new<S: Into<String>>(tag: S) -> AndroidLogger {
88        LogBuilder::new(tag).build()
89    }
90
91    /// Initializes the global logger with `self`
92    pub fn init(self) -> Result<(), SetLoggerError> {
93        log::set_logger(|max_level| {
94            max_level.set(LogLevelFilter::max());
95            Box::new(self)
96        })
97    }
98}
99
100impl Log for AndroidLogger {
101    fn enabled(&self, _: &LogMetadata) -> bool {
102        true
103    }
104
105    fn log(&self, record: &LogRecord) {
106        if !Log::enabled(self, record.metadata()) {
107            return;
108        }
109
110        let format = CString::new((self.format)(record)).unwrap();
111
112        let prio = match record.level() {
113            LogLevel::Error => LogPriority::ERROR,
114            LogLevel::Warn => LogPriority::WARN,
115            LogLevel::Info => LogPriority::INFO,
116            LogLevel::Debug => LogPriority::DEBUG,
117            LogLevel::Trace => LogPriority::VERBOSE,
118        };
119
120        unsafe {
121            __android_log_write(prio as _, self.tag.as_ptr(), format.as_ptr());
122        }
123    }
124}
125
126impl LogBuilder {
127    /// Initializes the builder with defaults
128    pub fn new<S: Into<String>>(tag: S) -> LogBuilder {
129        LogBuilder {
130            tag: CString::new(tag.into()).unwrap(),
131            format: Box::new(|record: &LogRecord| {
132                format!("{}: {}", record.location().module_path(), record.args())
133            }),
134        }
135    }
136
137    /// Sets the format function for formatting the log output
138    pub fn format<F: 'static>(&mut self, format: F) -> &mut Self
139        where F: Fn(&LogRecord) -> String + Sync + Send
140    {
141        self.format = Box::new(format);
142        self
143    }
144
145    /// Builds an `AndroidLogger` and initializes the global logger
146    pub fn init(self) -> Result<(), SetLoggerError> {
147        self.build().init()
148    }
149
150    /// Builds an `AndroidLogger`
151    pub fn build(self) -> AndroidLogger {
152        AndroidLogger {
153            tag: self.tag,
154            format: self.format,
155        }
156    }
157}