1use std::fmt::Write;
6use std::os::fd::AsFd;
7use std::sync::LazyLock;
8use zx::{self as zx, ObjectType};
9
10static LOGGER: LazyLock<KernelLogger> = LazyLock::new(KernelLogger::new);
11
12pub struct KernelLogger {
14 debuglog: zx::DebugLog,
15}
16
17impl KernelLogger {
18 fn new() -> KernelLogger {
20 let debuglog = fdio::clone_fd(std::io::stdout().as_fd()).expect("get handle from stdout");
21 assert_eq!(debuglog.basic_info().unwrap().object_type, ObjectType::DEBUGLOG);
22 KernelLogger { debuglog: debuglog.into() }
23 }
24
25 pub fn init() {
30 log::set_logger(&*LOGGER).expect("set logger must succeed");
31 log::set_max_level(log::LevelFilter::Info);
32 let previous_hook = std::panic::take_hook();
33 std::panic::set_hook(Box::new(move |info| {
34 log::error!("PANIC {info}");
35 previous_hook(info);
36 }));
37 }
38}
39
40impl log::Log for KernelLogger {
41 fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
42 metadata.level() <= log::Level::Info
44 }
45
46 fn log(&self, record: &log::Record<'_>) {
47 if self.enabled(record.metadata()) {
48 let msg_buffer = format!("{}", record.args());
49 let mut visitor = StringVisitor(msg_buffer);
50 let _ = record.key_values().visit(&mut visitor);
51 let msg_buffer = visitor.0;
52
53 let msg_prefix = format!("[component_manager] {}: ", record.level());
54
55 let mut msg = msg_buffer.as_str();
57 while msg.len() > 0 {
58 let mut split_point = if let Some(newline_pos) = msg.find('\n') {
61 newline_pos + 1
62 } else {
63 msg.len()
64 };
65 split_point =
66 std::cmp::min(split_point, zx::sys::ZX_LOG_RECORD_DATA_MAX - msg_prefix.len());
67
68 while !msg.is_char_boundary(split_point) {
71 split_point -= 1;
72 }
73
74 let mut msg_to_write = format!("{}{}", msg_prefix, &msg[..split_point]);
78
79 if msg_to_write.chars().last() == Some('\n') {
81 msg_to_write.truncate(msg_to_write.len() - 1);
82 }
83
84 if let Err(_) = self.debuglog.write(msg_to_write.as_bytes()) {
85 }
91 msg = &msg[split_point..];
92 }
93 }
94 }
95
96 fn flush(&self) {}
97}
98
99struct StringVisitor(String);
100
101impl log::kv::VisitSource<'_> for StringVisitor {
102 fn visit_pair(
103 &mut self,
104 key: log::kv::Key<'_>,
105 value: log::kv::Value<'_>,
106 ) -> Result<(), log::kv::Error> {
107 value.visit(StringValueVisitor { buf: &mut self.0, key: key.as_str() })
108 }
109}
110
111struct StringValueVisitor<'a> {
112 buf: &'a mut String,
113 key: &'a str,
114}
115
116impl log::kv::VisitValue<'_> for StringValueVisitor<'_> {
117 fn visit_any(&mut self, value: log::kv::Value<'_>) -> Result<(), log::kv::Error> {
118 write!(self.buf, " {}={}", self.key, value).expect("writing into strings does not fail");
119 Ok(())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use anyhow::Context;
127 use fidl_fuchsia_boot as fboot;
128 use fuchsia_component::client::connect_channel_to_protocol;
129 use log::{error, info, warn};
130 use rand::Rng;
131 use std::panic;
132
133 const MAX_INFO_LINE_LEN: usize =
134 zx::sys::ZX_LOG_RECORD_DATA_MAX - "[component_manager] INFO: ".len();
135
136 fn get_readonlylog() -> zx::DebugLog {
137 let (client_end, server_end) = zx::Channel::create();
138 connect_channel_to_protocol::<fboot::ReadOnlyLogMarker>(server_end).unwrap();
139 let service = fboot::ReadOnlyLogSynchronousProxy::new(client_end);
140 let log = service.get(zx::MonotonicInstant::INFINITE).expect("couldn't get read only log");
141 log
142 }
143
144 fn expect_message_in_debuglog(sent_msg: String) {
148 let debuglog = get_readonlylog();
149 for _ in 0..10000 {
150 match debuglog.read() {
151 Ok(record) => {
152 let log = record.data().to_string();
153 if log.starts_with(&sent_msg) {
154 return;
156 }
157 }
158 Err(status) if status == zx::Status::SHOULD_WAIT => {
159 debuglog
160 .wait_one(zx::Signals::LOG_READABLE, zx::MonotonicInstant::INFINITE)
161 .expect("Failed to wait for log readable");
162 continue;
163 }
164 Err(status) => {
165 panic!("Unexpected error from zx_debuglog_read: {}", status);
166 }
167 }
168 }
169 panic!("first 10000 log messages didn't include the one we sent!");
170 }
171
172 fn init() {
176 const STDOUT_FD: i32 = 1;
177
178 let resource = zx::Resource::from(zx::NullableHandle::invalid());
179 let debuglog = zx::DebugLog::create(&resource, zx::DebugLogOpts::empty())
180 .context("Failed to create debuglog object")
181 .unwrap();
182 fdio::bind_to_fd(debuglog.into_handle(), STDOUT_FD).unwrap();
183
184 KernelLogger::init();
185 }
186
187 fn make_str_with_len(prefix: &str, len: usize) -> String {
188 let mut rng = rand::rng();
189 let mut res = format!("{}{}{}", prefix, rng.random::<u64>().to_string(), "a".repeat(len));
190 res.truncate(len);
191 res
192 }
193
194 #[test]
195 fn log_info_test() {
196 let mut rng = rand::rng();
197 let logged_value: u64 = rng.random();
198
199 init();
200 info!("log_test {}", logged_value);
201
202 expect_message_in_debuglog(format!("[component_manager] INFO: log_test {}", logged_value));
203 }
204
205 #[test]
206 fn log_info_newline_test() {
207 let mut rng = rand::rng();
208 let logged_value1: u64 = rng.random();
209 let logged_value2: u64 = rng.random();
210
211 init();
212 info!("log_test1 {}\nlog_test2 {}", logged_value1, logged_value2);
213
214 expect_message_in_debuglog(format!(
215 "[component_manager] INFO: log_test1 {}",
216 logged_value1
217 ));
218 expect_message_in_debuglog(format!(
219 "[component_manager] INFO: log_test2 {}",
220 logged_value2
221 ));
222 }
223
224 #[test]
225 fn log_many_newlines_test() {
226 let mut rng = rand::rng();
227 let logged_value1: u64 = rng.random();
228 let logged_value2: u64 = rng.random();
229
230 init();
231 info!("\n\nmnl_log_test1 {}\n\nmnl_log_test2 {}\n\n", logged_value1, logged_value2);
232
233 expect_message_in_debuglog(format!(
234 "[component_manager] INFO: mnl_log_test1 {}",
235 logged_value1
236 ));
237 expect_message_in_debuglog(format!(
238 "[component_manager] INFO: mnl_log_test2 {}",
239 logged_value2
240 ));
241 }
242
243 #[test]
244 fn log_one_very_long_line_test() {
245 let line1: String = make_str_with_len("line1:", MAX_INFO_LINE_LEN);
246 let line2: String = make_str_with_len("line2:", MAX_INFO_LINE_LEN);
247 let line3: String = make_str_with_len("line3:", MAX_INFO_LINE_LEN);
248
249 init();
250 info!("{}{}{}", line1, line2, line3);
251
252 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
253 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
254 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line3));
255 }
256
257 #[test]
258 fn log_line_that_would_be_split_without_newline_test() {
259 let line1: String = make_str_with_len("line1:", 128);
260 let line2: String = make_str_with_len("line2:", 128);
261
262 init();
263 info!("{}\n{}", line1, line2);
264
265 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
266 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
267 }
268
269 #[test]
270 fn log_overly_long_line_with_newline_test() {
271 let line1: String = make_str_with_len("line1:", MAX_INFO_LINE_LEN);
272 let line1part2: String = make_str_with_len("line1part2:", 80);
273 let line2: String = make_str_with_len("line2:", 80);
274
275 init();
276 info!("{}{}\n{}", line1, line1part2, line2);
277
278 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
279 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1part2));
280 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line2));
281 }
282
283 #[test]
284 fn log_pathological_utf8_data() {
285 let line1: String = make_str_with_len("emojiline1:", MAX_INFO_LINE_LEN - 1);
288
289 init();
290 info!("{}😈", line1);
291
292 expect_message_in_debuglog(format!("[component_manager] INFO: {}", line1));
293 expect_message_in_debuglog(format!("[component_manager] INFO: 😈"));
294 }
295
296 #[test]
297 fn log_warn_test() {
298 let mut rng = rand::rng();
299 let logged_value: u64 = rng.random();
300
301 init();
302 warn!("log_test {}", logged_value);
303
304 expect_message_in_debuglog(format!("[component_manager] WARN: log_test {}", logged_value));
305 }
306
307 #[test]
308 fn log_error_test() {
309 let mut rng = rand::rng();
310 let logged_value: u64 = rng.random();
311
312 init();
313 error!("log_test {}", logged_value);
314
315 expect_message_in_debuglog(format!("[component_manager] ERROR: log_test {}", logged_value));
316 }
317
318 #[test]
319 #[should_panic(expected = "panic_test")]
320 #[cfg_attr(feature = "variant_asan", ignore)]
322 #[cfg_attr(feature = "variant_hwasan", ignore)]
323 fn log_panic_test() {
324 let mut rng = rand::rng();
325 let logged_value: u64 = rng.random();
326
327 let old_hook = panic::take_hook();
328 panic::set_hook(Box::new(move |info| {
329 old_hook(info);
332 expect_message_in_debuglog(format!("[component_manager] PANIC: panicked at"));
333 expect_message_in_debuglog(format!("panic_test {logged_value}"));
334 }));
335
336 init();
337 panic!("panic_test {}", logged_value);
338 }
339}