Skip to main content

custom_appender/
custom_appender.rs

1//! Demonstrates the `Appender`/`Layout` extensibility: a custom layout that
2//! formats events as `key=value` pairs, and a custom appender that collects
3//! formatted messages into a shared in-memory buffer.
4
5use std::sync::{Arc, Mutex};
6
7use log4rc::{Appender, Layout, LogEvent, LogLevel, Logger};
8
9/// A layout that formats events as "key=value" pairs.
10struct KeyValueLayout;
11
12impl Layout for KeyValueLayout {
13    fn format(&self, event: &LogEvent) -> String {
14        format!("level={} message={}", event.level.as_str(), event.message)
15    }
16}
17
18/// An appender that collects formatted messages into a shared buffer.
19#[derive(Clone)]
20struct VecAppender {
21    layout: Arc<dyn Layout>,
22    messages: Arc<Mutex<Vec<String>>>,
23}
24
25impl VecAppender {
26    fn new(layout: Arc<dyn Layout>) -> Self {
27        Self {
28            layout,
29            messages: Arc::new(Mutex::new(Vec::new())),
30        }
31    }
32
33    fn messages(&self) -> Vec<String> {
34        self.messages.lock().unwrap().clone()
35    }
36}
37
38impl Appender for VecAppender {
39    fn append(&self, event: &LogEvent) {
40        self.messages.lock().unwrap().push(self.layout.format(event));
41    }
42}
43
44fn main() {
45    let collector = VecAppender::new(Arc::new(KeyValueLayout));
46
47    // Build a logger with a custom appender and install it as the global logger.
48    Logger::builder(LogLevel::Info)
49        .appender(Box::new(collector.clone()))
50        .init();
51
52    log4rc::info!("Hello, appender!");
53    log4rc::debug!("This is filtered out at Info level");
54    log4rc::error!("Something went wrong");
55
56    // Only the Info and Error messages made it through the level filter.
57    for line in collector.messages() {
58        println!("captured: {line}");
59    }
60}