1 // Copyright 2016 The android_logger Developers
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7
8 //! A logger which writes to android output.
9 //!
10 //! ## Example
11 //!
12 //! ```
13 //! #[macro_use] extern crate log;
14 //! extern crate android_logger;
15 //!
16 //! use log::Level;
17 //! use android_logger::Config;
18 //!
19 //! /// Android code may not have obvious "main", this is just an example.
20 //! fn main() {
21 //! android_logger::init_once(
22 //! Config::default().with_min_level(Level::Trace),
23 //! );
24 //!
25 //! debug!("this is a debug {}", "message");
26 //! error!("this is printed by default");
27 //! }
28 //! ```
29 //!
30 //! ## Example with module path filter
31 //!
32 //! It is possible to limit log messages to output from a specific crate,
33 //! and override the logcat tag name (by default, the crate name is used):
34 //!
35 //! ```
36 //! #[macro_use] extern crate log;
37 //! extern crate android_logger;
38 //!
39 //! use log::Level;
40 //! use android_logger::{Config,FilterBuilder};
41 //!
42 //! fn main() {
43 //! android_logger::init_once(
44 //! Config::default()
45 //! .with_min_level(Level::Trace)
46 //! .with_tag("mytag")
47 //! .with_filter(FilterBuilder::new().parse("debug,hello::crate=trace").build()),
48 //! );
49 //!
50 //! // ..
51 //! }
52 //! ```
53 //!
54 //! ## Example with a custom log formatter
55 //!
56 //! ```
57 //! use android_logger::Config;
58 //!
59 //! android_logger::init_once(
60 //! Config::default()
61 //! .with_min_level(log::Level::Trace)
62 //! .format(|f, record| write!(f, "my_app: {}", record.args()))
63 //! )
64 //! ```
65
66 #[cfg(target_os = "android")]
67 extern crate android_log_sys as log_ffi;
68 #[macro_use]
69 extern crate lazy_static;
70 #[macro_use]
71 extern crate log;
72
73 extern crate env_logger;
74
75 use std::sync::RwLock;
76
77 #[cfg(target_os = "android")]
78 use log_ffi::LogPriority;
79 use log::{Level, Log, Metadata, Record};
80 use std::ffi::{CStr, CString};
81 use std::mem;
82 use std::fmt;
83 use std::ptr;
84
85 pub use env_logger::filter::{Filter, Builder as FilterBuilder};
86 pub use env_logger::fmt::Formatter;
87
88 pub(crate) type FormatFn = Box<dyn Fn(&mut dyn fmt::Write, &Record) -> fmt::Result + Sync + Send>;
89
90 /// Output log to android system.
91 #[cfg(target_os = "android")]
android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr)92 fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
93 unsafe {
94 log_ffi::__android_log_write(
95 prio as log_ffi::c_int,
96 tag.as_ptr() as *const log_ffi::c_char,
97 msg.as_ptr() as *const log_ffi::c_char,
98 )
99 };
100 }
101
102 /// Dummy output placeholder for tests.
103 #[cfg(not(target_os = "android"))]
android_log(_priority: Level, _tag: &CStr, _msg: &CStr)104 fn android_log(_priority: Level, _tag: &CStr, _msg: &CStr) {}
105
106 /// Underlying android logger backend
107 pub struct AndroidLogger {
108 config: RwLock<Config>,
109 }
110
111 impl AndroidLogger {
112 /// Create new logger instance from config
new(config: Config) -> AndroidLogger113 pub fn new(config: Config) -> AndroidLogger {
114 AndroidLogger {
115 config: RwLock::new(config),
116 }
117 }
118 }
119
120 lazy_static! {
121 static ref ANDROID_LOGGER: AndroidLogger = AndroidLogger::default();
122 }
123
124 const LOGGING_TAG_MAX_LEN: usize = 23;
125 const LOGGING_MSG_MAX_LEN: usize = 4000;
126
127 impl Default for AndroidLogger {
128 /// Create a new logger with default config
default() -> AndroidLogger129 fn default() -> AndroidLogger {
130 AndroidLogger {
131 config: RwLock::new(Config::default()),
132 }
133 }
134 }
135
136 impl Log for AndroidLogger {
enabled(&self, _: &Metadata) -> bool137 fn enabled(&self, _: &Metadata) -> bool {
138 true
139 }
140
log(&self, record: &Record)141 fn log(&self, record: &Record) {
142 let config = self.config
143 .read()
144 .expect("failed to acquire android_log filter lock for read");
145
146 if !config.filter_matches(record) {
147 return;
148 }
149
150 // tag must not exceed LOGGING_TAG_MAX_LEN
151 #[allow(deprecated)] // created an issue #35 for this
152 let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
153
154 let module_path = record.module_path().unwrap_or_default().to_owned();
155
156 // If no tag was specified, use module name
157 let custom_tag = &config.tag;
158 let tag = custom_tag.as_ref().map(|s| s.as_bytes()).unwrap_or(module_path.as_bytes());
159
160 // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
161 self.fill_tag_bytes(&mut tag_bytes, tag);
162 // use stack array as C string
163 let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };
164
165 // message must not exceed LOGGING_MSG_MAX_LEN
166 // therefore split log message into multiple log calls
167 let mut writer = PlatformLogWriter::new(record.level(), tag);
168
169 // If a custom tag is used, add the module path to the message.
170 // Use PlatformLogWriter to output chunks if they exceed max size.
171 let _ = match (custom_tag, &config.custom_format) {
172 (_, Some(format)) => format(&mut writer, record),
173 (Some(_), _) => fmt::write(
174 &mut writer,
175 format_args!("{}: {}", module_path, *record.args()),
176 ),
177 _ => fmt::write(&mut writer, *record.args()),
178 };
179
180 // output the remaining message (this would usually be the most common case)
181 writer.flush();
182 }
183
flush(&self)184 fn flush(&self) {}
185 }
186
187 impl AndroidLogger {
fill_tag_bytes(&self, array: &mut [u8], tag: &[u8])188 fn fill_tag_bytes(&self, array: &mut [u8], tag: &[u8]) {
189 if tag.len() > LOGGING_TAG_MAX_LEN {
190 for (input, output) in tag.iter()
191 .take(LOGGING_TAG_MAX_LEN - 2)
192 .chain(b"..\0".iter())
193 .zip(array.iter_mut())
194 {
195 *output = *input;
196 }
197 } else {
198 for (input, output) in tag.iter()
199 .chain(b"\0".iter())
200 .zip(array.iter_mut())
201 {
202 *output = *input;
203 }
204 }
205 }
206 }
207
208 /// Filter for android logger.
209 pub struct Config {
210 log_level: Option<Level>,
211 filter: Option<env_logger::filter::Filter>,
212 tag: Option<CString>,
213 custom_format: Option<FormatFn>,
214 }
215
216 impl Default for Config {
default() -> Self217 fn default() -> Self {
218 Config {
219 log_level: None,
220 filter: None,
221 tag: None,
222 custom_format: None,
223 }
224 }
225 }
226
227 impl Config {
228 /// Change the minimum log level.
229 ///
230 /// All values above the set level are logged. For example, if
231 /// `Warn` is set, the `Error` is logged too, but `Info` isn't.
with_min_level(mut self, level: Level) -> Self232 pub fn with_min_level(mut self, level: Level) -> Self {
233 self.log_level = Some(level);
234 self
235 }
236
filter_matches(&self, record: &Record) -> bool237 fn filter_matches(&self, record: &Record) -> bool {
238 if let Some(ref filter) = self.filter {
239 filter.matches(&record)
240 } else {
241 true
242 }
243 }
244
with_filter(mut self, filter: env_logger::filter::Filter) -> Self245 pub fn with_filter(mut self, filter: env_logger::filter::Filter) -> Self {
246 self.filter = Some(filter);
247 self
248 }
249
with_tag<S: Into<Vec<u8>>>(mut self, tag: S) -> Self250 pub fn with_tag<S: Into<Vec<u8>>>(mut self, tag: S) -> Self {
251 self.tag = Some(CString::new(tag).expect("Can't convert tag to CString"));
252 self
253 }
254
255 /// Sets the format function for formatting the log output.
256 /// ```
257 /// # use android_logger::Config;
258 /// android_logger::init_once(
259 /// Config::default()
260 /// .with_min_level(log::Level::Trace)
261 /// .format(|f, record| write!(f, "my_app: {}", record.args()))
262 /// )
263 /// ```
format<F>(mut self, format: F) -> Self where F: Fn(&mut dyn fmt::Write, &Record) -> fmt::Result + Sync + Send + 'static,264 pub fn format<F>(mut self, format: F) -> Self
265 where
266 F: Fn(&mut dyn fmt::Write, &Record) -> fmt::Result + Sync + Send + 'static,
267 {
268 self.custom_format = Some(Box::new(format));
269 self
270 }
271 }
272
273 struct PlatformLogWriter<'a> {
274 #[cfg(target_os = "android")] priority: LogPriority,
275 #[cfg(not(target_os = "android"))] priority: Level,
276 len: usize,
277 last_newline_index: usize,
278 tag: &'a CStr,
279 buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
280 }
281
282 impl<'a> PlatformLogWriter<'a> {
283 #[cfg(target_os = "android")]
new(level: Level, tag: &CStr) -> PlatformLogWriter284 pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
285 #[allow(deprecated)] // created an issue #35 for this
286 PlatformLogWriter {
287 priority: match level {
288 Level::Warn => LogPriority::WARN,
289 Level::Info => LogPriority::INFO,
290 Level::Debug => LogPriority::DEBUG,
291 Level::Error => LogPriority::ERROR,
292 Level::Trace => LogPriority::VERBOSE,
293 },
294 len: 0,
295 last_newline_index: 0,
296 tag,
297 buffer: unsafe { mem::uninitialized() },
298 }
299 }
300
301 #[cfg(not(target_os = "android"))]
new(level: Level, tag: &CStr) -> PlatformLogWriter302 pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
303 #[allow(deprecated)] // created an issue #35 for this
304 PlatformLogWriter {
305 priority: level,
306 len: 0,
307 last_newline_index: 0,
308 tag,
309 buffer: unsafe { mem::uninitialized() },
310 }
311 }
312
313 /// Flush some bytes to android logger.
314 ///
315 /// If there is a newline, flush up to it.
316 /// If ther was no newline, flush all.
317 ///
318 /// Not guaranteed to flush everything.
temporal_flush(&mut self)319 fn temporal_flush(&mut self) {
320 let total_len = self.len;
321
322 if total_len == 0 {
323 return;
324 }
325
326 if self.last_newline_index > 0 {
327 let copy_from_index = self.last_newline_index;
328 let remaining_chunk_len = total_len - copy_from_index;
329
330 self.output_specified_len(copy_from_index);
331 self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
332 self.len = remaining_chunk_len;
333 } else {
334 self.output_specified_len(total_len);
335 self.len = 0;
336 }
337 self.last_newline_index = 0;
338 }
339
340 /// Flush everything remaining to android logger.
flush(&mut self)341 fn flush(&mut self) {
342 let total_len = self.len;
343
344 if total_len == 0 {
345 return;
346 }
347
348 self.output_specified_len(total_len);
349 self.len = 0;
350 self.last_newline_index = 0;
351 }
352
353 /// Output buffer up until the \0 which will be placed at `len` position.
output_specified_len(&mut self, len: usize)354 fn output_specified_len(&mut self, len: usize) {
355 let mut last_byte: u8 = b'\0';
356 mem::swap(&mut last_byte, unsafe {
357 self.buffer.get_unchecked_mut(len)
358 });
359
360 let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
361 android_log(self.priority, self.tag, msg);
362
363 *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
364 }
365
366 /// Copy `len` bytes from `index` position to starting position.
copy_bytes_to_start(&mut self, index: usize, len: usize)367 fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
368 let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
369 let dst = self.buffer.as_mut_ptr();
370 unsafe { ptr::copy(src, dst, len) };
371 }
372 }
373
374 impl<'a> fmt::Write for PlatformLogWriter<'a> {
write_str(&mut self, s: &str) -> fmt::Result375 fn write_str(&mut self, s: &str) -> fmt::Result {
376 let mut incomming_bytes = s.as_bytes();
377
378 while !incomming_bytes.is_empty() {
379 let len = self.len;
380
381 // write everything possible to buffer and mark last \n
382 let new_len = len + incomming_bytes.len();
383 let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN]
384 .iter_mut()
385 .zip(incomming_bytes)
386 .enumerate()
387 .fold(None, |acc, (i, (output, input))| {
388 *output = *input;
389 if *input == b'\n' {
390 Some(i)
391 } else {
392 acc
393 }
394 });
395
396 // update last \n index
397 if let Some(newline) = last_newline {
398 self.last_newline_index = len + newline;
399 }
400
401 // calculate how many bytes were written
402 let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
403 // if the len was not exceeded
404 self.len = new_len;
405 new_len - len // written len
406 } else {
407 // if new length was exceeded
408 self.len = LOGGING_MSG_MAX_LEN;
409 self.temporal_flush();
410
411 LOGGING_MSG_MAX_LEN - len // written len
412 };
413
414 incomming_bytes = &incomming_bytes[written_len..];
415 }
416
417 Ok(())
418 }
419 }
420
421 /// Send a log record to Android logging backend.
422 ///
423 /// This action does not require initialization. However, without initialization it
424 /// will use the default filter, which allows all logs.
log(record: &Record)425 pub fn log(record: &Record) {
426 ANDROID_LOGGER.log(record)
427 }
428
429 /// Initializes the global logger with an android logger.
430 ///
431 /// This can be called many times, but will only initialize logging once,
432 /// and will not replace any other previously initialized logger.
433 ///
434 /// It is ok to call this at the activity creation, and it will be
435 /// repeatedly called on every lifecycle restart (i.e. screen rotation).
init_once(config: Config)436 pub fn init_once(config: Config) {
437 if let Err(err) = log::set_logger(&*ANDROID_LOGGER) {
438 debug!("android_logger: log::set_logger failed: {}", err);
439 } else {
440 if let Some(level) = config.log_level {
441 log::set_max_level(level.to_level_filter());
442 }
443 *ANDROID_LOGGER
444 .config
445 .write()
446 .expect("failed to acquire android_log filter lock for write") = config;
447 }
448 }
449
450 #[cfg(test)]
451 mod tests {
452 use super::*;
453 use std::fmt::Write;
454 use std::sync::atomic::{AtomicBool, Ordering};
455
456 #[test]
check_config_values()457 fn check_config_values() {
458 // Filter is checked in config_filter_match below.
459 let config = Config::default()
460 .with_min_level(Level::Trace)
461 .with_tag("my_app");
462
463 assert_eq!(config.log_level, Some(Level::Trace));
464 assert_eq!(config.tag, Some(CString::new("my_app").unwrap()));
465 }
466
467 #[test]
log_calls_formatter()468 fn log_calls_formatter() {
469 static FORMAT_FN_WAS_CALLED: AtomicBool = AtomicBool::new(false);
470 let config = Config::default()
471 .with_min_level(Level::Info)
472 .format(|_, _| {
473 FORMAT_FN_WAS_CALLED.store(true, Ordering::SeqCst);
474 Ok(())
475 });
476 let logger = AndroidLogger::new(config);
477
478 logger.log(&Record::builder().level(Level::Info).build());
479
480 assert!(FORMAT_FN_WAS_CALLED.load(Ordering::SeqCst));
481 }
482
483 #[test]
logger_always_enabled()484 fn logger_always_enabled() {
485 let logger = AndroidLogger::new(Config::default());
486
487 assert!(logger.enabled(&log::MetadataBuilder::new().build()));
488 }
489
490 // Test whether the filter gets called correctly. Not meant to be exhaustive for all filter
491 // options, as these are handled directly by the filter itself.
492 #[test]
config_filter_match()493 fn config_filter_match() {
494 let info_record = Record::builder().level(Level::Info).build();
495 let debug_record = Record::builder().level(Level::Debug).build();
496
497 let info_all_filter = env_logger::filter::Builder::new().parse("info").build();
498 let info_all_config = Config::default().with_filter(info_all_filter);
499
500 assert!(info_all_config.filter_matches(&info_record));
501 assert!(!info_all_config.filter_matches(&debug_record));
502 }
503
504 #[test]
fill_tag_bytes_truncates_long_tag()505 fn fill_tag_bytes_truncates_long_tag() {
506 let logger = AndroidLogger::new(Config::default());
507 let too_long_tag: [u8; LOGGING_TAG_MAX_LEN + 20] = [b'a'; LOGGING_TAG_MAX_LEN + 20];
508
509 let mut result: [u8; LOGGING_TAG_MAX_LEN + 1] = Default::default();
510 logger.fill_tag_bytes(&mut result, &too_long_tag);
511
512 let mut expected_result = [b'a'; LOGGING_TAG_MAX_LEN - 2].to_vec();
513 expected_result.extend("..\0".as_bytes());
514 assert_eq!(result.to_vec(), expected_result);
515 }
516
517 #[test]
fill_tag_bytes_keeps_short_tag()518 fn fill_tag_bytes_keeps_short_tag() {
519 let logger = AndroidLogger::new(Config::default());
520 let short_tag: [u8; 3] = [b'a'; 3];
521
522 let mut result: [u8; LOGGING_TAG_MAX_LEN + 1] = Default::default();
523 logger.fill_tag_bytes(&mut result, &short_tag);
524
525 let mut expected_result = short_tag.to_vec();
526 expected_result.push(0);
527 assert_eq!(result.to_vec()[..4], expected_result);
528 }
529
530 #[test]
platform_log_writer_init_values()531 fn platform_log_writer_init_values() {
532 let tag = CStr::from_bytes_with_nul(b"tag\0").unwrap();
533
534 let writer = PlatformLogWriter::new(Level::Warn, &tag);
535
536 assert_eq!(writer.tag, tag);
537 // Android uses LogPriority instead, which doesn't implement equality checks
538 #[cfg(not(target_os = "android"))]
539 assert_eq!(writer.priority, Level::Warn);
540 }
541
542 #[test]
temporal_flush()543 fn temporal_flush() {
544 let mut writer = get_tag_writer();
545
546 writer
547 .write_str("12\n\n567\n90")
548 .expect("Unable to write to PlatformLogWriter");
549
550 assert_eq!(writer.len, 10);
551 writer.temporal_flush();
552 // Should have flushed up until the last newline.
553 assert_eq!(writer.len, 3);
554 assert_eq!(writer.last_newline_index, 0);
555 assert_eq!(&writer.buffer.to_vec()[..writer.len], "\n90".as_bytes());
556
557 writer.temporal_flush();
558 // Should have flushed all remaining bytes.
559 assert_eq!(writer.len, 0);
560 assert_eq!(writer.last_newline_index, 0);
561 }
562
563 #[test]
flush()564 fn flush() {
565 let mut writer = get_tag_writer();
566 writer
567 .write_str("abcdefghij\n\nklm\nnopqr\nstuvwxyz")
568 .expect("Unable to write to PlatformLogWriter");
569
570 writer.flush();
571
572 assert_eq!(writer.last_newline_index, 0);
573 assert_eq!(writer.len, 0);
574 }
575
576 #[test]
last_newline_index()577 fn last_newline_index() {
578 let mut writer = get_tag_writer();
579
580 writer
581 .write_str("12\n\n567\n90")
582 .expect("Unable to write to PlatformLogWriter");
583
584 assert_eq!(writer.last_newline_index, 7);
585 }
586
587 #[test]
output_specified_len_leaves_buffer_unchanged()588 fn output_specified_len_leaves_buffer_unchanged() {
589 let mut writer = get_tag_writer();
590 let log_string = "abcdefghij\n\nklm\nnopqr\nstuvwxyz";
591 writer
592 .write_str(log_string)
593 .expect("Unable to write to PlatformLogWriter");
594
595 writer.output_specified_len(5);
596
597 assert_eq!(
598 writer.buffer[..log_string.len()].to_vec(),
599 log_string.as_bytes()
600 );
601 }
602
603 #[test]
copy_bytes_to_start()604 fn copy_bytes_to_start() {
605 let mut writer = get_tag_writer();
606 writer
607 .write_str("0123456789")
608 .expect("Unable to write to PlatformLogWriter");
609
610 writer.copy_bytes_to_start(3, 2);
611
612 assert_eq!(writer.buffer[..10].to_vec(), "3423456789".as_bytes());
613 }
614
615 #[test]
copy_bytes_to_start_nop()616 fn copy_bytes_to_start_nop() {
617 let test_string = "Test_string_with\n\n\n\nnewlines\n";
618 let mut writer = get_tag_writer();
619 writer
620 .write_str(test_string)
621 .expect("Unable to write to PlatformLogWriter");
622
623 writer.copy_bytes_to_start(0, 20);
624 writer.copy_bytes_to_start(10, 0);
625
626 assert_eq!(
627 writer.buffer[..test_string.len()].to_vec(),
628 test_string.as_bytes()
629 );
630 }
631
get_tag_writer() -> PlatformLogWriter<'static>632 fn get_tag_writer() -> PlatformLogWriter<'static> {
633 PlatformLogWriter::new(Level::Warn, &CStr::from_bytes_with_nul(b"tag\0").unwrap())
634 }
635 }
636