-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add documentation about how to let Rust do logging and let Dart get those logs #486
Comments
I can paste my own code that works in my production environment here. Btw my code not only logs to Dart, but also logs to Android/iOS "standard" logging as well.
pub struct LogEntry {
pub time_millis: i64,
pub level: i32,
pub tag: String,
pub msg: String,
}
pub fn create_log_stream(s: StreamSink<LogEntry>) -> Result<()> {
logger::SendToDartLogger::set_stream_sink(s);
Ok(())
}
pub fn rust_set_up() {
logger::init_logger();
}
use std::sync::Once;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use flutter_rust_bridge::StreamSink;
use lazy_static::lazy_static;
use log::{error, info, warn, Log, Metadata, Record};
use parking_lot::RwLock;
use simplelog::*;
use crate::frb::api::LogEntry;
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(level)),
Box::new(MyMobileLogger::new(level)),
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
TermLogger::new(
level,
ConfigBuilder::new()
.set_time_format_str("%H:%M:%S%.3f")
.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 {
level: LevelFilter,
}
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(level: LevelFilter) -> Self {
SendToDartLogger { level }
}
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() {
sink.add(entry);
}
}
fn flush(&self) {
// no need
}
}
impl SharedLogger for SendToDartLogger {
fn level(&self) -> LevelFilter {
self.level
}
fn config(&self) -> Option<&Config> {
None
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
pub struct MyMobileLogger {
level: LevelFilter,
#[cfg(target_os = "ios")]
ios_logger: oslog::OsLogger,
}
impl MyMobileLogger {
pub fn new(level: LevelFilter) -> Self {
MyMobileLogger {
level,
#[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 {
self.level
}
fn config(&self) -> Option<&Config> {
None
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
class _VisionUtilsRsImplExtended extends VisionUtilsRsImpl
with
FlutterRustBridgeSetupMixin,
FlutterRustBridgeTimeoutMixin,
VisionUtilsFrbLogMixin,
VisionUtilsFrbErrorReportMixin {
static const _kTag = 'VisionUtilsRsImplExtended';
_VisionUtilsRsImplExtended._raw(super.inner) : super.raw() {
Log.d(_kTag, 'inside constructor, call setupMixinConstructor');
setupMixinConstructor();
}
@override
Future<void> setup() async {
final config = await visionUtilsConfig;
await rustSetUp(
release: config.release, pseudoSessId: config.pseudoSessId, hint: FlutterRustBridgeSetupMixin.kHintSetup);
createLogStream().listen((event) {
Log.instance.log(event.level, _kRustTagPrefix + event.tag, '${event.msg}(rust_time=${event.timeMillis})');
});
}
@override
Duration? get timeLimitForExecuteNormal {
return isInDebugMode ? const Duration(seconds: 30) : const Duration(seconds: 15);
}
@override
void log(String message) => Log.d(_kTag, message);
} |
/cc @thomas725 |
that's a lovely feature, thank you for sharing! I found I could get rid of most errors by adding those 3 lines to my
Also since my flutter_rust_bridge project layout is copied from https://github.com/Desdaemon/flutter_rust_bridge_template I had to remove I believe Now I got 3 errors left:
|
btw parking_lot is a public Rust library, find it using Google |
That is a method in crate |
oh, thanks for the hint.. it's name sounds very domain specific ;)
do I maybe need an older version? I've added EDIT: But I guess I can simply remove the custom timestamp format and go with the default for now, let's first see if I can get everything else working. |
Sure. Those are minor details and surely you can choose your own :) |
I would have liked to have access to the logger level definitions in flutter, so I though I move them from logger.rs SendToDartLogger struct over into my api.rs LogEntry struct + adding pub keyword to each of them, but now the flutter_rust_bridge_codegen step fails with:
Did I do something stupid or is support for pub const within a struct a not yet implemented feature of the flutter_rust_bridge? |
"Permission denied" - sounds like a separate problem. Maybe create a new issue. And check do you have |
your right, I did, see: #494
I do. Otherwise I guess |
How do I use the logger.rs code you posted above on the rust side, after the stream has been setup by the dart side? |
See how rust do standard logging. |
Oh! Nice! Thank you! To get it to compile for android, I had to remove the
Are you using a different version of the |
You are welcome! log 0.4.14 in my case, but anyway just modify code accordingly |
well that's strange, even if I specify version 0.4.14 in my EDIT: oh, it seems I misunderstood what EDIT2: I found here: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html Just as it doesn't believe that the type |
I would like to add something to this thread that might be useful for relative beginners, like me. My IDE kept complaining about the following code snipped, because pub struct LogEntry {
pub time_millis: i64,
pub level: i32,
pub tag: String,
pub msg: String,
}
pub fn create_log_stream(s: StreamSink<LogEntry>) -> anyhow::Result<()> {
Ok(())
} Don't try implementing this yourself, because |
Feel free to add some doc at proper places and make a PR :) |
@thomas725 That is a rust-language problem unrelated to my package indeed :) Try asking on StackOverflow, the logging package, etc |
Done :-) (#507) |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
I briefly update the doc to point to this issue, since this issue contains all code already. https://github.com/fzyzcjy/flutter_rust_bridge/blob/master/book/src/feature/stream.md |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new issue. |
Context:
#443 (comment) , where @thomas725 asks about this.
The text was updated successfully, but these errors were encountered: