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
|
- name: rustlog
|
||||||
type: aar
|
type: aar
|
||||||
description: Android hook into the log crate.
|
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:
|
httpconfig:
|
||||||
path: components/viaduct/android
|
path: components/viaduct/android
|
||||||
artifactId: httpconfig
|
artifactId: httpconfig
|
||||||
|
|
|
@ -1792,6 +1792,7 @@ dependencies = [
|
||||||
"places",
|
"places",
|
||||||
"push",
|
"push",
|
||||||
"rc_log_ffi",
|
"rc_log_ffi",
|
||||||
|
"rust-log-forwarder",
|
||||||
"sync_manager",
|
"sync_manager",
|
||||||
"tabs",
|
"tabs",
|
||||||
"viaduct",
|
"viaduct",
|
||||||
|
@ -1821,6 +1822,7 @@ dependencies = [
|
||||||
"places",
|
"places",
|
||||||
"push",
|
"push",
|
||||||
"rc_log_ffi",
|
"rc_log_ffi",
|
||||||
|
"rust-log-forwarder",
|
||||||
"sync15",
|
"sync15",
|
||||||
"tabs",
|
"tabs",
|
||||||
"viaduct",
|
"viaduct",
|
||||||
|
@ -2923,6 +2925,15 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-log-forwarder"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"uniffi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
|
|
|
@ -23,6 +23,7 @@ members = [
|
||||||
"components/support/rc_crypto/nss/nss_build_common",
|
"components/support/rc_crypto/nss/nss_build_common",
|
||||||
"components/support/rc_crypto/nss/nss_sys",
|
"components/support/rc_crypto/nss/nss_sys",
|
||||||
"components/support/rc_crypto/nss/systest",
|
"components/support/rc_crypto/nss/systest",
|
||||||
|
"components/support/rust-log-forwarder",
|
||||||
"components/support/sql",
|
"components/support/sql",
|
||||||
"components/support/types",
|
"components/support/types",
|
||||||
"components/support/viaduct-reqwest",
|
"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" }
|
places = { path = "../../components/places" }
|
||||||
push = { path = "../../components/push" }
|
push = { path = "../../components/push" }
|
||||||
rc_log_ffi = { path = "../../components/rc_log" }
|
rc_log_ffi = { path = "../../components/rc_log" }
|
||||||
|
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||||
viaduct = { path = "../../components/viaduct" }
|
viaduct = { path = "../../components/viaduct" }
|
||||||
nimbus-sdk = { path = "../../components/nimbus" }
|
nimbus-sdk = { path = "../../components/nimbus" }
|
||||||
autofill = { path = "../../components/autofill" }
|
autofill = { path = "../../components/autofill" }
|
||||||
|
|
|
@ -16,7 +16,10 @@ pub use logins;
|
||||||
pub use nimbus;
|
pub use nimbus;
|
||||||
pub use places;
|
pub use places;
|
||||||
pub use push;
|
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 rc_log_ffi;
|
||||||
|
pub use rust_log_forwarder;
|
||||||
pub use sync_manager;
|
pub use sync_manager;
|
||||||
pub use tabs;
|
pub use tabs;
|
||||||
pub use viaduct;
|
pub use viaduct;
|
||||||
|
|
|
@ -10,6 +10,7 @@ crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rc_log_ffi = { path = "../../components/rc_log" }
|
rc_log_ffi = { path = "../../components/rc_log" }
|
||||||
|
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||||
viaduct = { path = "../../components/viaduct" }
|
viaduct = { path = "../../components/viaduct" }
|
||||||
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
|
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
|
||||||
nimbus-sdk = { path = "../../components/nimbus" }
|
nimbus-sdk = { path = "../../components/nimbus" }
|
||||||
|
|
|
@ -13,7 +13,10 @@ pub use logins;
|
||||||
pub use nimbus;
|
pub use nimbus;
|
||||||
pub use places;
|
pub use places;
|
||||||
pub use push;
|
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 rc_log_ffi;
|
||||||
|
pub use rust_log_forwarder;
|
||||||
pub use sync15;
|
pub use sync15;
|
||||||
pub use tabs;
|
pub use tabs;
|
||||||
pub use viaduct_reqwest;
|
pub use viaduct_reqwest;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче