daboross / fern Goto Github PK
View Code? Open in Web Editor NEWSimple, efficient logging for Rust
License: MIT License
Simple, efficient logging for Rust
License: MIT License
It looks like the cert for your website expired on the 23rd because every time I try to read the fern docs chromium shows the "Your connection is not private" message (NET::ERR_CERT_DATE_INVALID
).
In the Qt framework it is possible to set a mask which is used for filtering the logs:
QT_LOGGING_RULES="*.debug=false;driver.usb.debug=true"
That would be nice if fern supported this as well, by using, for example, the record.module_path()
as a compare against.
Is there a way to color the full line based on its level? For instance, if the level is error, the color of the full line would be red.
This may not just be for this library, but I used this on windows and it created an output file with unix end-of-line characters. I'm used to python doing magic newline conversions in a windows environment. I couldn't seem to find where the newline character was appended to the message but perhaps using something akin to the Python os.linesep
would be better?
This example stalling after writing second "Kek" but works fine if "log_file" commented.
// log = "0.4.14"
// fern = "0.6.0"
use std::fmt;
fn setup_logger() -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("output.log")?)
.apply()?;
Ok(())
}
fn main() {
setup_logger().unwrap();
println!("run");
log::error!("Error: {}", get_str());
println!("exiting");
}
struct Error;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
log::debug!("Kek");
write!(f, "wow")
}
}
fn get_str() -> &'static str {
log::warn!("get_str: {}", Error);
"test"
}
Running `target/debug/test_i18_stall`
run
[test_i18_stall][WARN] get_str: [test_i18_stall][DEBUG] Kek
wow
[test_i18_stall][DEBUG] Kek
gdb backtrace after interrupting
(gdb) bt
#0 0x00007ffff7f93610 in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00007ffff7f8bf53 in pthread_mutex_lock () from /lib64/libpthread.so.0
#2 0x0000555555578dbd in std::sys::unix::mutex::Mutex::lock (self=0x5555555f3ed0)
at /home/evgen/.rustup/toolchains/1.47.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/unix/mutex.rs:63
#3 0x0000555555563f8a in std::sys_common::mutex::Mutex::raw_lock (self=0x5555555f3ed0)
at /home/evgen/.rustup/toolchains/1.47.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/mutex.rs:42
#4 0x0000555555571d26 in std::sync::mutex::Mutex<T>::lock (self=0x5555555f3f90)
at /home/evgen/.rustup/toolchains/1.47.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sync/mutex.rs:269
#5 0x0000555555578048 in <fern::log_impl::File as log::Log>::log::{{closure}} (record=0x7fffffffc3b8)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:549
#6 0x0000555555577eb1 in fern::log_impl::fallback_on_error (record=0x7fffffffc3b8, log_func=...)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:733
#7 <fern::log_impl::File as log::Log>::log (self=0x5555555f3f90, record=0x7fffffffc3b8)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:535
#8 0x0000555555575e44 in <fern::log_impl::Output as log::Log>::log (self=0x5555555f3f88, record=0x7fffffffc3b8)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:326
#9 0x00005555555763a4 in fern::log_impl::Dispatch::finish_logging (self=0x5555555f1e30, record=0x7fffffffc3b8)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:427
#10 0x00005555555767f1 in fern::log_impl::FormatCallback::finish (self=..., formatted_message=...)
at /home/evgen/.cargo/registry/src/github.com-1ecc6299db9ec823/fern-0.6.0/src/log_impl.rs:487
The log crate allows the use of targets, which is useful to add information about the origin of a log message, but it seems fern
provides no facility for reading targets from DispatchConfig
. Could this be added?
https://doc.rust-lang.org/log/log/index.html
A log request consists of a target, a level, and a body. A target is a string which defaults to the module path of the location of the log request, though that default may be overridden. Logger implementations typically use the target to filter requests based on some user configuration.
An example from this page:
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
As of rust-lang/log#324, there's unstable support for key/value pairs in logging in log
. For more information, see the tracking issue.
We probably want some amount of builtin support for key/value pairs in fern
. It's unclear, though, exactly what form this support should take.
Some initial ideas:
A more invasive setup could be:
KeyValueDispatch
dispatch type
(arguments, record)
to key/value pairs
Dispatch
to KeyValueDispatch
, of course each giving/accepting a KeyValueDispatch
insteadformat
with map
- a general kv map with the same principlesoutput
's single argument with multiple: one specifying a format (like newline-separated JSON), the other specifying where to write itI'm not committed to any of these, this is just initial brainstorming.
We'll probably want to look at what one can do with key/value pairs in logging configuration in slog
too, as they've explored this problem space a lot more than we have.
Hello!
How can I chain vec of fern::Dispatch ?
let mut logger_base = Dispatch::new();
logger_base.level(log_level);
for logger in loggers.iter(){
logger_base.chain(logger)
}
logger_base.apply()?;
I am struggling with this for a whole day
Fern is currently nice to configure, but might be a bit verbose for someone just wanting a CLI logger. It would be nice if we had some easy to use recipes for common use cases.
While this would be beneficial, I don't think adding additional duplicated functionality to fern
itself would be a good idea. Instead, a crate fern-recipes
could be created, containing a bunch of helper functions, each with two variations: returning std::io::Result<fern::Logger>
, and returning Result<(), fern::InitError>
.
If anyone has a better idea on how to include these recipes, or wants to start making some, feel free to chime in below.
Hi,
It would be great if tests could be written to the tests's stdout, and then (by default) shown only if the test fails.
It looks like .chain(std::io::stdout())
sends them to the process's real stdout, bypassing the test framework's hooks. I see there's a lot of history on this in rust-lang/rust#42474 and perhaps it's not possible for Fern to fix? But it seems like if println!
is captured, at least Fern could simply do println!
for everything.
Or perhaps this is possible already and I'm just missing it, and if so perhaps it could be clearer in the docs?
Compiling on Windows with Rust stable v1.42.0 and
fern = { version = "^0.6", features = ["colored"] }
gives:
error[E0425]: cannot find function `set_virtual_terminal` in module `colored::control`
--> C:\Users\braxton\.cargo\registry\src\github.com-1ecc6299db9ec823\fern-0.6.0\src\colors.rs:161:39
|
161 | let _ = colored::control::set_virtual_terminal(true);
| ^^^^^^^^^^^^^^^^^^^^ not found in `colored::control
That section of your code is:
#[inline]
pub fn new() -> Self {
#[cfg(windows)]
{
let _ = colored::control::set_virtual_terminal(true);
}
Self::default()
}
And after looking over the colored
crate, there's indeed nothing even resembling that in that module.
Perhaps this should be colored::control::set_override(true);
?
I'm planning to use fern in the asynchronous app and non-blocking logging is vital for us.
How can I setup fern to implement async logging? Can it write logs in a separate thread or at least use mpsc::channel as an output?
Hello
I've released reopen 1.0 and intend to consider it stable (well, it didn't need any touching for 2 years, so I guess it is „complete“). And I wanted to send a pull request with update, but I'm a bit unsure about one thing.
It's turned on by the reopen-03
feature. Is it OK to remove that one and replace by a reopen-1
or do you want to support both in parallel?
Hello
I'm looking through logging options available that'd fullfill my use case. Part of it could be done with fern, but there's one small bit lacking. I'd like to plug an arbitrary Write
object into it.
Would it make sense to add another From
implementation, accepting Box<Write>
in addition to all the specific ones? I think it would increase the flexibility.
I have been using this crate without
log = "*"
inside Cargo.toml. Well, it still worked up to a point, that where rustc complained that there was no log::LogLevelFilter inside log crate. If you could add this detail to crate documentation, it would be great.
I know, there is a dependancy on log 0.2.5, but somehow I had to manually add it into Cargo.toml.
Gareins
I don't see any example of this in docs.rs.
Thanks for writing a very useful library.
It would be nice to have some examples for using fern
with colored logging.
Right now this will probably be tricky, especially because there is no LogLevel->Color mapping created.
For implementing colors, ansi_term
seems like a good crate to use. We could either build an example with this, or add it as an optional dependency to fern, and create helper functions.
for example we could use it like this:
fern::Dispatch::new()
// ...
.format(move |out, message, record| {
out.finish(format_args!(
"[{}] {}",
// if out.color, we render it with ANSI color (e.g. io::stdout())
// otherwise render it as plain text (e.g. fern::log_file)
if out.color { colors.color(record.level()) } else { record.level() },
message
))
})
I'm trying to implement JSON logging using fern, but I'm having issues with messages containing newlines.
See this example program:
fn main() {
let mut dispatch = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{{ \"message\": \"{}\" }}",
message
))
})
.chain(std::io::stdout())
.apply().unwrap();
log::info!("This is a single line and works fine");
log::info!("This is mulitple lines\n and doesn't produce valid JSON");
}
And the output:
{ "message": "This is a single line and works fine" }
{ "message": "This is mulitple lines
and doesn't produce valid JSON" }
Is there any good way of escaping the newline(s) in the message?
I've set up my logger:
fn set_up_logging() {
use fern::InitError;
use std::error::Error;
/// Sets up the global logger
fn set_up_logging_internal() -> Result<(), InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
record.level(),
record.target(),
message
))
})
.chain(std::io::stdout())
.chain(fern::log_file("my_log.log")?) // <------- see here
.apply()?;
Ok(())
}
match set_up_logging_internal() {
Ok(_) => { },
Err(e) => match e {
InitError::Io(e) => {
println!("[WARN] Logging IO init error: \r\nkind: {:?}\r\n\r\ndescription:\r\n{}\r\n\r\ncause:\r\n{:?}\r\n",
e.kind(), e.description(), e.cause());
},
InitError::SetLoggerError(e) => {
println!("[WARN] Logging initalization error: \r\ndescription:\r\n{}\r\n\r\ncause:\r\n{:?}\r\n",
e.description(), e.cause());
}
}
}
}
Then I tried to use it:
fn main() {
set_up_logging();
info!("hello world");
}
The stdout gets printed correctly, but the file is just empty. The log file is created, but not actually written to, which kind of defeats the point of a logger. Is there anything I'm doing wrong?
Switching to owo-colors may be useful for getting rid of some of the colored
specific code used currently.
Hi,
I've set up logs coloration and output, and have this:
2020/01/22 22:08:21 �[92mINFO�[0m [lucid::server] Running Lucid server on 127.0.0.1:7021 | PID: 48400
2020/01/22 22:08:21 �[92mINFO�[0m [lucid::server] Lucid API Endpoint: https://127.0.0.1:7021/api/
2020/01/22 22:08:21 �[92mINFO�[0m [lucid::server] Use Ctrl+C to stop the server.
A bit boring ^^
Current implementation here: lucid-kv/lucid#49
Kind regards
It'd be nice to be able to use fern
's manual configuration and at the same time still get an additional RUST_LOG
interface with only an additional flag.
As with #60, I'm not sure if this fits the crate. I think if we reused or reimplemented only the parsing, though, we might be able to combine configurations from inside the program and from the env variable, to allow them to benefit eachother rather than replace eachother? And if we implement it as a feature flag, then it shouldn't be too much additional overhead.
I think most of that logic applies to #60 too, so implementing both might be a good idea. We could even try to accept log4rs-compatible configuration.
On the other hand, fern's configuration is infinitely recursive, and it wouldn't be entirely clear where to stick / allow the target filters. Maybe it'd have to be a method on dispatch, "allow_from_env"?
How does this scale to the file configuration, then? Does it? Not sure.
Ideally, this would be a stdout-like color output stream which parsed ANSI color codes and transformed them into the correct output on both Windows and UNIX terminals.
Crates of note:
Ideas for implementation:
We should release a patch with:
Opening an issue for this since I'm not going to address it immediately, but it will be done. If you're from the far future, and I've not yet gotten to this, feel free to reply & send a ping.
I'm using fern = "0.5"
in Cargo.toml
When I use the sample code in CLion IDE, the line .chain(fern::log_file("output.log")?)
displays warning mismatched types expected Result<File, Error>, found File
When I go modify the Fern library file fern-0.5.5/src/lib.rs and change the signature from pub fn log_file<P: AsRef<Path>>(path: P) -> io::Result<File> {
to pub fn log_file<P: AsRef<Path>>(path: P) -> File {
then the warning disappears, and the implementation still outputs to the log file and terminal successfully.
I wish to change the log level in runtime without restarting my long-running service. Is there a way of doing so?
Hello
I've managed to get the log_enabled out of sync from what is actually logged in the end (this is a small reproducer, but I've actually hit it in production where it gives me some trouble).
use log::{debug, info, log_enabled};
fn main() {
let dummy = fern::Dispatch::new()
.level(log::LevelFilter::Warn)
.chain(std::io::stderr());
let stdout = fern::Dispatch::new()
.level(log::LevelFilter::Info)
.level_for("abc", log::LevelFilter::Debug)
.chain(std::io::stdout());
fern::Dispatch::new()
.chain(stdout)
.chain(dummy)
.apply()
.unwrap();
info!("Hello world");
debug!("Should not show");
dbg!(log_enabled!(log::Level::Debug));
}
The output is this:
Hello world
[src/main.rs:23] log_enabled!(log :: Level :: Debug) = true
So, it doesn't log the debug message, but says log is enabled for debug.
Is this on purpose (not chasing through the slave Dispatches because of performance), or just some kind of missed thing?
Currently I am using this snippet to setup fern:
fn setup_logger() {
let logger_config = fern::DispatchConfig {
format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
let t = time::now();
let ms = t.tm_nsec/1000_000;
let path = _location.__module_path;
let line = _location.__line;
format!("{}.{:3} [{}] {} {}: {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, path, line, msg)
}),
output: vec![fern::OutputConfig::stderr()],
level: log::LogLevelFilter::Trace,
};
if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
panic!("Failed to initialize global logger: {}", e);
}
}
It sets up logging globally, however is it possible to setup different loglevels for different threads.
For example I would like that module mqtt::client
would log only error messages. Is there possibility to do that?
If I activate coloring of the log levels I end up with the color escape codes in my file as well. Is there any way to make fern
log to stdout and to a file but only color stdout?
If there's another kind of backend that we don't support, it should be easy to add a shim in any user crate. This is for things which aren't Log
either, so they don't fall into the Box<Log>
handler.
I think this would be best done via an Output::other
method like:
fn other<F>(handler: F) -> Self
where
F: FnMut(log::Record)
It would be usable like:
fern::Dispatch::new()
.chain(fern::Output::other(|record| actual_log(record)))
On the implementation side, we could use the existing implementations for Box<Log>
.
The Output::other
would have a function-local struct Shim<F: FnMut(log::Record)>(F);
implementing log::Log
.
Then the method can just return (Box::new(Shim(handler)) as Box<Log>).into()
.
https://dabo.guru/rust/fern/fern/
404 Not Found
The requested resource was not found on the server.
Link to home
In fern 0.6.0
, the log_reopen
function depends on the reopen-03
feature to be enabled, which it isn't by default. This isn't documented, and it results in a pretty confusing error message about the function not existing in a typical usage scenario.
Could not comile datebased log example
At windows os,
Error : Could not find DateBasedLogFile in fern
There are two crates syslog
and sysly
neither of them integrates either with log
or fern
. Any plans on this?
Should syslog logger be in fern? Should it be under feature flag?
Should be fairly straightforward, we can do the same thing we did for the syslog3/4 distinction for adding support for syslog 5. Main work is in rewriting full examples to use syslog 5.
There were some things that syslog 3 could do, but syslog 4 couldn't. I can't remember off the top of my head what they were. If syslog 5 fixes these, and is at least as capable as both syslog 3 and 4, we can remove syslog 3/4 examples and leave only actual library code, replacing them entirely with syslog 5 examples.
I want it to log into logs/mm-dd-yyyy.log
, but if the program is online for more than a day this will become outdated, how would I make it automatically switch to the next day's file?
[2019-09-29 22:37:40][�[32mINFO �[0m][test] Hello, world! [2019-09-29 22:37:40][�[33mWARN �[0m][test] Hello, world! [2019-09-29 22:37:40][�[31mERROR�[0m][test] Hello, world!
Is there a way to prevent fern from writing color codes into files?
The link to the documentation is broken. Would be simple enough to create a gh-pages branch and just use rustdoc?
I'm trying to use fern with one of the examples in the repo:
// ...
extern crate log;
extern crate fern;
// ...
use fern::colors::{Color, ColoredLevelConfig};
use log::{debug, error, info, trace, warn};
fn main() {
setup_logger();
//...
}
fn setup_logger() {
let colors_line = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
.info(Color::White)
.debug(Color::White)
.trace(Color::BrightBlack);
let colors_level = colors_line.clone().info(Color::Green);
fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{color_line}[{date}][{target}][{level}{color_line}] {message}\x1B[0m",
color_line = format_args!(
"\x1B[{}m",
colors_line.get_color(&record.level()).to_fg_str()
),
date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
target = record.target(),
level = colors_level.color(record.level()),
message = message,
));
})
.level(log::LevelFilter::Warn)
.level_for("pretty_colored", log::LevelFilter::Trace)
.chain(std::io::stdout())
.apply()
.unwrap();
debug!("finished setting up logging! yay!");
}
This is the output of cargo build:
Compiling piko v0.1.0 (/home/lyubentodorov/Projects/piko)
error[E0432]: unresolved import `fern::colors`
--> src/bin/main.rs:33:11
|
33 | use fern::colors::{Color, ColoredLevelConfig};
| ^^^^^^ could not find `colors` in `fern`
warning: unused imports: `error`, `trace`, `warn`
--> src/bin/main.rs:34:18
|
34 | use log::{debug, error, info, trace, warn};
| ^^^^^ ^^^^^ ^^^^
|
= note: `#[warn(unused_imports)]` on by default
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0432`.
error: could not compile `piko`.
To learn more, run the command again with --verbose.
And these are my dependancies:
[dependencies]
rand = "0.7.3"
config = "0.10.1"
rayon = "1.3.1"
sha2 = "0.9.1"
byteorder = "1.3.4"
bytes = "0.5.6"
num = "0.3.0"
num-traits = "0.2.12"
num-derive = "0.3.1"
serde = { version = "1.0.115", features = ["derive"] }
serde_cbor = "0.11.1"
clokwerk = "0.3.3"
lazy_static = "1.4.0"
log = "0.4"
fern = "0.5"
chrono = "0.4.15"
I've tried with a couple of versions of log and fern, including the latest ones. I can't see what I'm doing wrong and I couldn't find similar issues anywhere.
Having line numbers like other languages would be nice to locate error when debugging, but I haven't seen any mention about line numbers.
Isn't it supported? (seems no "line" in the codebase)
syslog 4 provides support for formatting messages in RFC 3164 or RFC 5424 format
Having line numbers like other languages would be nice to locate error when debugging, but I haven't seen any mention about line numbers.
Isn't it supported? (seems no "line" in the codebase)
I think it would be sane to add an option for a simple log file which removes old records once it reaches 16 megabytes, or a similar size. This would be useful for sane command line applications which might want to output much more debug logging, and now wouldn't have to worry about cleaning it up.
This would need to be implemented efficiently though, so more knowledge on ways to do this in Linux and Windows is needed.
Hello,
It would be nice to also log panics if possible.
Now it seems the panics are only visible on the stderr of the application.
I found something which kinda does this, https://github.com/sfackler/rust-log-panics but if it could be integrated into fern that would be great.
like after an hour
It'd be very nice to panic!
instead of trying writing something when we are running tests. Could we have an option which specified what to do in error!
or warn!
handlers? Panicking in the tests very useful because we may see some problems we may have. Personally I provide two kind of critical functions - one uses error!
and another one uses panic!
for showing me why my test fails.
Hi, I'm interested in using fern logging in forked subprocesses, however, I don't see a way to uninstall the parent process's global logger and install a child process logger for the subprocess. What is the recommendation here?
i use fern for logging things in my integration tests. Lets say I only use one blanket LevelFilter
for all modules (though this is not the case). I'd love to be able to send everything LevelFilter::Info
and above to my log file, then restrict it further to everything LevelFilter::Warn
and above to send to STDOUT
. Is there already a way to do this, and if not, could it be considered as an enhancement?
This is my current configuration for fern, file_out
logs with the correct level_width
of 8, but term_out
completely ignores the width.
I have also tested to see if it follows alignment formatting marks, and it does not.
let colors = ColoredLevelConfig::new()
.trace(Color::Magenta)
.debug(Color::Cyan)
.info(Color::Green)
.warn(Color::Yellow)
.error(Color::Red);
let term_out = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{time} {level:level_width$}{target:target_width$}\t> {msg}",
time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
level = colors.color(record.level()),
target = record.target(),
msg = message,
level_width = 8,
target_width = 60
))
})
.chain(std::io::stdout())
.into_shared();
let file_out = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{time} {level:level_width$}{target:target_width$}\t> {msg}",
time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
level = record.level(),
target = record.target(),
msg = message,
level_width = 8,
target_width = 60
))
})
.chain(fern::log_file("output.log").expect("Unable to access log file"))
.into_shared();
fern::Dispatch::new()
.level(log::LevelFilter::Info)
.level_for("momiji", log::LevelFilter::Debug)
.chain(term_out)
.chain(file_out)
.apply().expect("Failed to apply fern settings");
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.