1//! This crate implements a *backend* to the C logger API in crate `logger`.
2//!
3//! This backend delegates to the Rust logging framework in crate `log`.
4//!
5//! The only other backend to `logger` that we currently use is the
6//! `ShimLogger`. Once that is migrated to Rust and hooks directly into the
7//! Rust `log` framework, this crate will be the *only* back-end for `logger`.
8//! At that point `logger` can be merged into this crate and simplified.
9//!
10//! TODO: This crate should be `no_std`.
1112// https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md
13#![deny(unsafe_op_in_unsafe_fn)]
1415use std::ffi::CStr;
16use std::os::raw::{c_char, c_int};
1718use log::log_enabled;
1920/// Flush Rust's log::logger().
21#[unsafe(no_mangle)]
22pub extern "C-unwind" fn rustlogger_flush() {
23 log::logger().flush();
24}
2526/// Returns the `str` pointed to by `ptr` if it's non-NULL and points
27/// to a UTF-8 null-terminated string.
28/// # Safety
29///
30/// `ptr` must point a NULL-terminated C String if non-null, and must
31/// be immutable for the lifetime of the returned `str`.
32unsafe fn optional_str(ptr: *const c_char) -> Option<&'static str> {
33if ptr.is_null() {
34None
35} else {
36// Safe if caller obeyed doc'd preconditions.
37unsafe { std::ffi::CStr::from_ptr(ptr).to_str().ok() }
38 }
39}
4041pub fn c_to_rust_log_level(level: logger::LogLevel) -> Option<log::Level> {
42use log::Level::*;
43match level {
44 logger::_LogLevel_LOGLEVEL_ERROR => Some(Error),
45 logger::_LogLevel_LOGLEVEL_WARNING => Some(Warn),
46 logger::_LogLevel_LOGLEVEL_INFO => Some(Info),
47 logger::_LogLevel_LOGLEVEL_DEBUG => Some(Debug),
48 logger::_LogLevel_LOGLEVEL_TRACE => Some(Trace),
49 logger::_LogLevel_LOGLEVEL_UNSET => None,
50_ => panic!("Unexpected log level {}", level),
51 }
52}
5354/// Whether logging is currently enabled for `level`.
55#[unsafe(no_mangle)]
56pub extern "C-unwind" fn rustlogger_isEnabled(level: logger::LogLevel) -> c_int {
57let level = c_to_rust_log_level(level).unwrap();
58log_enabled!(level).into()
59}
6061/// Log to Rust's log::logger().
62///
63/// # Safety
64///
65/// Pointer args must be safely dereferenceable. `format` and `args` must
66/// follow the rules of `sprintf(3)`.
67#[unsafe(no_mangle)]
68pub unsafe extern "C-unwind" fn rustlogger_log(
69 level: logger::LogLevel,
70 file_name: *const c_char,
71 fn_name: *const c_char,
72 line: i32,
73 format: *const c_char,
74 args: va_list::VaList,
75) {
76let log_level = c_to_rust_log_level(level).unwrap();
7778if !log_enabled!(log_level) {
79return;
80 }
8182assert!(!format.is_null());
83let format = unsafe { CStr::from_ptr(format) };
84let mut msgbuf = formatting_nostd::FormatBuffer::<500>::new();
85// SAFETY: Safe if caller provided valid format and args.
86unsafe { msgbuf.sprintf(format, args) };
8788 log::logger().log(
89&log::Record::builder()
90 .level(log_level)
91// SAFETY: file_name is statically allocated.
92.file_static(unsafe { optional_str(file_name) })
93 .line(Some(u32::try_from(line).unwrap()))
94// SAFETY: fn_name is statically allocated.
95.module_path_static(unsafe { optional_str(fn_name) })
96 .args(format_args!("{}", msgbuf))
97 .build(),
98 );
99}