Skip to content

Instantly share code, notes, and snippets.

@leiless
Created June 30, 2023 07:16
Show Gist options
  • Save leiless/42d074b997142a1d065aae39a94c3d35 to your computer and use it in GitHub Desktop.
Save leiless/42d074b997142a1d065aae39a94c3d35 to your computer and use it in GitHub Desktop.
Rust: log + env_logger + color-backtrace
use std::io::Write;
struct FileStream {
file: std::fs::File,
}
impl FileStream {
fn new<S: AsRef<std::path::Path>>(s: S) -> std::io::Result<Self> {
//let file = std::fs::File::create(s)?;
let file = std::fs::File::options().create(true).append(true).open(s)?;
Ok(Self {
file,
})
}
}
impl std::io::Write for FileStream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.file.flush()
}
}
impl color_backtrace::termcolor::WriteColor for FileStream {
fn supports_color(&self) -> bool {
false
}
fn set_color(&mut self, _spec: &color_backtrace::termcolor::ColorSpec) -> std::io::Result<()> {
Ok(())
}
fn reset(&mut self) -> std::io::Result<()> {
Ok(())
}
}
// log = "0.4"
// env_logger = "0.10"
// chrono = "0.4"
// color-backtrace = "0.5"
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file_name = "app.log";
let target = Box::new(std::fs::File::create(file_name).expect("Can't create file"));
env_logger::Builder::new()
.target(env_logger::Target::Pipe(target))
.filter(None, log::LevelFilter::Trace)
.format(|buf, record| {
writeln!(
buf,
"[{} {:>5} {}:{}] {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f %z"),
record.level(),
record.file().unwrap_or("unknown"),
record.line().unwrap_or(0),
record.args()
)
})
.init();
log::debug!("Debug");
log::info!("Info");
log::warn!("Warn");
log::trace!("Trace");
log::error!("Error");
color_backtrace::BacktracePrinter::new()
.message("\n[CRITICAL] The application panicked (crashed)")
.install(FileStream::new(file_name)?);
// Test
assert!(false);
Ok(())
}
@leiless
Copy link
Author

leiless commented Jun 30, 2023

struct FileStream {
    file: std::fs::File,
}

impl FileStream {
    fn new<S: AsRef<std::path::Path>>(s: S) -> std::io::Result<Self> {
        //let file = std::fs::File::create(s)?;
        let file = std::fs::File::options().create(true).append(true).open(s)?;
        Ok(Self {
            file,
        })
    }
}

impl std::io::Write for FileStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.file.flush()
    }
}

impl color_backtrace::termcolor::WriteColor for FileStream {
    fn supports_color(&self) -> bool {
        false
    }

    fn set_color(&mut self, _spec: &color_backtrace::termcolor::ColorSpec) -> std::io::Result<()> {
        Ok(())
    }

    fn reset(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

// log = "0.4"
// log4rs = "1.2"
// color-backtrace = "0.5"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_name = "app.log";

    // https://docs.rs/log4rs/1.2.0/log4rs/encode/pattern/index.html
    let logfile = log4rs::append::file::FileAppender::builder()
        .encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new("[{d(%Y-%m-%d %H:%M:%S%.3f %z)} {t}:{T} {l:>5} {f}:{L:0>3}] {m}{n}")))
        .append(false)
        .build(file_name)?;
    let appender_name = "dummy";

    let config = log4rs::config::Config::builder()
        .appender(log4rs::config::Appender::builder().build(appender_name, Box::new(logfile)))
        .build(log4rs::config::Root::builder()
            .appender(appender_name)
            .build(log::LevelFilter::Trace))?;

    log4rs::init_config(config)?;

    color_backtrace::BacktracePrinter::new()
        .message("\n[CRITICAL] The application panicked (crashed)")
        .install(FileStream::new(file_name)?);

    // Tests

    log::debug!("Debug");
    log::info!("Info");
    log::warn!("Warn");
    log::trace!("Trace");
    log::error!("Error");

    assert!(false);

    Ok(())
}
[2023-06-30 15:47:00.318 +0800 rust_test:main DEBUG src/main.rs:066] Debug
[2023-06-30 15:47:00.318 +0800 rust_test:main  INFO src/main.rs:067] Info
[2023-06-30 15:47:00.318 +0800 rust_test:main  WARN src/main.rs:068] Warn
[2023-06-30 15:47:00.318 +0800 rust_test:main TRACE src/main.rs:069] Trace
[2023-06-30 15:47:00.318 +0800 rust_test:main ERROR src/main.rs:070] Error

[CRITICAL] The application panicked (crashed)
Message:  assertion failed: false
Location: src/main.rs:72

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                              ⋮ 8 frames hidden ⋮                               
 9: core::panicking::panic::h9533b2fee90b999e
    at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/panicking.rs:114
10: rust_test::main::h907124e8f59547f2
    at /home/foo/CLionProjects/rust-test/src/main.rs:72
      70 │     log::error!("Error");
      71 │ 
      72 >     assert!(false);
      73 │ 
      74 │     Ok(())
11: core::ops::function::FnOnce::call_once::h9c3256714700dc1e
    at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/ops/function.rs:250
                              ⋮ 16 frames hidden ⋮

@leiless
Copy link
Author

leiless commented Jun 30, 2023

log4rs + RollingFileAppender solution

struct FileStream {
    file: std::fs::File,
}

impl FileStream {
    fn new<S: AsRef<std::path::Path>>(s: S) -> std::io::Result<Self> {
        //let file = std::fs::File::create(s)?;
        let file = std::fs::File::options().create(true).append(true).open(s)?;
        Ok(Self {
            file,
        })
    }
}

impl std::io::Write for FileStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.file.flush()
    }
}

impl color_backtrace::termcolor::WriteColor for FileStream {
    fn supports_color(&self) -> bool {
        false
    }

    fn set_color(&mut self, _spec: &color_backtrace::termcolor::ColorSpec) -> std::io::Result<()> {
        Ok(())
    }

    fn reset(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

// log = "0.4"
// log4rs = "1.2"
// color-backtrace = "0.5"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_name = "app.log";

    let max_rolling_file = 3;
    let fixed_window_roller =
        log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller::builder().build(&format!("{}.{{}}", file_name), max_rolling_file)?;

    let max_log_size_in_bytes = 1024 * 1024;
    let size_trigger = log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger::new(max_log_size_in_bytes);

    let compound_policy =
        log4rs::append::rolling_file::policy::compound::CompoundPolicy::new(Box::new(size_trigger), Box::new(fixed_window_roller));

    let logfile = log4rs::append::rolling_file::RollingFileAppender::builder()
        // https://docs.rs/log4rs/1.2.0/log4rs/encode/pattern/index.html
        .encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new("[{d(%Y-%m-%d %H:%M:%S%.3f %z)} {t}:{T} {l:>5} {f}:{L:0>3}] {m}{n}")))
        .build(file_name, Box::new(compound_policy))?;

    let appender_name = "dummy";

    let config = log4rs::config::Config::builder()
        .appender(log4rs::config::Appender::builder().build(appender_name, Box::new(logfile)))
        .build(log4rs::config::Root::builder()
            .appender(appender_name)
            .build(log::LevelFilter::Trace))?;

    log4rs::init_config(config)?;

    color_backtrace::BacktracePrinter::new()
        .message("\n[CRITICAL] The application panicked (crashed)")
        .install(FileStream::new(file_name)?);

    // Tests

    log::debug!("Debug");
    log::info!("Info");
    log::warn!("Warn");
    log::trace!("Trace");
    log::error!("Error");

    assert!(false);

    Ok(())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment