Use UniFFI to export our logging API (gh-5308)
Added a new crate called `rust-log-forwarder` that forwards logs to consumers. This does basically the same thing as `rc-log`, but it uses UniFFI. Once our consumer apps swich over to `rust-log-forwarder`, we can remove the `rc-log` component.
This commit is contained in:
Родитель
cc652b0531
Коммит
8496e93409
|
@ -50,6 +50,13 @@ projects:
|
|||
- name: rustlog
|
||||
type: aar
|
||||
description: Android hook into the log crate.
|
||||
rust-log-forwarder:
|
||||
path: components/support/rust-log-forwarder/android
|
||||
artifactId: rust-log-forwarder
|
||||
publications:
|
||||
- name: rust-log-forwarder
|
||||
type: aar
|
||||
description: Forward logs from Rust
|
||||
httpconfig:
|
||||
path: components/viaduct/android
|
||||
artifactId: httpconfig
|
||||
|
|
|
@ -1792,6 +1792,7 @@ dependencies = [
|
|||
"places",
|
||||
"push",
|
||||
"rc_log_ffi",
|
||||
"rust-log-forwarder",
|
||||
"sync_manager",
|
||||
"tabs",
|
||||
"viaduct",
|
||||
|
@ -1821,6 +1822,7 @@ dependencies = [
|
|||
"places",
|
||||
"push",
|
||||
"rc_log_ffi",
|
||||
"rust-log-forwarder",
|
||||
"sync15",
|
||||
"tabs",
|
||||
"viaduct",
|
||||
|
@ -2923,6 +2925,15 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-log-forwarder"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
|
|
|
@ -23,6 +23,7 @@ members = [
|
|||
"components/support/rc_crypto/nss/nss_build_common",
|
||||
"components/support/rc_crypto/nss/nss_sys",
|
||||
"components/support/rc_crypto/nss/systest",
|
||||
"components/support/rust-log-forwarder",
|
||||
"components/support/sql",
|
||||
"components/support/types",
|
||||
"components/support/viaduct-reqwest",
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "rust-log-forwarder"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["application-services@mozilla.com"]
|
||||
license = "MPL-2.0"
|
||||
exclude = ["/android", "/ios"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
parking_lot = ">=0.11,<=0.12"
|
||||
uniffi = "0.23"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.23", features = ["build"] }
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
apply from: "$rootDir/build-scripts/component-common.gradle"
|
||||
apply from: "$rootDir/publish.gradle"
|
||||
|
||||
ext.configureUniFFIBindgen("../src/rust_log_forwarder.udl")
|
||||
ext.dependsOnTheMegazord()
|
||||
ext.configurePublish()
|
|
@ -0,0 +1,2 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.mozilla.appservices.rust_log_forwarder" />
|
|
@ -0,0 +1,8 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("./src/rust_log_forwarder.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Logger interface for foreign code
|
||||
//!
|
||||
//! This is what the application code defines. It's responsible for taking rust log records and
|
||||
//! feeding them to the application logging system.
|
||||
|
||||
pub use log::Level;
|
||||
|
||||
/// log::Record, except it exposes it's data as fields rather than methods
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Record {
|
||||
pub level: Level,
|
||||
pub target: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub trait Logger: Sync + Send {
|
||||
fn log(&self, record: Record);
|
||||
}
|
||||
|
||||
impl From<&log::Record<'_>> for Record {
|
||||
fn from(record: &log::Record) -> Self {
|
||||
Self {
|
||||
level: record.level(),
|
||||
target: record.target().to_string(),
|
||||
message: record.args().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
mod foreign_logger;
|
||||
mod rust_logger;
|
||||
|
||||
pub use foreign_logger::{Level, Logger, Record};
|
||||
|
||||
static HAVE_SET_MAX_LEVEL: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Set the logger to forward to.
|
||||
///
|
||||
/// Pass in None to disable logging.
|
||||
pub fn set_logger(logger: Option<Box<dyn Logger>>) {
|
||||
// Set a default max level, if none has already been set
|
||||
if !HAVE_SET_MAX_LEVEL.load(Ordering::Relaxed) {
|
||||
set_max_level(Level::Debug);
|
||||
}
|
||||
rust_logger::set_foreign_logger(logger)
|
||||
}
|
||||
|
||||
/// Set the maximum log level filter. Records below this level will not be sent to the logger.
|
||||
pub fn set_max_level(level: Level) {
|
||||
log::set_max_level(level.to_level_filter());
|
||||
HAVE_SET_MAX_LEVEL.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
uniffi::include_scaffolding!("rust_log_forwarder");
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TestLogger {
|
||||
records: Arc<Mutex<Vec<Record>>>,
|
||||
}
|
||||
|
||||
impl TestLogger {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
records: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_records(&self, correct_records: Vec<Record>) {
|
||||
assert_eq!(*self.records.lock().unwrap(), correct_records);
|
||||
}
|
||||
|
||||
fn clear_records(&self) {
|
||||
self.records.lock().unwrap().clear()
|
||||
}
|
||||
}
|
||||
|
||||
impl Logger for TestLogger {
|
||||
fn log(&self, record: Record) {
|
||||
self.records.lock().unwrap().push(record)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logging() {
|
||||
let logger = TestLogger::new();
|
||||
set_logger(Some(Box::new(logger.clone())));
|
||||
log::info!("Test message");
|
||||
log::warn!("Test message2");
|
||||
logger.check_records(vec![
|
||||
Record {
|
||||
level: Level::Info,
|
||||
target: "rust_log_forwarder::test".into(),
|
||||
message: "Test message".into(),
|
||||
},
|
||||
Record {
|
||||
level: Level::Warn,
|
||||
target: "rust_log_forwarder::test".into(),
|
||||
message: "Test message2".into(),
|
||||
},
|
||||
]);
|
||||
logger.clear_records();
|
||||
set_logger(None);
|
||||
log::info!("Test message");
|
||||
log::warn!("Test message2");
|
||||
logger.check_records(vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_level() {
|
||||
set_max_level(Level::Debug);
|
||||
assert_eq!(log::max_level(), log::Level::Debug);
|
||||
set_max_level(Level::Warn);
|
||||
assert_eq!(log::max_level(), log::Level::Warn);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_level_default() {
|
||||
HAVE_SET_MAX_LEVEL.store(false, Ordering::Relaxed);
|
||||
let logger = TestLogger::new();
|
||||
// Calling set_logger should set the level to `Debug' by default
|
||||
set_logger(Some(Box::new(logger)));
|
||||
assert_eq!(log::max_level(), log::Level::Debug);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_level_default_ignored_if_set_manually() {
|
||||
HAVE_SET_MAX_LEVEL.store(false, Ordering::Relaxed);
|
||||
set_max_level(Level::Warn);
|
||||
// Calling set_logger should not set the level if it was set manually.
|
||||
set_logger(Some(Box::new(TestLogger::new())));
|
||||
assert_eq!(log::max_level(), log::Level::Warn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
namespace rust_log_forwarder {
|
||||
// Set the logger to forward to.
|
||||
//
|
||||
// Pass in null to disable logging.
|
||||
void set_logger(Logger? logger);
|
||||
// Set the maximum log level filter. Records below this level will not be sent to the logger.
|
||||
void set_max_level(Level level);
|
||||
};
|
||||
|
||||
enum Level {
|
||||
"Error",
|
||||
"Warn",
|
||||
"Info",
|
||||
"Debug",
|
||||
"Trace",
|
||||
};
|
||||
|
||||
dictionary Record {
|
||||
Level level;
|
||||
// The target field from the Rust log crate. Usually the Rust module name, however log! calls can manually override the target name.
|
||||
string target;
|
||||
string message;
|
||||
};
|
||||
|
||||
callback interface Logger {
|
||||
void log(Record record);
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Rust Logger implementation
|
||||
//!
|
||||
//! This is responsible for taking logs from the rust log crate and forwarding them to a
|
||||
//! foreign_logger::Logger instance.
|
||||
|
||||
use crate::foreign_logger::Logger as ForeignLogger;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Once,
|
||||
};
|
||||
|
||||
// ForeignLogger to forward to
|
||||
static RUST_LOGGER: Logger = Logger::new();
|
||||
// Handles calling `log::set_logger`, which can only be called once.
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
struct Logger {
|
||||
foreign_logger: RwLock<Option<Box<dyn ForeignLogger>>>,
|
||||
is_enabled: AtomicBool,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
foreign_logger: RwLock::new(None),
|
||||
is_enabled: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_foreign_logger(&self, foreign_logger: Option<Box<dyn ForeignLogger>>) {
|
||||
self.is_enabled
|
||||
.store(foreign_logger.is_some(), Ordering::Relaxed);
|
||||
*self.foreign_logger.write() = foreign_logger;
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, _: &log::Metadata<'_>) -> bool {
|
||||
self.is_enabled.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record<'_>) {
|
||||
if let Some(foreign_logger) = &*self.foreign_logger.read() {
|
||||
foreign_logger.log(record.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
pub fn set_foreign_logger(foreign_logger: Option<Box<dyn ForeignLogger>>) {
|
||||
INIT.call_once(|| {
|
||||
// This should be the only component that calls `log::set_logger()`. If not, then
|
||||
// panic'ing seems reasonable.
|
||||
log::set_logger(&RUST_LOGGER).expect(
|
||||
"Failed to initialize rust-log-forwarder::Logger, other log implementation already initialized?",
|
||||
);
|
||||
});
|
||||
RUST_LOGGER.set_foreign_logger(foreign_logger);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
[bindings.kotlin]
|
||||
package_name = "mozilla.appservices.rust_log_forwarder"
|
||||
cdylib_name = "megazord"
|
||||
|
||||
[bindings.swift]
|
||||
ffi_module_name = "MozillaRustComponents"
|
||||
ffi_module_filename = "rustlogforwarderFFI"
|
||||
generate_module_map = false
|
|
@ -16,6 +16,7 @@ sync_manager = { path = "../../components/sync_manager/" }
|
|||
places = { path = "../../components/places" }
|
||||
push = { path = "../../components/push" }
|
||||
rc_log_ffi = { path = "../../components/rc_log" }
|
||||
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||
viaduct = { path = "../../components/viaduct" }
|
||||
nimbus-sdk = { path = "../../components/nimbus" }
|
||||
autofill = { path = "../../components/autofill" }
|
||||
|
|
|
@ -16,7 +16,10 @@ pub use logins;
|
|||
pub use nimbus;
|
||||
pub use places;
|
||||
pub use push;
|
||||
// TODO: Drop this dependency once android-components switches to using `rust_log_forwarder` for
|
||||
// log forwarding.
|
||||
pub use rc_log_ffi;
|
||||
pub use rust_log_forwarder;
|
||||
pub use sync_manager;
|
||||
pub use tabs;
|
||||
pub use viaduct;
|
||||
|
|
|
@ -10,6 +10,7 @@ crate-type = ["staticlib"]
|
|||
|
||||
[dependencies]
|
||||
rc_log_ffi = { path = "../../components/rc_log" }
|
||||
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||
viaduct = { path = "../../components/viaduct" }
|
||||
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
|
||||
nimbus-sdk = { path = "../../components/nimbus" }
|
||||
|
|
|
@ -13,7 +13,10 @@ pub use logins;
|
|||
pub use nimbus;
|
||||
pub use places;
|
||||
pub use push;
|
||||
// TODO: Drop this dependency once firefox-ios switches to using `rust_log_forwarder` for log
|
||||
// forwarding.
|
||||
pub use rc_log_ffi;
|
||||
pub use rust_log_forwarder;
|
||||
pub use sync15;
|
||||
pub use tabs;
|
||||
pub use viaduct_reqwest;
|
||||
|
|
Загрузка…
Ссылка в новой задаче