xref: /aosp_15_r20/external/pigweed/pw_rust/examples/tokenized_logging/pw_log_backend.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 //! An example `pw_log` backend that uses `pw_tokenizer`.
16 //!
17 //! Log output is base64 encoded and printed using ARM semihosting.
18 #![no_std]
19 
20 #[doc(hidden)]
21 pub mod __private {
22     use cortex_m_semihosting::hprintln;
23     use pw_log_backend_api::LogLevel;
24     use pw_status::Result;
25     use pw_stream::{Cursor, Write};
26     use pw_tokenizer::MessageWriter;
27 
28     // Re-export for use by the `pw_logf_backend!` macro.
29     pub use pw_tokenizer::{tokenize_core_fmt_to_writer, tokenize_printf_to_writer};
30 
31     const ENCODE_BUFFER_SIZE: usize = 32;
32 
33     // A simple implementation of [`pw_tokenizer::MessageWriter`] that writes
34     // data to a buffer.  On message finalization, it base64 encodes the data
35     // and prints it using `hprintln!`.
36     pub struct LogMessageWriter {
37         cursor: Cursor<[u8; ENCODE_BUFFER_SIZE]>,
38     }
39 
40     impl MessageWriter for LogMessageWriter {
new() -> Self41         fn new() -> Self {
42             Self {
43                 cursor: Cursor::new([0u8; ENCODE_BUFFER_SIZE]),
44             }
45         }
46 
write(&mut self, data: &[u8]) -> Result<()>47         fn write(&mut self, data: &[u8]) -> Result<()> {
48             self.cursor.write_all(data)
49         }
50 
remaining(&self) -> usize51         fn remaining(&self) -> usize {
52             self.cursor.remaining()
53         }
54 
finalize(self) -> Result<()>55         fn finalize(self) -> Result<()> {
56             let write_len = self.cursor.position();
57             let data = self.cursor.into_inner();
58 
59             // Pigweed's detokenization tools recognize base64 encoded data
60             // prefixed with a `$` as tokenized data interspersed with plain text.
61             let mut encode_buffer = [0u8; pw_base64::encoded_size(ENCODE_BUFFER_SIZE)];
62             if let Ok(s) = pw_base64::encode_str(&data[..write_len], &mut encode_buffer) {
63                 hprintln!("${}", s);
64             }
65 
66             Ok(())
67         }
68     }
69 
log_level_tag(level: LogLevel) -> &'static str70     pub const fn log_level_tag(level: LogLevel) -> &'static str {
71         match level {
72             LogLevel::Debug => "DBG",
73             LogLevel::Info => "INF",
74             LogLevel::Warn => "WRN",
75             LogLevel::Error => "ERR",
76             LogLevel::Critical => "CRT",
77             LogLevel::Fatal => "FTL",
78         }
79     }
80 }
81 
82 // Implement the `pw_log` backend API.
83 //
84 // Since we're logging to a shared/ambient resource we can use
85 // tokenize_*_to_writer!` instead of `tokenize_*_to_buffer!` and avoid the
86 // overhead of initializing any intermediate buffers or objects.
87 //
88 // Uses `pw_format` special `PW_FMT_CONCAT` operator to prepend a place to
89 // print the log level.
90 #[macro_export]
91 macro_rules! pw_log_backend {
92   ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{
93     let _ = $crate::__private::tokenize_core_fmt_to_writer!(
94       $crate::__private::LogMessageWriter,
95       "[{}] " PW_FMT_CONCAT $format_string,
96       $crate::__private::log_level_tag($log_level) as &str,
97       $($args),*);
98   }};
99 }
100 
101 #[macro_export]
102 macro_rules! pw_logf_backend {
103   ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{
104     let _ = $crate::__private::tokenize_printf_to_writer!(
105       $crate::__private::LogMessageWriter,
106       "[%s] " PW_FMT_CONCAT $format_string,
107       $crate::__private::log_level_tag($log_level),
108       $($args),*);
109   }};
110 }
111