文章阅读
flutter_rust_bridge在控制台打印日志
2025/12/1685 次阅读9 分钟
添加日志相关依赖
lazy_static = "1.5.0"
log = "0.4.22"
parking_lot = "0.12.3"
simplelog = {version="0.12.2",futures=["time"]}
time = "0.3.36"
anyhow = "1.0.86"
创建entitys.rs文件
pub struct LogEntry {
pub time_millis: i64,
pub level: i32,
pub tag: String,
pub msg: String,
}
创建log.rs文件
use std::sync::Once;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use lazy_static::lazy_static;
use log::{error, info, warn, LevelFilter, Log, Metadata, Record};
use parking_lot::RwLock;
use simplelog::*;
use crate::frb_generated::StreamSink;
use super::entitys::LogEntry;
pub fn create_log_stream(s: StreamSink<LogEntry>) -> anyhow::Result<()> {
SendToDartLogger::set_stream_sink(s);
Ok(())
}
pub fn rust_set_up() {
init_logger();
}
static INIT_LOGGER_ONCE: Once = Once::new();
pub fn init_logger() {
// https://stackoverflow.com/questions/30177845/how-to-initialize-the-logger-for-integration-tests
INIT_LOGGER_ONCE.call_once(|| {
let level = if cfg!(debug_assertions) {
LevelFilter::Debug
} else {
LevelFilter::Warn
};
assert!(
level <= log::STATIC_MAX_LEVEL,
"Should respect log::STATIC_MAX_LEVEL={:?}, which is done in compile time. level{:?}",
log::STATIC_MAX_LEVEL,
level
);
CombinedLogger::init(vec![
Box::new(SendToDartLogger::new()),
Box::new(MyMobileLogger::new()),
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
TermLogger::new(
level,
ConfigBuilder::new()
.set_time_format_custom(format_description!("[hour]:[minute]:[second].[subsecond]"))
.build(),
TerminalMode::Mixed,
ColorChoice::Auto,
),
])
.unwrap_or_else(|e| {
error!("init_logger (inside 'once') has error: {:?}", e);
});
info!("init_logger (inside 'once') finished");
warn!(
"init_logger finished, chosen level={:?} (deliberately output by warn level)",
level
);
});
}
lazy_static! {
static ref SEND_TO_DART_LOGGER_STREAM_SINK: RwLock<Option<StreamSink<LogEntry>>> =
RwLock::new(None);
}
pub struct SendToDartLogger {
}
impl SendToDartLogger {
pub fn set_stream_sink(stream_sink: StreamSink<LogEntry>) {
let mut guard = SEND_TO_DART_LOGGER_STREAM_SINK.write();
let overriding = guard.is_some();
*guard = Some(stream_sink);
drop(guard);
if overriding {
warn!(
"SendToDartLogger::set_stream_sink but already exist a sink, thus overriding. \
(This may or may not be a problem. It will happen normally if hot-reload Flutter app.)"
);
}
}
pub fn new() -> Self {
SendToDartLogger {
}
}
fn record_to_entry(record: &Record) -> LogEntry {
let time_millis = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| Duration::from_secs(0))
.as_millis() as i64;
let level = match record.level() {
Level::Trace => Self::LEVEL_TRACE,
Level::Debug => Self::LEVEL_DEBUG,
Level::Info => Self::LEVEL_INFO,
Level::Warn => Self::LEVEL_WARN,
Level::Error => Self::LEVEL_ERROR,
};
let tag = record.file().unwrap_or_else(|| record.target()).to_owned();
let msg = format!("{}", record.args());
LogEntry {
time_millis,
level,
tag,
msg,
}
}
const LEVEL_TRACE: i32 = 5000;
const LEVEL_DEBUG: i32 = 10000;
const LEVEL_INFO: i32 = 20000;
const LEVEL_WARN: i32 = 30000;
const LEVEL_ERROR: i32 = 40000;
}
impl Log for SendToDartLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
let entry = Self::record_to_entry(record);
if let Some(sink) = &*SEND_TO_DART_LOGGER_STREAM_SINK.read() {
let _ = sink.add(entry);
}
}
fn flush(&self) {
// no need
}
}
impl SharedLogger for SendToDartLogger {
fn config(&self) -> Option<&Config> {
None
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
fn level(&self) -> LevelFilter {
LevelFilter::Debug
}
}
pub struct MyMobileLogger {
#[cfg(target_os = "ios")]
ios_logger: oslog::OsLogger,
}
impl MyMobileLogger {
pub fn new() -> Self {
MyMobileLogger {
#[cfg(target_os = "ios")]
ios_logger: oslog::OsLogger::new("vision_utils_rs"),
}
}
}
impl Log for MyMobileLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
#[allow(unused_variables)]
fn log(&self, record: &Record) {
#[cfg(any(target_os = "android", target_os = "ios"))]
let modified_record = {
let override_level = Level::Info;
record.to_builder().level(override_level).build()
};
#[cfg(target_os = "android")]
android_logger::log(&modified_record);
#[cfg(target_os = "ios")]
self.ios_logger.log(&modified_record);
}
fn flush(&self) {
// no need
}
}
impl SharedLogger for MyMobileLogger {
fn level(&self) -> LevelFilter {
LevelFilter::Debug
}
fn config(&self) -> Option<&Config> {
None
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
初始化日志
rust_set_up();
在flutter端监听日志
void listenLog() {
createLogStream().listen((log) {
print("log ...");
});
}