puffin/
log.rs

1use std::env;
2use std::path::Path;
3use std::str::FromStr;
4
5use log::LevelFilter;
6use log4rs::append::console::ConsoleAppender;
7use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
8use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
9use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
10use log4rs::append::rolling_file::RollingFileAppender;
11use log4rs::config::{load_config_file, Appender, Logger, Root};
12use log4rs::encode::pattern::PatternEncoder;
13use log4rs::filter::threshold::ThresholdFilter;
14use log4rs::Config;
15
16pub fn config_default(base_directory: &Path) -> Config {
17    Config::builder()
18        .appender(appender_stderr("stderr"))
19        .appender(appender_tofile(
20            "tofile",
21            base_directory.join("stats_puffin_main_broker.log"),
22        ))
23        .build(
24            Root::builder()
25                .appender("stderr")
26                .appender("tofile")
27                .build(log_level()),
28        )
29        .unwrap()
30}
31
32pub fn load_fuzzing_client() -> Config {
33    load_config_file("client_log_config.yml", Default::default()).unwrap()
34}
35
36fn appender_stderr<S>(name: S) -> Appender
37where
38    S: AsRef<str>,
39{
40    Appender::builder().build(
41        name.as_ref(),
42        Box::new(
43            ConsoleAppender::builder()
44                .target(log4rs::append::console::Target::Stderr)
45                .encoder(Box::new(PatternEncoder::new(
46                    "{h({d(%Y-%m-%dT%H:%M:%S%Z)}\t{m}{n})}",
47                )))
48                .build(),
49        ),
50    )
51}
52
53fn appender_tofile<S, P>(name: S, log_path: P) -> Appender
54where
55    S: AsRef<str>,
56    P: AsRef<Path>,
57{
58    let window_size = 20; // log0, log1, log2, .., log19
59    let fixed_window_roller = FixedWindowRoller::builder()
60        .build("log{}", window_size)
61        .unwrap();
62    let size_limit = 100 * 1024 * 1024; // 100MB as max log file size to roll
63    let size_trigger = SizeTrigger::new(size_limit);
64    let compound_policy =
65        CompoundPolicy::new(Box::new(size_trigger), Box::new(fixed_window_roller));
66
67    Appender::builder().build(
68        name.as_ref(),
69        Box::new(
70            RollingFileAppender::builder()
71                .encoder(Box::new(PatternEncoder::new("{d}\t{l}\t{m}{n}")))
72                .build(log_path, Box::new(compound_policy))
73                .unwrap(),
74        ),
75    )
76}
77
78fn log_level() -> LevelFilter {
79    // TODO allow fined-grain configuration of the log level
80    //
81    // At least for:
82    //   - libafl* during fuzzing
83    //   - tlspuffin's packages
84    //   - tlspuffin's packages during fuzzing (libafl client)
85    //
86    // [MM] Maybe also allow package-level control like env_logger?
87    env::var("RUST_LOG")
88        .ok()
89        .and_then(|level| LevelFilter::from_str(&level).ok())
90        .unwrap_or(LevelFilter::Info)
91}
92
93pub fn set_experiment_fuzzing_client(base_directory: &Path, level_filter: LevelFilter) -> Config {
94    // Common encoder
95    let encoder = Box::new(PatternEncoder::new("{d}\t{l}\t{m}{n}"));
96
97    // Console appender
98    let stdout = ConsoleAppender::builder().build();
99
100    // Helper to build a rolling file appender
101    let max_log_size = 10 * 1024 * 1024; // 10 MB
102    let build_rolling_appender = |path: &str, pattern: &str, filter: Option<LevelFilter>| {
103        let trigger = Box::new(SizeTrigger::new(max_log_size));
104        let roller = Box::new(
105            FixedWindowRoller::builder()
106                .base(1)
107                .build(
108                    &format!("{}{}", base_directory.to_str().unwrap(), pattern),
109                    20,
110                )
111                .unwrap(),
112        );
113
114        let policy = Box::new(CompoundPolicy::new(trigger, roller));
115        let rfa = RollingFileAppender::builder()
116            .encoder(encoder.clone())
117            .build(base_directory.join(path), policy)
118            .unwrap();
119        if let Some(filter) = filter {
120            Appender::builder()
121                .filter(Box::new(ThresholdFilter::new(filter)))
122                .build(path, Box::new(rfa))
123        } else {
124            Appender::builder().build(path, Box::new(rfa))
125        }
126    };
127
128    let fuzzer_log = build_rolling_appender("fuzzer.log", "fuzzer.{}.gz", None);
129    let harness_log = build_rolling_appender("harness.log", "harness.{}.gz", None);
130    let info_log = build_rolling_appender("info.log", "info.{}.gz", Some(LevelFilter::Info));
131    let warn_log = build_rolling_appender("warn.log", "warn.{}.gz", Some(LevelFilter::Warn));
132    let error_log = build_rolling_appender("error.log", "error.{}.gz", Some(LevelFilter::Error));
133
134    let config = Config::builder()
135        // appenders
136        .appender(Appender::builder().build("stdout", Box::new(stdout)))
137        .appender(fuzzer_log)
138        .appender(harness_log)
139        .appender(info_log)
140        .appender(warn_log)
141        .appender(error_log);
142
143    let mut root_appenders = vec!["info.log", "warn.log", "error.log"];
144    // Specific appenders and loggers for verbose modes (when RUST_LOG is set strictly more verbose
145    // than INFO)
146    let config = if level_filter >= LevelFilter::Debug {
147        root_appenders.push("debug.log");
148        let debug_log =
149            build_rolling_appender("debug.log", "debug.{}.gz", Some(LevelFilter::Debug));
150        config.appender(debug_log).logger(
151            Logger::builder()
152                .appenders(vec!["debug.log"])
153                .additive(true)
154                .build("debug", LevelFilter::Debug),
155        )
156    } else {
157        config
158    };
159
160    let config = if level_filter >= LevelFilter::Trace {
161        root_appenders.push("trace.log");
162        let trace_log =
163            build_rolling_appender("trace.log", "trace.{}.gz", Some(LevelFilter::Trace));
164        config.appender(trace_log).logger(
165            Logger::builder()
166                .appenders(vec!["trace.log"])
167                .additive(true)
168                .build("trace", LevelFilter::Trace),
169        )
170    } else {
171        config
172    };
173
174    // loggers
175    config
176        .logger(Logger::builder()
177            .appenders(vec!["harness.log"])
178            .additive(true)
179            .build("puffin::harness", level_filter)) // Change to `Trace` for full verbosity
180        .logger(Logger::builder()
181            .appenders(vec!["fuzzer.log"])
182            .additive(true)
183            .build("puffin::fuzzer", level_filter))
184        // Root logger
185        .build(
186            Root::builder()
187                .appenders(root_appenders)
188                .build(log_level()),
189        )
190        .unwrap()
191}