glean_ffi/
fd_logger.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use std::fs::File;
6use std::io::Write;
7use std::sync::RwLock;
8
9#[cfg(target_os = "windows")]
10use std::os::windows::io::FromRawHandle;
11
12#[cfg(target_os = "windows")]
13use std::ffi::c_void;
14
15#[cfg(not(target_os = "windows"))]
16use std::os::unix::io::FromRawFd;
17
18use serde::Serialize;
19
20/// An implementation of log::Log that writes log messages in JSON format to a
21/// file descriptor/handle. The logging level is ignored in this implementation:
22/// it is up to the receiver of these log messages (on the language binding
23/// side) to filter the log messages based on their level.
24/// The JSON payload of each message in an object with the following keys:
25///    - `level` (string): One of the logging levels defined here:
26///      https://docs.rs/log/0.4.11/log/enum.Level.html
27///    - `message` (string): The logging message.
28pub struct FdLogger {
29    pub file: RwLock<File>,
30}
31
32#[derive(Serialize)]
33struct FdLoggingRecord {
34    level: String,
35    message: String,
36}
37
38#[cfg(target_os = "windows")]
39unsafe fn get_file_from_fd(fd: u64) -> File {
40    File::from_raw_handle(fd as *mut c_void)
41}
42
43#[cfg(not(target_os = "windows"))]
44unsafe fn get_file_from_fd(fd: u64) -> File {
45    File::from_raw_fd(fd as i32)
46}
47
48impl FdLogger {
49    pub unsafe fn new(fd: u64) -> Self {
50        FdLogger {
51            file: RwLock::new(get_file_from_fd(fd)),
52        }
53    }
54}
55
56impl log::Log for FdLogger {
57    fn enabled(&self, _metadata: &log::Metadata) -> bool {
58        // This logger always emits logging messages of any level, and the
59        // language binding consuming these messages is responsible for
60        // filtering and routing them.
61        true
62    }
63
64    fn log(&self, record: &log::Record) {
65        // Normally, classes implementing the Log trait would filter based on
66        // the log level here. But in this case, we want to emit all log
67        // messages and let the logging system in the language binding filter
68        // and route them.
69        let payload = FdLoggingRecord {
70            level: record.level().to_string(),
71            message: record.args().to_string(),
72        };
73        let _ = writeln!(
74            self.file.write().unwrap(),
75            "{}",
76            serde_json::to_string(&payload).unwrap()
77        );
78    }
79
80    fn flush(&self) {
81        let _ = self.file.write().unwrap().flush();
82    }
83}