• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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