1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use log::{Metadata, Record};
use std::fmt::Write;

extern "C" {
	fn px4_log_modulename(level: i32, module: *const u8, fmt: *const u8, ...);
	fn px4_log_raw(level: i32, fmt: *const u8, ...);
}

#[doc(hidden)]
pub enum LogLevel {
	Debug = 0,
	Info = 1,
	Warn = 2,
	Error = 3,
	Panic = 4,
}

#[doc(hidden)]
pub fn log_raw(level: LogLevel, message: &str) {
	unsafe {
		px4_log_raw(
			level as i32,
			"%.*s\0".as_ptr(),
			message.len() as i32,
			message.as_ptr(),
		);
	}
}

/// Print output without any decoration.
///
/// The equivalent of `PX4_INFO_RAW` in C and C++.
///
/// ## Example
///
/// ```
/// # #[macro_use] extern crate px4;
/// # #[no_mangle] fn px4_log_raw() {}
/// # fn main() {
/// info_raw!("Hello {}!\n", "World");
/// # }
/// ```
#[macro_export]
macro_rules! info_raw {
	($($arg:tt)+) => (
		$crate::log_raw($crate::LogLevel::Info, &format!($($arg)+))
	)
}

struct Px4Logger;

impl log::Log for Px4Logger {
	fn enabled(&self, metadata: &Metadata) -> bool {
		metadata.level() <= log::Level::Info
	}

	fn log(&self, record: &Record) {
		if !self.enabled(record.metadata()) {
			return;
		}
		let target = record.target();
		let s = format!("{}\0{}\0", target, record.args());
		let (module, message) = s.split_at(target.len() + 1);
		let level = match record.level() {
			log::Level::Error => LogLevel::Error,
			log::Level::Warn => LogLevel::Warn,
			log::Level::Info => LogLevel::Info,
			log::Level::Debug => LogLevel::Debug,
			log::Level::Trace => LogLevel::Debug,
		};
		unsafe {
			px4_log_modulename(
				level as i32,
				module.as_ptr(),
				"%s\0".as_ptr(),
				message.as_ptr(),
			);
		}
	}

	fn flush(&self) {}
}

static LOGGER: Px4Logger = Px4Logger;

pub unsafe fn init(modulename: &'static [u8]) {
	if log::set_logger(&LOGGER).is_ok() {
		log::set_max_level(log::LevelFilter::Info);
		std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo| {
			let payload: &str = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
				s
			} else if let Some(s) = info.payload().downcast_ref::<String>() {
				&s
			} else {
				"[panic message not available]"
			};
			let mut message = String::new();
			let thread = std::thread::current();
			if let Some(name) = thread.name() {
				write!(message, "thread '{}' ", name).unwrap();
			}
			write!(message, "panicked at '{}'", payload).unwrap();
			if let Some(loc) = info.location() {
				write!(message, ", {}", loc).unwrap();
			}
			message.push('\0');
			px4_log_modulename(
				LogLevel::Panic as i32,
				modulename.as_ptr(),
				"%s\0".as_ptr(),
				message.as_ptr(),
			);
		}));
	}
}