1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fmt::Display;

use ftlog::{
    appender::{file::Period, FileAppender},
    info, FtLogFormat,
};
use log::{Level, LevelFilter, Record};
use time::Duration;
fn init() {
    // Custom log style.

    // A formatter defines how to build a message.
    // Since Formatting message into string can slow down the log macro call,
    // the idomatic way is to send required field as is to log thread, and build
    // message in log thread.
    struct MyFormatter;
    impl FtLogFormat for MyFormatter {
        fn msg(&self, record: &Record) -> Box<dyn Send + Sync + std::fmt::Display> {
            Box::new(Msg {
                level: record.level(),
                thread: std::thread::current().name().map(|n| n.to_string()),
                file: record.file_static(),
                line: record.line(),
                args: format!("{}", record.args()),
                module_path: record.module_path_static(),
            })
        }
    }

    // Store necessary field, define how to build into string with `Display` trait.
    struct Msg {
        level: Level,
        thread: Option<String>,
        file: Option<&'static str>,
        line: Option<u32>,
        args: String,
        module_path: Option<&'static str>,
    }

    impl Display for Msg {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.write_str(&format!(
                "{}@{}||{}:{}[{}] {}",
                self.thread.as_ref().map(|x| x.as_str()).unwrap_or(""),
                self.module_path.unwrap_or(""),
                self.file.unwrap_or(""),
                self.line.unwrap_or(0),
                self.level,
                self.args
            ))
        }
    }

    let time_format = time::format_description::parse_owned::<1>(
        "[year]/[month]/[day] [hour]:[minute]:[second].[subsecond digits:6]",
    )
    .unwrap();
    ftlog::Builder::new()
        // use our own format
        .format(MyFormatter)
        // use our own time format
        .time_format(time_format)
        // global max log level
        .max_log_level(LevelFilter::Info)
        // define root appender, pass None would write to stderr
        .root(FileAppender::rotate_with_expire(
            "./current.log",
            Period::Day,
            Duration::days(7),
        ))
        // ---------- configure additional filter ----------
        // write to "ftlog-appender" appender, with different level filter
        .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error)
        // write to root appender, but with different level filter
        .filter("ftlog", None, LevelFilter::Trace)
        // write to "ftlog" appender, with default level filter
        .filter("ftlog::appender::file", "ftlog", None)
        // ----------  configure additional appender ----------
        // new appender
        .appender("ftlog-appender", FileAppender::new("ftlog-appender.log"))
        // new appender, rotate to new file every Day
        .appender("ftlog", FileAppender::rotate("ftlog.log", Period::Day))
        .try_init()
        .expect("logger build or set failed");
}

fn main() {
    init();
    info!("Hello, world!");
    for i in 0..120 {
        info!("running {}!", i);
        info!(limit=3000i64; "limit running{} !", i);
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
    log::logger().flush();
}

/*
Output:

2022/11/11 13:53:13.933123 0ms logger@ftlog||src/lib.rs:439[WARN] Logs with level more verbose than INFO will be ignored in `ftlog`
2022/11/11 13:53:13.933123 0ms main@complex||examples/complex.rs:83[INFO] Hello, world!
2022/11/11 13:53:13.934123 1ms main@complex||examples/complex.rs:85[INFO] running 0!
2022/11/11 13:53:13.934123 3ms 0 main@complex||examples/complex.rs:86[INFO] limit running0 !
2022/11/11 13:53:13.934123 3ms logger@ftlog::appender::file||src/appender/file.rs:255[INFO] Log file deleted: current-20221111T1352.log
2022/11/11 13:53:14.939123 0ms main@complex||examples/complex.rs:85[INFO] running 1!
2022/11/11 13:53:15.939123 0ms main@complex||examples/complex.rs:85[INFO] running 2!
2022/11/11 13:53:16.943123 0ms main@complex||examples/complex.rs:85[INFO] running 3!
2022/11/11 13:53:16.943123 0ms 2 main@complex||examples/complex.rs:86[INFO] limit running3 !
2022/11/11 13:53:17.945123 0ms main@complex||examples/complex.rs:85[INFO] running 4!
2022/11/11 13:53:18.946123 0ms main@complex||examples/complex.rs:85[INFO] running 5!
2022/11/11 13:53:19.951123 0ms main@complex||examples/complex.rs:85[INFO] running 6!
2022/11/11 13:53:19.951123 0ms 2 main@complex||examples/complex.rs:86[INFO] limit running6 !
2022/11/11 13:53:20.956123 0ms main@complex||examples/complex.rs:85[INFO] running 7!
2022/11/11 13:53:21.961123 0ms main@complex||examples/complex.rs:85[INFO] running 8!
2022/11/11 13:53:22.966123 0ms main@complex||examples/complex.rs:85[INFO] running 9!
2022/11/11 13:53:22.966123 4ms 2 main@complex||examples/complex.rs:86[INFO] limit running9 !


Default style example:
2022-04-11 15:08:19.847+08 0ms INFO main@src/main.rs:25 Hello, world!
2022-04-11 15:08:19.847+08 0ms INFO main@src/main.rs:28 running 0!
2022-04-11 15:08:19.847+08 0ms 0 INFO main@src/main.rs:29 limit running0 !
2022-04-11 15:08:20.849+08 0ms INFO main@src/main.rs:28 running 1!
2022-04-11 15:08:21.852+08 0ms INFO main@src/main.rs:28 running 2!
2022-04-11 15:08:22.857+08 0ms INFO main@src/main.rs:28 running 3!
2022-04-11 15:08:22.857+08 0ms 2 INFO main@src/main.rs:29 limit running3 !
2022-04-11 15:08:23.862+08 0ms INFO main@src/main.rs:28 running 4!
2022-04-11 15:08:24.864+08 0ms INFO main@src/main.rs:28 running 5!
 */