Dynamically set file path in log4rs

Viewed 781

I already asked this question in the Rust subreddit but wanted to ask it here too.

I'm using the log4rs crate and want to find a way to generate more than one log file. I have a YAML file set up with the file appender created, and am trying to have the path be unique so it doesn't have to either append or truncate the original file.

appenders:
  file:
    kind: file
    path: "log/{h({d(%H:%M:%S)})}.log"

But this does not work and gives me this error:

log4rs: error deserializing appender file: The filename, directory name, or volume label syntax is incorrect. (os error 123)

I know that log4rs has a way to do patterns but it doesn't seem to work specifically for the path parameter.

I also saw this other crate called log4rs_routing_appender which looks promising but I don't know if I will need to use that.

Finally, I want to be able to do this non-programmatically (i.e. only with one YAML file), and am wondering if it's possible within log4rs

Thanks a lot!

2 Answers

I do not believe what you want is possible with yaml configuration. However, log4rs provides another way to build it's logger, which is through log4rs::Config::builder():

// get current date
let date = chrono::Utc::now();

// create log file appender
let logfile = FileAppender::builder()
  .encoder(Box::new(PatternEncoder::default()))
  // set the file name based on the current date
  .build(format!("log/{}.log", date))
  .unwrap();

// add the logfile appender to the config
let config = Config::builder()
  .appender(Appender::builder().build("logfile", Box::new(logfile)))
  .build(Root::builder().appender("logfile").build(LevelFilter::Info))
  .unwrap();

// init log4rs
log4rs::init_config(config).unwrap();
log::info!("Hello, world!");
Ok(())

I understand that you want to use YAML configuration. However, as you said, patterns do not seem to work with the path variable, seeing as writing this fails:

path:
  pattern: "log/requests-{d}-{m}-{n}.log"

Another option would be to manually parse the yaml with serde_yaml (which log4rs actually uses internally) and parse custom variables with regex.

I realize the rolling_file type make it so that it automatically increments numbers to the log names! This is the example of what I did.

appenders:
  default:
    kind: console
    encoder:
      kind: pattern
      pattern: "{h({d(%H:%M:%S)})} - {m}{n}"
  log_file:
    kind: rolling_file
    append: true
    path: "logs/log.log"
    encoder:
      pattern: "{h({d(%m-%d-%Y %H:%M:%S)})} - {m}{n}"
    policy:
      kind: compound
      trigger:
        kind: size
        limit: 10mb
      roller:
        kind: fixed_window
        base: 1
        count: 100
        pattern: "logs/log{}.log"

root:
  level: info
  appenders:
    - default
    - log_file

This generates log{}.log (replace {} with incrementing numbers) files within the logs folder after the file reaches 10MB of size. Since I set append: true the log file will keep accumulating until it reaches the size limit.

Hopefully this helps others too!

Related