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}