blob: 0ead11739b3470f2a8363a1187bfbda817c9cb5f [file] [log] [blame]
Thirumurugan7ea0a5e2024-10-16 15:44:031// Copyright 2024 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Haihan Chenac4d0fc2024-11-08 21:59:345chromium::import! {
Lukasz Anforowicz191a4d32024-11-12 01:48:086 "//base:logging_log_severity_bindgen" as log_severity;
Haihan Chenac4d0fc2024-11-08 21:59:347}
8
Lukasz Anforowicz191a4d32024-11-12 01:48:089use log::Level::{Debug, Error, Info, Trace, Warn};
10use log::{LevelFilter, Metadata, Record};
11use log_severity::logging::{LOGGING_ERROR, LOGGING_INFO, LOGGING_WARNING};
Collin Bakere1e02fde2025-03-26 16:40:1612use std::ffi::CString;
Collin Bakerf0fd42e2025-03-21 16:47:3913use std::pin::Pin;
Haihan Chenac4d0fc2024-11-08 21:59:3414
Thirumurugan7ea0a5e2024-10-16 15:44:0315struct RustLogger;
16
17impl log::Log for RustLogger {
18 fn enabled(&self, _metadata: &Metadata) -> bool {
19 // Always enabled, as it's controlled by command line flags managed by the C++
20 // implementation.
21 true
22 }
23
24 fn log(&self, record: &Record) {
Collin Bakere1e02fde2025-03-26 16:40:1625 // TODO([email protected]): Rather than using heap allocation to pass |msg|
26 // and |file|, we should return a pointer and size object to leverage the
27 // string_view object in C++. https://crbug.com/371112531
28 let file = CString::new(record.file().unwrap())
29 .expect("CString::new failed to create the log file name!");
Collin Bakerf0fd42e2025-03-21 16:47:3930 let wrapped_args = RustFmtArguments(*record.args());
Collin Bakere1e02fde2025-03-26 16:40:1631 unsafe {
32 ffi::print_rust_log(
33 &wrapped_args,
34 file.as_ptr(),
35 record.line().unwrap() as i32,
36 match record.metadata().level() {
37 Error => LOGGING_ERROR,
38 Warn => LOGGING_WARNING,
39 Info => LOGGING_INFO,
40 // Note that Debug and Trace level logs are dropped at
41 // compile time at the macro call-site when DCHECK_IS_ON()
42 // is false. This is done through a Cargo feature.
43 Debug | Trace => LOGGING_INFO,
44 },
45 record.metadata().level() == Trace,
46 )
47 }
Thirumurugan7ea0a5e2024-10-16 15:44:0348 }
49 fn flush(&self) {}
50}
51
52static RUST_LOGGER: RustLogger = RustLogger;
53
Collin Bakerf0fd42e2025-03-21 16:47:3954/// Wrap a `std::fmt::Arguments` to pass to C++ code.
55struct RustFmtArguments<'a>(std::fmt::Arguments<'a>);
56
57impl<'a> RustFmtArguments<'a> {
58 /// Format `msg` to the C++-provided stream in `wrapper`.
59 fn format(&self, mut wrapper: Pin<&mut ffi::LogMessageRustWrapper>) {
60 // No error expected because our `Write` impl below is infallible.
61 std::fmt::write(&mut wrapper, self.0).unwrap();
62 }
63}
64
65// Glue impl to use std::fmt tools with `ffi::LogMessageRustWrapper`.
66impl std::fmt::Write for Pin<&mut ffi::LogMessageRustWrapper> {
67 fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
68 self.as_mut().write_to_stream(s);
69 Ok(())
70 }
71}
72
Thirumurugan7ea0a5e2024-10-16 15:44:0373#[cxx::bridge(namespace = "logging::internal")]
74mod ffi {
75 extern "Rust" {
Collin Bakerf0fd42e2025-03-21 16:47:3976 type RustFmtArguments<'a>;
77
78 fn format(&self, wrapper: Pin<&mut LogMessageRustWrapper>);
79
Thirumurugan7ea0a5e2024-10-16 15:44:0380 fn init_rust_log_crate();
81 }
Collin Baker7cbc0d172025-03-19 21:02:0182
Collin Bakerf0fd42e2025-03-21 16:47:3983 unsafe extern "C++" {
Collin Baker7cbc0d172025-03-19 21:02:0184 include!("base/logging/rust_log_integration.h");
85
Collin Bakerf0fd42e2025-03-21 16:47:3986 /// Wraps a C++ LogMessage object so we can write to its ostream.
87 type LogMessageRustWrapper;
88
89 /// Write a block of characters to the stream.
90 fn write_to_stream(self: Pin<&mut LogMessageRustWrapper>, s: &str);
91
92 /// Emit a log message to the C++-managed logger. `msg` is passed back
93 /// to `format_to_wrapped_message` to be stringified.
Collin Bakere1e02fde2025-03-26 16:40:1694 unsafe fn print_rust_log(
Collin Bakerf0fd42e2025-03-21 16:47:3995 msg: &RustFmtArguments,
Collin Bakere1e02fde2025-03-26 16:40:1696 file: *const c_char,
Collin Baker7cbc0d172025-03-19 21:02:0197 line: i32,
98 severity: i32,
99 verbose: bool,
100 );
101 }
Thirumurugan7ea0a5e2024-10-16 15:44:03102}
103
104pub fn init_rust_log_crate() {
105 // An error may occur if set_logger has already been called, which can happen
106 // during unit tests. In that case, return from the method without executing the
107 // subsequent code.
108 if let Err(_) = log::set_logger(&RUST_LOGGER) {
109 return;
110 };
111 log::set_max_level(LevelFilter::Trace);
112}