-
Notifications
You must be signed in to change notification settings - Fork 405
Logging interface #91
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
Conversation
Still early work, need to add more comments and a test part, but want your thinking about it. Mainly, it s a lightweight copy of crate log, without a global logger, and less features. Maybe there is still to much stuff, and I'll prune more if needed. |
travis failures because I need to add Logger to test, working on that |
Could it be worth exploring to use |
Then the client has to set it and you have to use unsafe every time you want to read from it, no? Arcs aren't too bad if they're not copied often.
…On July 26, 2018 3:33:21 AM UTC, yuntai ***@***.***> wrote:
Could it be worth exploring to use `static ref Logger` instead of
passing around `Arc<Logger>`?
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
#91 (comment)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, looks cool!
src/util/logger.rs
Outdated
} | ||
|
||
/// A trait encapsulating the operations required of a logger | ||
pub trait Logger: Sync + Send { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be full-pub somehow, so I presume the macros and this need to be in a separate module?
6736566
to
e5c6c54
Compare
Updated, currently you need to pass |
e5c6c54
to
d2ab686
Compare
d2ab686 rebased |
d2ab686
to
3356d6e
Compare
Should build + rebase |
3356d6e
to
36273c9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really awesome work, thanks! It does seem to me to be a bit overengineered, though. It looks like there's about three ways to filter log messages (client-side, build-time, and via the filter stuff). I originally suggested supporting build-time to avoid performance issues with formatting strings and passing them to the user for every debug message, but if you can encapsulate that in the object passed to the client, it seems like there should be pretty low overhead for something like that, so maybe we can just always call into the client and let them filter as they see fit? If it becomes an issue maybe we can template our types based on the logger type so LLVM can inline the call?
src/util/logger.rs
Outdated
) | ||
} | ||
|
||
macro_rules! warn { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make these names a little less generic if we're gonna use them all over the crate? Like log_debug instead of debug, at least?
src/util/logger.rs
Outdated
|
||
/// An enum representing the available verbosity level filters of the logger. | ||
#[derive(Copy, Eq, Debug, Hash)] | ||
pub enum LevelFilter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems way overdesigned to have a filter API like this where our log logic does the filtering. Seems much simpler to me to just call into the logger for every log message and let the client filter it how they see fit? Would cut out a ton of code here, no?
src/util/logger.rs
Outdated
#[derive(Clone,Debug)] | ||
pub struct Record<'a> { | ||
metadata: Metadata<'a>, | ||
args: fmt::Arguments<'a>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh this is cool! If we have this do we need features to disable logging of low-level messages? Most of the reason I suggested having a feature was to avoid calling format!() for every debug message even when its just going to be discarded as that can be a performance issue in some applications, but it looks like maybe that wouldn't be an issue with this?
36273c9
to
2f9b8a7
Compare
Instead of relaying on static variable for global filtering, I added conditional compilation directly in macro, same effect with less code. Based on playing with rustc linking flags of dead code, it prunes useless call to logger in macro body. It doesn't speed up compile-time as macro expanding is handled earlier, but it's a little decrease in binary size. |
I cut the filter API, at first I was dubious to let it for already existents logger implementations being backward compatible with us, but well I've already cut too much for this being right. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think this is a bit overengineered, but I'm getting increasingly excited to start using it...I'm tired of maintaining a patch that adds a ton of println!()s everywhere :p.
src/util/logger.rs
Outdated
} | ||
} | ||
|
||
impl FromStr for Level { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just not implement FromStr for Level? I'm not sure who would ever really need that, and it adds a whole Err type and associated impls.
Cargo.toml
Outdated
@@ -15,6 +15,11 @@ build = "build.rs" | |||
# Supports tracking channels with a non-bitcoin chain hashes. Currently enables all kinds of fun DoS attacks. | |||
non_bitcoin_chain_hash_routing = [] | |||
fuzztarget = ["secp256k1/fuzztarget", "bitcoin/fuzztarget"] | |||
max_level_error = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point about overhead was more a question of "would someone care about the peroformance hit of this" - the reason to have the conditional compilation is that the act of doing the format!() itself can often be nontrivial, especially when you're running at something like trace, but because rust lets us do format_args!() without actually doing the full format!() unless the client actually wants to write out the line to their log, the overhead of doing a log call that gets dropped should be pretty low (mostly the indirect call through the Arc, but given we're doing a ton of crypto anyway so what's some indirect calls, which theoretically we could get dropped by templating if we needed to in the future). All that said, having the options isn't many lines of code, so I don't feel so strongly anymore.
Still, if we do want to keep the features, I'd think its more intuitive to have them go the other way - default to call the user's Logger for everything TRACE and up, and let the user turn things off at compile-time if they want to opt into it.
src/util/test_utils.rs
Outdated
|
||
use bitcoin::blockdata::transaction::Transaction; | ||
|
||
use secp256k1::PublicKey; | ||
|
||
use std::sync::{Arc,Mutex}; | ||
use std::{mem}; | ||
use std::fmt::Write; | ||
use std::cell::RefCell; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused.
src/util/test_utils.rs
Outdated
|
||
use bitcoin::blockdata::transaction::Transaction; | ||
|
||
use secp256k1::PublicKey; | ||
|
||
use std::sync::{Arc,Mutex}; | ||
use std::{mem}; | ||
use std::fmt::Write; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused.
src/util/logger.rs
Outdated
use std::str::FromStr; | ||
use std::sync::Arc; | ||
|
||
use std::ascii::AsciiExt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears unused now?
src/util/logger.rs
Outdated
} | ||
} | ||
|
||
#[allow(dead_code)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you just add a few sample logs in obvious places and remove the allow(dead_code)s? That way we can see it works and no need for bending over backwards here.
src/util/logger.rs
Outdated
|
||
#[allow(dead_code)] | ||
impl Level { | ||
fn from_usize(u: usize) -> Option<Level> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here - if you remove from_str can probably remove from_usize as well.
src/util/logger.rs
Outdated
/// A trait encapsulating the operations required of a logger | ||
pub trait Logger: Sync + Send { | ||
/// Enable the logger with a maximum log level | ||
fn enable(&mut self, level: Level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this (and flush, and level) need to be present? I think that's all client-side stuff that a library doesn't really need to concern itself with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still unsure why we'd need a third way to limit the log level (which this appears to be?).
2f9b8a7
to
51482c5
Compare
Updated, reverse the logic for filtering, but I think it's fine to keep the 2 ways as you can have multiples loggers, each with its level policy and you want some global setting, without having to update each one. Also cut down the useless stuff and throw sample logs in some places (trying to find others ones) |
51482c5
to
f97ef28
Compare
Right, no need of enable in the API, let it be implemented client-side. Rebased |
Cargo.toml
Outdated
@@ -15,6 +15,11 @@ build = "build.rs" | |||
# Supports tracking channels with a non-bitcoin chain hashes. Currently enables all kinds of fun DoS attacks. | |||
non_bitcoin_chain_hash_routing = [] | |||
fuzztarget = ["secp256k1/fuzztarget", "bitcoin/fuzztarget"] | |||
max_level_off = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make these a bit more descriptive (eg "disable_log_level_X")? Also, these need to be documented somewhere (I guess in the docs for Logger?).
src/ln/channel.rs
Outdated
@@ -1176,6 +1184,7 @@ impl Channel { | |||
let (remote_initial_commitment_tx, our_signature) = match self.funding_created_signature(&msg.signature) { | |||
Ok(res) => res, | |||
Err(e) => { | |||
log_error!(self, "Got error handling message {}", e.err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, log_error probably shouldn't be used for things remote peers can trivially trigger (eg a failed HTLC, we could use it for things where a channel had a confirmed transaction but something resulted in it being closed due to error), but, in general, we should try to keep log_error "DoS-resistant" and have some docs indicating that its safe to use and a remote peer cannot trivially fill up a client's disk using it.
src/util/logger.rs
Outdated
|
||
static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; | ||
|
||
static LEVEL_PARSE_ERROR: &'static str = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears unused now?
src/util/logger.rs
Outdated
Trace, | ||
} | ||
|
||
impl Clone for Level { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this just be derived?
src/util/logger.rs
Outdated
pub struct Record<'a> { | ||
metadata: Metadata<'a>, | ||
args: fmt::Arguments<'a>, | ||
module_path: Option<&'a str>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this (and the below two additional vars) an Option? It seems like its always available (and should always be available), no?
src/util/logger.rs
Outdated
} | ||
|
||
macro_rules! log_internal { | ||
($self: ident, target: $target:expr, $lvl:expr, $($arg:tt)+) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we accept a $target here when we always are called without it until we call outselves? Can't we just drop the indirection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes my mistake. Legacy design let you opt out using different pattern-matching of the macro. But in fact it's up to the implementation if it doesn't want to bother with additional infos
|
||
#[test] | ||
fn test_logging_macros() { | ||
let mut logger = TestLogger::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, would be nice if this actually checked that logs were generated, but not a big deal wither way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah at first, I wawed to pipe the output or implement Write trait on a wrapper giving it the output of format_args! but not sure if it worth the hurdle. For me, it's testing that format_args! and stdout works well.
src/util/test_utils.rs
Outdated
|
||
impl Logger for TestLogger { | ||
fn log(&self, record: &Record) { | ||
if self.level >= record.metadata().level() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe just always log the record since output isnt printed unless the test fails anyway? Could skip the enable() function then, too.
src/util/mod.rs
Outdated
|
||
#[cfg(not(feature = "fuzztarget"))] | ||
#[macro_use] | ||
pub(crate) mod logger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Logger trait needs to be always-pub, but the macros for logging should probably be pub(crate). I guess they need to be separated out into different mods?
src/util/mod.rs
Outdated
@@ -12,3 +12,12 @@ pub use self::rng::reset_rng_state; | |||
|
|||
#[cfg(test)] | |||
pub(crate) mod test_utils; | |||
#[cfg(feature = "fuzztarget")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, the Logger thing is simple enough I figure fuzz can impl its own test logger (which it can tune to enable logging info only when running in test so that it doesn't slow down the fuzzer too much).
932c81c
to
b1a1d3d
Compare
Updated and rebased. I cleaned the useless stuff as Metadata and others which were legacy design due to copy from log crate. |
src/util/test_utils.rs
Outdated
|
||
impl Logger for TestLogger { | ||
fn log(&self, record: &Record) { | ||
if self.level >= record.level() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth it to let it as an example for future logger implementers ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just gonna merge, will fix the two comments here later as logs are added.
src/util/logger.rs
Outdated
|
||
/// The message body. | ||
#[inline] | ||
pub fn args(&self) -> &fmt::Arguments<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just make the vars in Record pub (with docs) and not worry about accessors?
src/util/macro_logger.rs
Outdated
($self: ident, $lvl:expr, $($arg:tt)+) => ( | ||
&$self.logger.log(&Record::new($lvl, format_args!($($arg)+), module_path!(), file!(), line!())); | ||
); | ||
($self: ident, $lvl:expr, $($arg:tt)+) => (log_internal!($self, $lvl, $($arg)+)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this comment wasn't addressed: #91 (comment)
951e7d5
to
19c84c5
Compare
Implement error, warn, info, debug and trace macros, internally calling an instance of Logger, and passing it to every main structures Build-time or client-side filtering. Issue lightningdevkit#54
19c84c5
to
0029f04
Compare
Implement error, warn, info, debug and trace macros, internally calling
an instance of Logger, and passing it to every main structures
Issue #54