[Python] Expose fd logging through UniFFI

This commit is contained in:
Jan-Erik Rediger 2022-02-08 13:04:15 +01:00 коммит произвёл Jan-Erik Rediger
Родитель 4e1c0e62af
Коммит 6ad68f09a9
3 изменённых файлов: 131 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,85 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::fs::File;
use std::io::Write;
use std::sync::RwLock;
#[cfg(target_os = "windows")]
use std::os::windows::io::FromRawHandle;
#[cfg(target_os = "windows")]
use std::ffi::c_void;
#[cfg(not(target_os = "windows"))]
use std::os::unix::io::FromRawFd;
use serde::Serialize;
/// An implementation of log::Log that writes log messages in JSON format to a
/// file descriptor/handle. The logging level is ignored in this implementation:
/// it is up to the receiver of these log messages (on the language binding
/// side) to filter the log messages based on their level.
/// The JSON payload of each message in an object with the following keys:
/// - `level` (string): One of the logging levels defined here:
/// https://docs.rs/log/0.4.11/log/enum.Level.html
/// - `message` (string): The logging message.
pub struct FdLogger {
pub file: RwLock<File>,
}
#[derive(Serialize)]
struct FdLoggingRecord {
level: String,
message: String,
target: String
}
#[cfg(target_os = "windows")]
unsafe fn get_file_from_fd(fd: u64) -> File {
File::from_raw_handle(fd as *mut c_void)
}
#[cfg(not(target_os = "windows"))]
unsafe fn get_file_from_fd(fd: u64) -> File {
File::from_raw_fd(fd as i32)
}
impl FdLogger {
pub unsafe fn new(fd: u64) -> Self {
FdLogger {
file: RwLock::new(get_file_from_fd(fd)),
}
}
}
impl log::Log for FdLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
// This logger always emits logging messages of any level, and the
// language binding consuming these messages is responsible for
// filtering and routing them.
true
}
fn log(&self, record: &log::Record) {
// Normally, classes implementing the Log trait would filter based on
// the log level here. But in this case, we want to emit all log
// messages and let the logging system in the language binding filter
// and route them.
let payload = FdLoggingRecord {
level: record.level().to_string(),
message: record.args().to_string(),
target: record.target().to_string(),
};
let _ = writeln!(
self.file.write().unwrap(),
"{}",
serde_json::to_string(&payload).unwrap()
);
}
fn flush(&self) {
let _ = self.file.write().unwrap().flush();
}
}

Просмотреть файл

@ -1,6 +1,12 @@
namespace glean {
void glean_enable_logging();
// Initialize the logging system to send JSON messages to a file descriptor
// (Unix) or file handle (Windows).
//
// No-op on Android and iOS. Use `glean_enable_logging` instead.
void glean_enable_logging_to_fd(u64 fd);
// Initializes Glean.
//
// This will fully initialize Glean in a separate thread.

Просмотреть файл

@ -43,6 +43,9 @@ pub mod traits;
pub mod upload;
mod util;
#[cfg(all(not(target_os = "android"), not(target_os = "ios")))]
mod fd_logger;
pub use crate::common_metric_data::{CommonMetricData, Lifetime};
pub use crate::core::Glean;
pub use crate::core_metrics::ClientInfoMetrics;
@ -828,6 +831,43 @@ pub fn glean_set_dirty_flag(new_value: bool) {
core::with_glean(|glean| glean.set_dirty_flag(new_value))
}
#[cfg(all(not(target_os = "android"), not(target_os = "ios")))]
static FD_LOGGER: OnceCell<fd_logger::FdLogger> = OnceCell::new();
/// Initialize the logging system to send JSON messages to a file descriptor
/// (Unix) or file handle (Windows).
///
/// Not available on Android and iOS.
///
/// `fd` is a writable file descriptor (on Unix) or file handle (on Windows).
///
/// # Safety
/// Unsafe because the fd u64 passed in will be interpreted as either a file
/// descriptor (Unix) or file handle (Windows) without any checking.
#[cfg(all(not(target_os = "android"), not(target_os = "ios")))]
pub fn glean_enable_logging_to_fd(fd: u64) {
// SAFETY: TODO.
unsafe {
// Set up logging to a file descriptor/handle. For this usage, the
// language binding should setup a pipe and pass in the descriptor to
// the writing side of the pipe as the `fd` parameter. Log messages are
// written as JSON to the file descriptor.
if FD_LOGGER.set(fd_logger::FdLogger::new(fd)).is_ok() {
// Set the level so everything goes through to the language
// binding side where it will be filtered by the language
// binding's logging system.
if log::set_logger(FD_LOGGER.get().unwrap()).is_ok() {
log::set_max_level(log::LevelFilter::Debug);
}
}
}
}
#[cfg(any(target_os = "android", target_os = "ios"))]
pub fn glean_enable_logging_to_fd(_fd: u64) {
// intentionally left empty
}
#[allow(missing_docs)]
mod ffi {
use super::*;