зеркало из https://github.com/mozilla/glean.git
[Python] Expose fd logging through UniFFI
This commit is contained in:
Родитель
4e1c0e62af
Коммит
6ad68f09a9
|
@ -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::*;
|
||||
|
|
Загрузка…
Ссылка в новой задаче