├── examples ├── immediate_log │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── flashlog │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── Cargo.lock ├── ftlog │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── slog │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── fast_log │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── Cargo.lock ├── tracing │ ├── Cargo.toml │ └── src │ │ └── main.rs └── fern │ ├── Cargo.toml │ └── src │ └── main.rs ├── .gitignore ├── Cargo.toml ├── CHANGELOG.md ├── src ├── timer.rs ├── rolling_file.rs ├── lib.rs ├── logger_v2.rs ├── compile_time.rs └── logger.rs └── README.md /examples/immediate_log/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-immediate-log" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | flashlog = { path = "../../" } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.log* 3 | /data 4 | /examples/ftlog/target 5 | /examples/fast_log/target 6 | /examples/flashlog/target 7 | /examples/slog/target 8 | /examples/*/target 9 | /exmaples/logs 10 | README-cache.md 11 | -------------------------------------------------------------------------------- /examples/flashlog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-flashlog" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [profile.release] 8 | lto = true 9 | 10 | 11 | [features] 12 | arr = [] 13 | i32 = [] 14 | test = [] 15 | 16 | [dependencies] 17 | flashlog = { version = "0.3.0", path = "../../", features = ["max-level-error"] } 18 | anyhow = "1.0" 19 | serde = { version = "1.0", features = ["derive"] } 20 | -------------------------------------------------------------------------------- /examples/ftlog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-ftlog" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [features] 8 | arr = [] 9 | i32 = [] 10 | 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | ftlog = "0.2" 15 | log = "0.4" 16 | flashlog = { version = "0.3", path = "../../" } 17 | time = { version = "0.3", features = ["macros", "serde", "formatting", "parsing", "local-offset"] } 18 | serde = { version = "1.0", features = ["derive"] } -------------------------------------------------------------------------------- /examples/slog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-slog" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [features] 8 | arr = [] 9 | i32 = [] 10 | 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | log = "0.4" 15 | flashlog = { version = "0.3", path = "../../" } 16 | time = { version = "0.3", features = ["macros", "serde", "formatting", "parsing", "local-offset"] } 17 | serde = { version = "1.0", features = ["derive"] } 18 | slog = "2.7" 19 | slog-term = "2.7" 20 | slog-async = "2.7" -------------------------------------------------------------------------------- /examples/fast_log/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-fast-log" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [features] 8 | default = ["arr"] 9 | arr = [] 10 | i32 = [] 11 | 12 | 13 | [dependencies] 14 | anyhow = "1.0" 15 | ftlog = "0.2" 16 | log = "0.4" 17 | fast_log = "1.7" 18 | flashlog = { version = "0.3", path = "../../" } 19 | time = { version = "0.3", features = ["macros", "serde", "formatting", "parsing", "local-offset"] } 20 | serde = { version = "1.0", features = ["derive"] } -------------------------------------------------------------------------------- /examples/tracing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tracing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [features] 8 | arr = [] 9 | i32 = [] 10 | 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | flashlog = { version = "0.3", path = "../../" } 15 | time = { version = "0.3", features = ["macros", "serde", "formatting", "parsing", "local-offset"] } 16 | serde = { version = "1.0", features = ["derive"] } 17 | tracing = "0.1" 18 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 19 | tracing-appender = "0.2" -------------------------------------------------------------------------------- /examples/fern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-fern" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [features] 8 | arr = [] 9 | i32 = [] 10 | 11 | 12 | [dependencies] 13 | flashlog = { version = "0.3", path = "../../" } 14 | anyhow = "1.0" 15 | serde = { version = "1.0", features = ["derive"] } 16 | femme = "2.2" 17 | log = { version = "0.4.14", features = [ 18 | "kv_unstable", 19 | "std", 20 | "kv_unstable_serde", 21 | ] } 22 | 23 | kv-log-macro = "1.0.5" 24 | fern = "0.6" 25 | time = "0.3" 26 | humantime = "2.1" -------------------------------------------------------------------------------- /examples/immediate_log/src/main.rs: -------------------------------------------------------------------------------- 1 | use flashlog::{Logger, LogLevel, TimeZone}; 2 | 3 | fn main() { 4 | // Create a logger with immediate flushing (0 interval) and minimal buffer size 5 | let _logger = Logger::initialize() 6 | .with_file("logs", "immediate") // Log to logs/immediate_*.log 7 | .unwrap() // Unwrap the Result 8 | .with_console_report(true) // Also output to console 9 | .with_msg_buffer_size(100) // Minimal buffer 10 | .with_msg_flush_interval(100) // Immediate flush 11 | .with_max_log_level(LogLevel::Info) 12 | .with_timezone(TimeZone::Seoul) 13 | .include_unixnano(true) 14 | .launch(); 15 | 16 | // Log some immediate messages using compile-time filtered macros 17 | flashlog::flash_info_ct!("Application started"); 18 | flashlog::flash_info_ct!(message = "Processing data", step = 1, total_steps = 10); 19 | flashlog::flash_warn_ct!("This is a warning message"); 20 | flashlog::flash_error_ct!(topic = "database_error", error_code = 500, component = "database"); 21 | 22 | // Force flush to ensure all messages are written 23 | flashlog::flush!(); 24 | 25 | println!("Immediate log example completed. Check logs/immediate_*.log"); 26 | } 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flashlog" 3 | version = "0.3.4" 4 | edition = "2021" 5 | authors = ["Junbeom Lee "] 6 | description = "A fast logging library for Rust" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/JunbeomL22/flashlog" 9 | keywords = ["Logging", "Fast", "Lazy"] 10 | 11 | [lib] 12 | name = "flashlog" 13 | path = "src/lib.rs" 14 | 15 | [dependencies] 16 | quanta = "0.12.6" 17 | once_cell = "1.21" 18 | lazy_static = "1.4" 19 | time = { version = "0.3", features = ["macros", "serde", "formatting", "parsing", "local-offset"] } 20 | chrono = { version = "0.4", features = ["serde"] } 21 | chrono-tz = "0.10" 22 | core_affinity = "0.8" 23 | crossbeam-utils = "0.8.21" 24 | crossbeam-channel = "0.5.15" 25 | serde = { version = "1.0", features = ["derive"] } 26 | serde_json = "1.0" 27 | serde_derive = "1.0" 28 | flate2 = "1.0" 29 | 30 | [dev-dependencies] 31 | anyhow = "1.0" 32 | criterion = "0.5" 33 | 34 | [workspace] 35 | members = ["examples/*"] 36 | 37 | 38 | [features] 39 | max-level-off = [] 40 | max-level-error = ["max-level-off"] 41 | max-level-warn = ["max-level-error"] 42 | max-level-info = ["max-level-warn"] 43 | max-level-debug = ["max-level-info"] 44 | max-level-trace = ["max-level-debug"] 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | ## [0.3.4] - 2025-07-31 7 | - Deafult message flush buffer size and time interval are set to zero (immediate flush 8 | - Configuration stage is in logger launching not in the definition of logger thread 9 | 10 | ## [0.3.3] - 2025-07-22 11 | - Replace expect with unwrap for crossbeam try_send and write_all operations to avoid overhead 12 | - Update flush condition logic to enable immediate flushing with zero buffer size and interval 13 | - Changed from `(buffer_size + new_msg_length > msg_buffer_size) || (current_timestamp - last_flush_time > msg_flush_interval)` 14 | - To `(buffer_size + new_msg_length >= msg_buffer_size) || (current_timestamp >= msg_flush_interval + last_flush_time)` 15 | - Update immediate_log example to use only flash_xxx_ct! macros 16 | 17 | ## [0.3.2] - 2025-07-07 18 | - crossbeam version update 19 | - replace expect with unwrap 20 | 21 | ## [0.3.1] - 2025-03-14 22 | - performance table in doc comments 23 | - `flash_xxx_ct!` macros uses only compile-time filter 24 | 25 | ## [0.3.0] - 2024-03-14 26 | - in the log macro, the struct data is cloned. The cloning is done in the current thread and serialized in the logging thread. 27 | - include_unixnano option added 28 | - user can choose what core to choose for affinity (with_logger_core) 29 | - compile time filter added 30 | - timestamp is made in logger thread 31 | 32 | ## [0.2.4] 33 | - rolling file function is added 34 | 35 | ## [0.2.3] 36 | - once_cell dependency changed 37 | 38 | ## [0.2.2] 39 | - minor bug fix 40 | 41 | ## [0.2.0] 42 | - report level load optimization (todo) 43 | - lazy string interned (todo) 44 | - log_info, log_debug, etc, deprecated (todo) 45 | = `topic` and `message` are base keys in Json output 46 | 47 | ## [0.1.5] - 2024-09-12 48 | - dropping the guard also flushes 49 | - Some minor changes in docs and README 50 | 51 | ## [0.1.3] - 2024-09-12 52 | - hot fix: now console and file report are both an option 53 | 54 | ## [0.1.1] - 2024-08-29 55 | ### Fixed 56 | - Corrected GitHub repository URL in project metadata 57 | 58 | ## [0.1.0] - 2024-08-29 59 | ### Added 60 | - Initial release of the crate 61 | -------------------------------------------------------------------------------- /examples/fast_log/src/main.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info, warn}; 2 | use fast_log::Config; 3 | use serde::{Deserialize, Serialize}; 4 | use flashlog::get_unix_nano; 5 | 6 | #[derive(Debug, Serialize, Deserialize, Clone)] 7 | pub struct LogStruct { 8 | data: [u64; 10], 9 | } 10 | 11 | impl Default for LogStruct { 12 | fn default() -> Self { 13 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 14 | } 15 | } 16 | 17 | fn fast_log_array_80bytes() { 18 | fast_log::init(Config::new().file("logs/arr.log").chan_len(Some(100000))).unwrap(); 19 | let iteration = 500_000; 20 | let test_number = 5; 21 | let log_struct = LogStruct::default(); 22 | let mut res_vec = Vec::new(); 23 | 24 | println!("Start test: struct containing 80 bytes array "); 25 | println!("Iteration: {}, Test number: {}", iteration, test_number); 26 | println!("At each test, sleep for 2 seconds and log warm up msg"); 27 | for _ in 0..test_number { 28 | std::thread::sleep(std::time::Duration::from_secs(2)); 29 | info!("Warm up"); 30 | let start = get_unix_nano(); 31 | for _ in 0..iteration { 32 | info!("Log message: {:?}", &log_struct); 33 | } 34 | let elapsed = get_unix_nano() - start; 35 | res_vec.push(elapsed); 36 | } 37 | 38 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 39 | 40 | for (i, res) in ave_res.iter().enumerate() { 41 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 42 | } 43 | 44 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 45 | } 46 | 47 | fn fast_log_i32() { 48 | fast_log::init(Config::new().file("logs/i32.log").chan_len(Some(100000))).unwrap(); 49 | let iteration = 500_000; 50 | let test_number = 5; 51 | let mut res_vec = Vec::new(); 52 | 53 | println!("Start test: i32"); 54 | println!("Iteration: {}, Test number: {}", iteration, test_number); 55 | println!("At each test, sleep for 2 seconds and log warm up msg"); 56 | for _ in 0..test_number { 57 | std::thread::sleep(std::time::Duration::from_secs(2)); 58 | info!("Warm up"); 59 | let start = get_unix_nano(); 60 | for i in 0..iteration { 61 | info!("Log message: {}", i); 62 | } 63 | log::logger().flush(); 64 | let elapsed = get_unix_nano() - start; 65 | res_vec.push(elapsed); 66 | } 67 | 68 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 69 | 70 | for (i, res) in ave_res.iter().enumerate() { 71 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 72 | } 73 | 74 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 75 | } 76 | 77 | fn main() { 78 | #[cfg(feature = "i32")] 79 | fast_log_i32(); 80 | 81 | #[cfg(feature = "arr")] 82 | fast_log_array_80bytes(); 83 | } -------------------------------------------------------------------------------- /examples/ftlog/src/main.rs: -------------------------------------------------------------------------------- 1 | use ftlog::{ 2 | appender::{file::Period, FileAppender}, 3 | info, LoggerGuard, 4 | }; 5 | use log::LevelFilter; 6 | use time::Duration; 7 | use flashlog::get_unix_nano; 8 | use serde::{Serialize, Deserialize}; 9 | 10 | 11 | #[derive(Debug, Serialize, Deserialize, Clone)] 12 | pub struct LogStruct { 13 | data: [u64; 10], 14 | } 15 | 16 | impl Default for LogStruct { 17 | fn default() -> Self { 18 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 19 | } 20 | } 21 | 22 | fn init() -> LoggerGuard { 23 | // Rotate every day, clean stale logs that were modified 7 days ago on each rotation 24 | let writer = FileAppender::builder() 25 | .path("logs/current.log") 26 | .rotate(Period::Minute) 27 | .expire(Duration::minutes(4)) 28 | .build(); 29 | ftlog::Builder::new() 30 | // global max log level 31 | .max_log_level(LevelFilter::Info) 32 | // define root appender, pass None would write to stderr 33 | .root(writer) 34 | .unbounded() 35 | // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" 36 | .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) 37 | .appender("ftlog-appender", FileAppender::new("logs/ftlog-appender.log")) 38 | .try_init() 39 | .expect("logger build or set failed") 40 | } 41 | 42 | fn ftlog_arr_80byte() { 43 | let logger = init(); 44 | let iteration = 500_000; 45 | let test_number = 5; 46 | let log_struct = LogStruct::default(); 47 | let mut res_vec = Vec::new(); 48 | 49 | println!("Start test: struct containing 80 bytes array "); 50 | println!("Iteration: {}, Test number: {}", iteration, test_number); 51 | println!("At each test, sleep for 2 seconds and log warm up msg"); 52 | for _ in 0..test_number { 53 | std::thread::sleep(std::time::Duration::from_secs(2)); 54 | info!("Warm up"); 55 | let start = get_unix_nano(); 56 | for _ in 0..iteration { 57 | info!("Log message: {:?}", &log_struct); 58 | } 59 | let elapsed = get_unix_nano() - start; 60 | res_vec.push(elapsed); 61 | } 62 | 63 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 64 | 65 | for (i, res) in ave_res.iter().enumerate() { 66 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 67 | } 68 | 69 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 70 | } 71 | 72 | fn ftlog_i32() { 73 | let logger = init(); 74 | let iteration = 500_000; 75 | let test_number = 5; 76 | let mut res_vec = Vec::new(); 77 | 78 | println!("Start test: i32"); 79 | println!("Iteration: {}, Test number: {}", iteration, test_number); 80 | println!("At each test, sleep for 2 seconds and log warm up msg"); 81 | for _ in 0..test_number { 82 | std::thread::sleep(std::time::Duration::from_secs(2)); 83 | info!("Warm up"); 84 | let start = get_unix_nano(); 85 | for i in 0..iteration { 86 | info!("Log message: {}", i); 87 | } 88 | let elapsed = get_unix_nano() - start; 89 | res_vec.push(elapsed); 90 | } 91 | 92 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 93 | 94 | for (i, res) in ave_res.iter().enumerate() { 95 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 96 | } 97 | 98 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 99 | } 100 | 101 | fn main() { 102 | #[cfg(feature = "i32")] 103 | ftlog_i32(); 104 | #[cfg(feature = "arr")] 105 | ftlog_arr_80byte(); 106 | } -------------------------------------------------------------------------------- /examples/slog/src/main.rs: -------------------------------------------------------------------------------- 1 | use slog; 2 | use slog_term; 3 | use slog_async; 4 | use std::fs::OpenOptions; 5 | use slog::Drain; 6 | use slog::{ 7 | o, 8 | info, 9 | }; 10 | use serde::{Deserialize, Serialize}; 11 | use flashlog::get_unix_nano; 12 | 13 | #[derive(Debug, Serialize, Deserialize, Clone)] 14 | pub struct LogStruct { 15 | data: [u64; 10], 16 | } 17 | 18 | impl Default for LogStruct { 19 | fn default() -> Self { 20 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 21 | } 22 | } 23 | 24 | fn slog_i32() { 25 | let log_path = "logs/i32.log"; 26 | let file = OpenOptions::new() 27 | .create(true) 28 | .write(true) 29 | .truncate(true) 30 | .open(log_path) 31 | .unwrap(); 32 | 33 | let decorator = slog_term::PlainDecorator::new(file); 34 | let drain = slog_term::FullFormat::new(decorator).build().fuse(); 35 | let drain = slog_async::Async::new(drain).build().fuse(); 36 | 37 | let _log = slog::Logger::root(drain, o!()); 38 | 39 | let iteration = 500_000; 40 | let test_number = 5; 41 | let mut res_vec = Vec::new(); 42 | 43 | println!("Start test: i32"); 44 | println!("Iteration: {}, Test number: {}", iteration, test_number); 45 | println!("At each test, sleep for 2 seconds and log warm up msg"); 46 | 47 | for _ in 0..test_number { 48 | std::thread::sleep(std::time::Duration::from_secs(2)); 49 | info!(_log, "Warm up"); 50 | let start = get_unix_nano(); 51 | for i in 0..iteration { 52 | info!(_log, "Log message: {}", i); 53 | } 54 | let elapsed = get_unix_nano() - start; 55 | res_vec.push(elapsed); 56 | } 57 | 58 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 59 | 60 | for (i, res) in ave_res.iter().enumerate() { 61 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 62 | } 63 | 64 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 65 | } 66 | 67 | fn slog_array_80bytes() { 68 | let log_path = "logs/arr.log"; 69 | let file = OpenOptions::new() 70 | .create(true) 71 | .write(true) 72 | .truncate(true) 73 | .open(log_path) 74 | .unwrap(); 75 | 76 | let decorator = slog_term::PlainDecorator::new(file); 77 | let drain = slog_term::FullFormat::new(decorator).build().fuse(); 78 | let drain = slog_async::Async::new(drain).build().fuse(); 79 | 80 | let _log = slog::Logger::root(drain, o!()); 81 | 82 | let iteration = 500_000; 83 | let test_number = 5; 84 | let log_struct = LogStruct::default(); 85 | let mut res_vec = Vec::new(); 86 | 87 | println!("Start test: struct containing 80 bytes array "); 88 | println!("Iteration: {}, Test number: {}", iteration, test_number); 89 | println!("At each test, sleep for 2 seconds and log warm up msg"); 90 | 91 | for _ in 0..test_number { 92 | std::thread::sleep(std::time::Duration::from_secs(2)); 93 | info!(_log, "Warm up"); 94 | let start = get_unix_nano(); 95 | for _ in 0..iteration { 96 | info!(_log, "Log message: {:?}", &log_struct); 97 | } 98 | let elapsed = get_unix_nano() - start; 99 | res_vec.push(elapsed); 100 | } 101 | 102 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 103 | 104 | for (i, res) in ave_res.iter().enumerate() { 105 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 106 | } 107 | 108 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 109 | } 110 | 111 | fn main() { 112 | #[cfg(feature = "i32")] 113 | slog_i32(); 114 | #[cfg(feature = "arr")] 115 | slog_array_80bytes(); 116 | } -------------------------------------------------------------------------------- /examples/tracing/src/main.rs: -------------------------------------------------------------------------------- 1 | use flashlog::get_unix_nano; 2 | use tracing::{info, span, Level}; 3 | use tracing_subscriber::{fmt, prelude::*, EnvFilter}; 4 | use tracing_appender::rolling::{RollingFileAppender, Rotation}; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | 8 | #[derive(Debug, Serialize, Deserialize, Clone)] 9 | pub struct LogStruct { 10 | data: [u64; 10], 11 | } 12 | 13 | impl Default for LogStruct { 14 | fn default() -> Self { 15 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 16 | } 17 | } 18 | 19 | fn tracing_i32() { 20 | // Set up a rolling file appender that rotates daily 21 | let file_appender = RollingFileAppender::new(Rotation::DAILY, "logs", "i32.log"); 22 | 23 | // Create a subscriber that writes to the file appender 24 | let subscriber = tracing_subscriber::registry() 25 | .with(fmt::Layer::new().with_writer(file_appender).with_ansi(false)); 26 | //..with(EnvFilter::from_default_env()); 27 | 28 | // Set the subscriber as the global default 29 | tracing::subscriber::set_global_default(subscriber) 30 | .expect("Failed to set subscriber"); 31 | 32 | let iteration = 500_000; 33 | let test_number = 5; 34 | 35 | let mut res_vec = Vec::new(); 36 | 37 | println!("Start test: i32"); 38 | println!("Iteration: {}, Test number: {}", iteration, test_number); 39 | println!("At each test, sleep for 2 seconds and log warm up msg"); 40 | 41 | for _ in 0..test_number { 42 | std::thread::sleep(std::time::Duration::from_secs(2)); 43 | info!("Warm up"); 44 | let start = get_unix_nano(); 45 | for _ in 0..iteration { 46 | info!("Log message: {}", 42); 47 | } 48 | let elapsed = get_unix_nano() - start; 49 | res_vec.push(elapsed); 50 | } 51 | 52 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 53 | 54 | for (i, res) in ave_res.iter().enumerate() { 55 | println!("Test {}: average time: {} ns", i, res); 56 | } 57 | 58 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 59 | } 60 | 61 | fn tracing_array_80bytes() { 62 | // Set up a rolling file appender that rotates daily 63 | let file_appender = RollingFileAppender::new(Rotation::DAILY, "logs", "arr.log"); 64 | 65 | // Create a subscriber that writes to the file appender 66 | let subscriber = tracing_subscriber::registry() 67 | .with(fmt::Layer::new().with_writer(file_appender).with_ansi(false)); 68 | //.with(EnvFilter::from_default_env()); 69 | 70 | // Set the subscriber as the global default 71 | tracing::subscriber::set_global_default(subscriber) 72 | .expect("Failed to set subscriber"); 73 | 74 | let iteration = 500_000; 75 | let test_number = 5; 76 | let log_struct = LogStruct::default(); 77 | let mut res_vec = Vec::new(); 78 | 79 | println!("Start test: struct containing 80 bytes array "); 80 | println!("Iteration: {}, Test number: {}", iteration, test_number); 81 | println!("At each test, sleep for 2 seconds and log warm up msg"); 82 | 83 | for _ in 0..test_number { 84 | std::thread::sleep(std::time::Duration::from_secs(2)); 85 | info!("Warm up"); 86 | let start = get_unix_nano(); 87 | for _ in 0..iteration { 88 | info!("Log message: {:?}", &log_struct); 89 | } 90 | let elapsed = get_unix_nano() - start; 91 | res_vec.push(elapsed); 92 | } 93 | 94 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 95 | 96 | for (i, res) in ave_res.iter().enumerate() { 97 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 98 | } 99 | 100 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 101 | } 102 | 103 | fn main() { 104 | #[cfg(feature = "i32")] 105 | tracing_i32(); 106 | #[cfg(feature = "arr")] 107 | tracing_array_80bytes(); 108 | } -------------------------------------------------------------------------------- /examples/fern/src/main.rs: -------------------------------------------------------------------------------- 1 | use fern::Dispatch; 2 | use log::info; 3 | use serde::{Serialize, Deserialize}; 4 | use flashlog::get_unix_nano; 5 | use std::fs::File; 6 | use std::time::SystemTime; 7 | use anyhow::Result; 8 | 9 | #[derive(Debug, Serialize, Deserialize, Clone)] 10 | pub struct LogStruct { 11 | data: [u64; 10], 12 | } 13 | 14 | impl Default for LogStruct { 15 | fn default() -> Self { 16 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 17 | } 18 | } 19 | 20 | fn fern_array_80bytes() -> Result<()> { 21 | // Configure logger at runtime 22 | Dispatch::new() 23 | // Perform allocation-free log formatting 24 | .format(|out, message, record| { 25 | out.finish(format_args!( 26 | "[{} {} {}] {}", 27 | humantime::format_rfc3339(std::time::SystemTime::now()), 28 | record.level(), 29 | record.target(), 30 | message 31 | )) 32 | }) 33 | // Add blanket level filter - 34 | .level(log::LevelFilter::Debug) 35 | // - and per-module overrides 36 | .level_for("hyper", log::LevelFilter::Info) 37 | // Output to stdout, files, and other Dispatch configurations 38 | //.chain(std::io::stdout()) 39 | .chain(fern::log_file("logs/struct.log")?) 40 | // Apply globally 41 | .apply()?; 42 | 43 | let iteration = 500_000; 44 | let test_number = 5; 45 | let log_struct = LogStruct::default(); 46 | let mut res_vec = Vec::new(); 47 | 48 | println!("Start test: struct containing 80 bytes array "); 49 | println!("Iteration: {}, Test number: {}", iteration, test_number); 50 | println!("At each test, sleep for 2 seconds and log warm up msg"); 51 | for _ in 0..test_number { 52 | std::thread::sleep(std::time::Duration::from_secs(2)); 53 | info!("Warm up"); 54 | let start = get_unix_nano(); 55 | for _ in 0..iteration { 56 | info!("Log message: {:?}", &log_struct); 57 | } 58 | let elapsed = get_unix_nano() - start; 59 | res_vec.push(elapsed); 60 | } 61 | 62 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 63 | 64 | for (i, res) in ave_res.iter().enumerate() { 65 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 66 | } 67 | 68 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 69 | 70 | Ok(()) 71 | } 72 | 73 | fn fern_i32() -> Result<()> { 74 | // Configure logger at runtime 75 | Dispatch::new() 76 | // Perform allocation-free log formatting 77 | .format(|out, message, record| { 78 | out.finish(format_args!( 79 | "[{} {} {}] {}", 80 | humantime::format_rfc3339(std::time::SystemTime::now()), 81 | record.level(), 82 | record.target(), 83 | message 84 | )) 85 | }) 86 | // Add blanket level filter - 87 | .level(log::LevelFilter::Debug) 88 | // - and per-module overrides 89 | .level_for("hyper", log::LevelFilter::Info) 90 | // Output to stdout, files, and other Dispatch configurations 91 | //.chain(std::io::stdout()) 92 | .chain(fern::log_file("logs/i32.log")?) 93 | // Apply globally 94 | .apply()?; 95 | 96 | let iteration = 500_000; 97 | let test_number = 5; 98 | let mut res_vec = Vec::new(); 99 | 100 | println!("Start test: i32"); 101 | println!("Iteration: {}, Test number: {}", iteration, test_number); 102 | println!("At each test, sleep for 2 seconds and log warm up msg"); 103 | for _ in 0..test_number { 104 | std::thread::sleep(std::time::Duration::from_secs(2)); 105 | info!("Warm up"); 106 | let start = get_unix_nano(); 107 | for i in 0..iteration { 108 | info!("Log message: {}", i); 109 | } 110 | let elapsed = get_unix_nano() - start; 111 | res_vec.push(elapsed); 112 | } 113 | 114 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 115 | 116 | for (i, res) in ave_res.iter().enumerate() { 117 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 118 | } 119 | 120 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 121 | 122 | Ok(()) 123 | } 124 | fn main() -> Result<()> { 125 | #[cfg(feature = "i32")] 126 | fern_i32(); 127 | #[cfg(feature = "arr")] 128 | fern_array_80bytes(); 129 | 130 | Ok(()) 131 | } -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::Lazy; 2 | use quanta::Clock; 3 | use std::time::{SystemTime, UNIX_EPOCH}; 4 | 5 | const UNIX_NANO_ANCHOR_BUFFER: u64 = 10; //10ns 6 | 7 | pub static UNIVERSIAL_CLOCK: Lazy = Lazy::new(Clock::new); 8 | 9 | #[inline] 10 | pub fn get_unix_nano() -> u64 { 11 | static UNIVERSIAL_SYSTEMTIME_ANCHOR: Lazy = Lazy::new(|| { 12 | SystemTime::now() 13 | .duration_since(UNIX_EPOCH) 14 | .unwrap() 15 | .as_nanos() as u64 16 | + UNIX_NANO_ANCHOR_BUFFER 17 | }); 18 | static UNIVERSIAL_CLOCK_ANCHOR: Lazy = Lazy::new(|| UNIVERSIAL_CLOCK.raw()); 19 | 20 | UNIVERSIAL_CLOCK.delta_as_nanos(*UNIVERSIAL_CLOCK_ANCHOR, UNIVERSIAL_CLOCK.raw()) 21 | + *UNIVERSIAL_SYSTEMTIME_ANCHOR 22 | } 23 | 24 | pub fn time_components_from_unix_nano(unix_nano: u64) -> (u8, u8, u8, u16) { 25 | let total_seconds = unix_nano / 1_000_000_000; 26 | let nanos = unix_nano % 1_000_000_000; 27 | 28 | let seconds_of_day = total_seconds % 86400; 29 | 30 | let hours = (seconds_of_day / 3600) as u8; 31 | let minutes = ((seconds_of_day % 3600) / 60) as u8; 32 | let seconds = (seconds_of_day % 60) as u8; 33 | let millis = (nanos / 1_000_000) as u16; 34 | 35 | (hours, minutes, seconds, millis) 36 | } 37 | 38 | pub fn convert_unix_nano_to_date_and_time(unix_nano: u64, utc_offset_hour: i32) -> (String, String) { 39 | const NANOS_IN_SEC: u64 = 1_000_000_000; 40 | const NANOS_IN_MIN: u64 = 60 * NANOS_IN_SEC; 41 | const NANOS_IN_HOUR: u64 = 60 * NANOS_IN_MIN; 42 | const NANOS_IN_DAY: u64 = 24 * NANOS_IN_HOUR; 43 | 44 | let days_since_epoch = unix_nano / NANOS_IN_DAY; 45 | let remaining_nanos = unix_nano % NANOS_IN_DAY; 46 | 47 | let hours = remaining_nanos / NANOS_IN_HOUR; 48 | let remaining_nanos = remaining_nanos % NANOS_IN_HOUR; 49 | 50 | let minutes = remaining_nanos / NANOS_IN_MIN; 51 | let remaining_nanos = remaining_nanos % NANOS_IN_MIN; 52 | 53 | let seconds = remaining_nanos / NANOS_IN_SEC; 54 | let remaining_nanos = remaining_nanos % NANOS_IN_SEC; 55 | 56 | let millis = remaining_nanos / 1_000_000; 57 | let remaining_nanos = remaining_nanos % 1_000_000; 58 | 59 | let micros = remaining_nanos / 1_000; 60 | let nanos = remaining_nanos % 1_000; 61 | 62 | // Adjust for UTC offset 63 | let mut total_hours = hours as i32 + utc_offset_hour; 64 | let mut total_days = days_since_epoch as i32; 65 | 66 | if total_hours >= 24 { 67 | total_hours -= 24; 68 | total_days += 1; 69 | } else if total_hours < 0 { 70 | total_hours += 24; 71 | total_days -= 1; 72 | } 73 | 74 | let (year, month, day) = days_to_date(total_days as u32); 75 | 76 | let date = format!("{:04}{:02}{:02}", year, month, day); 77 | let time = format!("{:02}:{:02}:{:02}.{:03}:{:03}:{:03}", total_hours, minutes, seconds, millis, micros, nanos); 78 | 79 | (date, time) 80 | } 81 | 82 | fn days_to_date(mut days: u32) -> (i32, u32, u32) { 83 | let mut year = 1970; 84 | 85 | // Find the year 86 | loop { 87 | let days_in_year = if is_leap_year(year) { 366 } else { 365 }; 88 | if days < days_in_year { 89 | break; 90 | } 91 | days -= days_in_year; 92 | year += 1; 93 | } 94 | 95 | // Find the month and day 96 | let mut month = 1; 97 | while days > 0 { 98 | let days_in_month = days_in_month(year, month); 99 | if days < days_in_month { 100 | break; 101 | } 102 | days -= days_in_month; 103 | month += 1; 104 | } 105 | 106 | (year, month, days + 1) 107 | } 108 | 109 | fn is_leap_year(year: i32) -> bool { 110 | (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) 111 | } 112 | 113 | fn days_in_month(year: i32, month: u32) -> u32 { 114 | match month { 115 | 1 => 31, 116 | 2 => { 117 | if is_leap_year(year) { 118 | 29 119 | } else { 120 | 28 121 | } 122 | } 123 | 3 => 31, 124 | 4 => 30, 125 | 5 => 31, 126 | 6 => 30, 127 | 7 => 31, 128 | 8 => 31, 129 | 9 => 30, 130 | 10 => 31, 131 | 11 => 30, 132 | 12 => 31, 133 | _ => 0, 134 | } 135 | } 136 | 137 | 138 | #[cfg(test)] 139 | mod tests { 140 | use super::*; 141 | 142 | #[test] 143 | fn test_get_unix_nano() { 144 | let unix_nano = get_unix_nano(); 145 | println!("unix_nano: {}", unix_nano); 146 | assert!(unix_nano > 0); 147 | } 148 | 149 | #[test] 150 | fn test_time_components_from_unix_nano() { 151 | let unix_nano = get_unix_nano(); 152 | let res = convert_unix_nano_to_date_and_time(unix_nano, 9); 153 | println!("{:?}", res); 154 | 155 | } 156 | } -------------------------------------------------------------------------------- /src/rolling_file.rs: -------------------------------------------------------------------------------- 1 | use crate::{get_unix_nano, UnixNano}; 2 | use std::path::{Path, PathBuf}; 3 | use std::fs::{File, OpenOptions, remove_file}; 4 | use std::io::{self, BufWriter, Write}; 5 | use chrono::Local; 6 | 7 | const SECOND_IN_NANOS: u64 = 1_000_000_000; 8 | const MINUATE_IN_NANOS: u64 = 60_000_000_000; 9 | const HOUR_IN_NANOS: u64 = 3_600_000_000_000; 10 | const DAY_IN_NANOS: u64 = 86_400_000_000_000; 11 | const WEEK_IN_NANOS: u64 = 604_800_000_000_000; 12 | 13 | #[derive(Clone, Debug)] 14 | pub enum RollingPeriod { 15 | Secondly, 16 | Minutely, 17 | Hourly, 18 | Daily, 19 | Weekly, 20 | } 21 | 22 | #[derive(Clone, Debug)] 23 | pub struct RollingConfig { 24 | pub base_path: PathBuf, 25 | pub file_name_prefix: String, 26 | // 27 | pub roll_period: Option, 28 | pub max_roll_files: Option, 29 | // 30 | pub compress: bool, 31 | } 32 | 33 | impl Default for RollingConfig { 34 | fn default() -> Self { 35 | Self { 36 | base_path: PathBuf::from("./"), 37 | file_name_prefix: "log".to_string(), 38 | roll_period: None, 39 | max_roll_files: None, 40 | compress: false, 41 | } 42 | } 43 | } 44 | 45 | pub struct RollingFileWriter { 46 | config: RollingConfig, 47 | current_file: Option>, 48 | rolling_nanos: Option, 49 | max_roll_files: usize, 50 | last_roll_time: UnixNano, 51 | } 52 | 53 | impl RollingFileWriter { 54 | pub fn new(config: RollingConfig) -> io::Result { 55 | let current_file = OpenOptions::new() 56 | .create(true) 57 | .append(true) 58 | .open(Self::generate_file_path(&config.base_path, &config.file_name_prefix))?; 59 | 60 | let last_roll_time = get_unix_nano(); 61 | let max_roll_files = config.max_roll_files.unwrap_or(10); 62 | let rolling_nanos = match config.roll_period { 63 | Some(RollingPeriod::Secondly) => Some(SECOND_IN_NANOS), 64 | Some(RollingPeriod::Minutely) => Some(MINUATE_IN_NANOS), 65 | Some(RollingPeriod::Hourly) => Some(HOUR_IN_NANOS), 66 | Some(RollingPeriod::Daily) => Some(DAY_IN_NANOS), 67 | Some(RollingPeriod::Weekly) => Some(WEEK_IN_NANOS), 68 | None => None, 69 | }; 70 | Ok(Self { 71 | config, 72 | current_file: Some(BufWriter::new(current_file)), 73 | rolling_nanos, 74 | max_roll_files, 75 | last_roll_time, 76 | }) 77 | } 78 | 79 | fn generate_file_path(base_path: &Path, prefix: &str) -> PathBuf { 80 | let now = Local::now(); 81 | let timestamp = now.format("%Y%m%d-%H%M%S"); 82 | let file_name = format!("{}-{}.log", prefix, timestamp); 83 | base_path.join(file_name) 84 | } 85 | 86 | pub fn write_all(&mut self, data: &[u8]) -> io::Result<()> { 87 | if self.should_roll(None) { 88 | self.roll_file()?; 89 | } 90 | 91 | if let Some(ref mut current_file) = self.current_file { 92 | current_file.write_all(data)?; 93 | } 94 | 95 | Ok(()) 96 | } 97 | 98 | pub fn flush(&mut self) -> io::Result<()> { 99 | if let Some(ref mut current_file) = self.current_file { 100 | current_file.flush()?; 101 | } 102 | Ok(()) 103 | } 104 | 105 | pub fn sync_all(&mut self) -> io::Result<()> { 106 | if let Some(ref mut current_file) = self.current_file { 107 | current_file.get_ref().sync_all()?; 108 | } 109 | Ok(()) 110 | } 111 | 112 | fn should_roll(&mut self, now: Option) -> bool { 113 | if self.rolling_nanos.is_none() { 114 | return false; 115 | } 116 | 117 | let now = now.unwrap_or(get_unix_nano()); 118 | let diff = now - self.last_roll_time; 119 | 120 | if diff >= self.rolling_nanos.unwrap() { 121 | self.last_roll_time = now; 122 | true 123 | } else { 124 | false 125 | } 126 | } 127 | 128 | fn roll_file(&mut self) -> io::Result<()> { 129 | // Flush and close current file 130 | if let Some(ref mut current_file) = self.current_file.take() { 131 | current_file.flush()?; 132 | } 133 | // Generate new file path 134 | let new_file_path = Self::generate_file_path(&self.config.base_path, &self.config.file_name_prefix); 135 | 136 | // Rotate old files if needed 137 | self.rotate_old_files()?; 138 | 139 | // open new file 140 | let new_file = OpenOptions::new() 141 | .create(true) 142 | .append(true) 143 | .open(&new_file_path)?; 144 | 145 | self.current_file = Some(BufWriter::new(new_file)); 146 | self.last_roll_time = get_unix_nano(); 147 | 148 | 149 | Ok(()) 150 | } 151 | 152 | fn compress_file(&self, file_path: &Path) -> io::Result<()> { 153 | let gz_path = file_path.with_extension("gz"); 154 | let input = std::fs::read(file_path)?; 155 | let output = File::create(&gz_path)?; 156 | let mut encoder = flate2::write::GzEncoder::new(output, flate2::Compression::fast()); 157 | encoder.write_all(&input)?; 158 | encoder.finish()?; 159 | remove_file(file_path)?; 160 | Ok(()) 161 | } 162 | 163 | fn collect_log_files(&self) -> io::Result> { 164 | let mut files = Vec::new(); 165 | for entry in std::fs::read_dir(&self.config.base_path)? { 166 | let entry = entry?; 167 | let path = entry.path(); 168 | if path.is_file() { 169 | if let Some(ext) = path.extension() { 170 | if ext == "log" { 171 | if let Some(file_name) = path.file_name() { 172 | if file_name.to_string_lossy().starts_with(&self.config.file_name_prefix) { 173 | files.push(path); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | } 180 | Ok(files) 181 | } 182 | 183 | fn collect_compressed_files(&self) -> io::Result> { 184 | let mut files = Vec::new(); 185 | for entry in std::fs::read_dir(&self.config.base_path)? { 186 | let entry = entry?; 187 | let path = entry.path(); 188 | if path.is_file() { 189 | if let Some(ext) = path.extension() { 190 | if ext == "gz" { 191 | if let Some(file_name) = path.file_name() { 192 | if file_name.to_string_lossy().starts_with(&self.config.file_name_prefix) { 193 | files.push(path); 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | Ok(files) 201 | } 202 | 203 | fn rotate_old_files(&self) -> io::Result<()> { 204 | let mut log_files = self.collect_log_files()?; 205 | log_files.sort_by(|a, b| b.cmp(a)); // Sort in reverse order 206 | 207 | // Remove oldest files if we exceed max_roll_files 208 | while log_files.len() >= self.max_roll_files { 209 | if let Some(oldest_file) = log_files.pop() { 210 | if self.config.compress { 211 | self.compress_file(&oldest_file)?; 212 | } else { 213 | remove_file(oldest_file)?; 214 | } 215 | } 216 | } 217 | 218 | let mut gz_files = self.collect_compressed_files()?; 219 | gz_files.sort_by(|a, b| b.cmp(a)); // Sort in reverse order 220 | 221 | // Remove oldest files if we exceed max_roll_files 222 | while gz_files.len() >= self.max_roll_files { 223 | if let Some(oldest_file) = gz_files.pop() { 224 | remove_file(oldest_file)?; 225 | } 226 | } 227 | 228 | Ok(()) 229 | } 230 | } -------------------------------------------------------------------------------- /examples/flashlog/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Serialize, Deserialize, Clone)] 5 | pub struct LogStruct { 6 | data: [u64; 10], 7 | } 8 | 9 | impl Default for LogStruct { 10 | fn default() -> Self { 11 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 12 | } 13 | } 14 | 15 | impl std::fmt::Display for LogStruct { 16 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 | write!(f, "{:?}", self.data) 18 | } 19 | } 20 | 21 | fn flashlog_array_80bytes() -> Result<()> { 22 | let _logger = flashlog::Logger::initialize() 23 | .with_file("logs", "message")? 24 | .with_console_report(false) 25 | .with_msg_buffer_size(500) 26 | .with_msg_flush_interval(500_000_000) 27 | .with_max_log_level(flashlog::LogLevel::Error) 28 | .with_timezone(flashlog::TimeZone::Local) 29 | .with_logger_core(0) 30 | .include_unixnano(true) 31 | .launch(); 32 | 33 | let iteration = 500_000; 34 | let test_number = 5; 35 | 36 | let log_struct = LogStruct::default(); 37 | let mut res_vec = Vec::new(); 38 | 39 | println!("Start test: struct containing 80 bytes array "); 40 | println!("Iteration: {}, Test number: {}", iteration, test_number); 41 | let sleep_sec = 2; 42 | println!("At each test, sleep for {} seconds and log warm up msg", sleep_sec); 43 | flashlog::flash_error_ct!("Warm up"); 44 | flashlog::flash_error_ct!("Warm up"); 45 | for _ in 0..test_number { 46 | std::thread::sleep(std::time::Duration::from_secs(sleep_sec)); 47 | let start = flashlog::get_unix_nano(); 48 | for _ in 0..iteration { 49 | flashlog::flash_error_ct!(LogStruct = log_struct); 50 | flashlog::flash_warn_ct!(LogStruct = log_struct); 51 | flashlog::flash_info_ct!(LogStruct = log_struct); 52 | //flashlog::flash_debug_ct!(LogStruct = log_struct); 53 | //flashlog::flash_trace_ct!(LogStruct = log_struct); 54 | 55 | /* 56 | flashlog::flash_error!(LogStruct = log_struct); 57 | flashlog::flash_warn!(LogStruct = log_struct); 58 | flashlog::flash_info!(LogStruct = log_struct); 59 | flashlog::flash_debug!(LogStruct = log_struct); 60 | flashlog::flash_trace!(LogStruct = log_struct); 61 | */ 62 | 63 | } 64 | let elapsed = flashlog::get_unix_nano() - start; 65 | res_vec.push(elapsed); 66 | } 67 | 68 | flashlog::flush!(); 69 | 70 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 71 | 72 | for (i, res) in ave_res.iter().skip(1).enumerate() { 73 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 74 | } 75 | 76 | println!("Average time: {:.1} ns", ave_res.iter().skip(1).sum::() / (test_number as f64 -1.0)); 77 | 78 | 79 | Ok(()) 80 | } 81 | 82 | fn flashlog_i32() -> Result<()> { 83 | let _logger = flashlog::Logger::initialize() 84 | .with_file("logs", "message")? 85 | .with_console_report(false) 86 | .with_msg_buffer_size(100) 87 | .with_msg_flush_interval(500_000_000) 88 | .with_max_log_level(flashlog::LogLevel::Error) 89 | .with_timezone(flashlog::TimeZone::Local) 90 | .with_logger_core(1) 91 | .include_unixnano(true) 92 | .launch(); 93 | 94 | let iteration = 300_000; 95 | let test_number = 5; 96 | 97 | let mut res_vec = Vec::new(); 98 | 99 | println!("Start test: i32 "); 100 | println!("Iteration: {}, Test number: {}", iteration, test_number); 101 | let sleep_sec = 2; 102 | println!("At each test, sleep for {} seconds and log warm up msg", sleep_sec); 103 | flashlog::flash_error_ct!("Warm up"); 104 | flashlog::flash_error_ct!("Warm up"); 105 | for _ in 0..test_number { 106 | std::thread::sleep(std::time::Duration::from_secs(sleep_sec)); 107 | let start = flashlog::get_unix_nano(); 108 | for i in 0..iteration { 109 | flashlog::flash_error_ct!(log_int = i); 110 | flashlog::flash_warn_ct!(log_int = i); 111 | flashlog::flash_info_ct!(log_int = i); 112 | } 113 | let elapsed = flashlog::get_unix_nano() - start; 114 | res_vec.push(elapsed); 115 | } 116 | //flush!(); 117 | 118 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 119 | 120 | for (i, res) in ave_res.iter().enumerate().skip(1) { 121 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 122 | } 123 | 124 | println!("Average time: {:.1} ns", ave_res.iter().skip(1).sum::() / (test_number as f64 -1.0)); 125 | 126 | Ok(()) 127 | } 128 | 129 | fn test_logger() -> Result<()> { 130 | let _logger = flashlog::Logger::initialize() 131 | .with_file("logs", "message")? 132 | .with_console_report(false) 133 | .with_msg_buffer_size(100) 134 | .with_msg_flush_interval(500_000_000) 135 | .with_max_log_level(flashlog::LogLevel::Error) 136 | .with_timezone(flashlog::TimeZone::Local) 137 | .with_logger_core(0) 138 | .include_unixnano(true) 139 | .launch(); 140 | 141 | let iteration = 500_000; 142 | let test_number = 5; 143 | let log_struct = LogStruct::default(); 144 | let mut res_vec = Vec::new(); 145 | 146 | println!("Start test"); 147 | println!("Iteration: {}, Test number: {}", iteration, test_number); 148 | println!("At each test, sleep for 3 seconds and log warm up msg"); 149 | for _ in 0..test_number { 150 | std::thread::sleep(std::time::Duration::from_secs(3)); 151 | flashlog::flash_info!("Warm up"); 152 | let start = flashlog::get_unix_nano(); 153 | for i in 0..iteration { 154 | flashlog::flash_trace_ct!(LogStruct = log_struct); 155 | flashlog::flash_debug_ct!(LogStruct = log_struct); 156 | flashlog::flash_info_ct!(LogStruct = log_struct); 157 | flashlog::flash_warn_ct!(LogStruct = log_struct); 158 | flashlog::flash_error_ct!(LogStruct = log_struct); 159 | } 160 | let elapsed = flashlog::get_unix_nano() - start; 161 | res_vec.push(elapsed); 162 | } 163 | //flush!(); 164 | 165 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 166 | 167 | for (i, res) in ave_res.iter().enumerate() { 168 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 169 | } 170 | 171 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 172 | // 173 | // 174 | let iteration = 500_000; 175 | let test_number = 5; 176 | 177 | let mut res_vec = Vec::new(); 178 | let log_struct = LogStruct::default(); 179 | println!("Start test"); 180 | println!("Iteration: {}, Test number: {}", iteration, test_number); 181 | println!("At each test, sleep for 3 seconds and log warm up msg"); 182 | for _ in 0..test_number { 183 | std::thread::sleep(std::time::Duration::from_secs(3)); 184 | flashlog::flash_info!("Warm up"); 185 | let start = flashlog::get_unix_nano(); 186 | for i in 0..iteration { 187 | //let log_struct = LogStruct::default(); 188 | //flashlog::flash_error!(LogStruct = log_struct); 189 | flashlog::flash_warn!("Hello"); 190 | //flashlog::flash_info!(LogStruct = log_struct); 191 | //flashlog::flash_debug!(LogStruct = log_struct); 192 | //flashlog::flash_trace!(LogStruct = log_struct); 193 | } 194 | let elapsed = flashlog::get_unix_nano() - start; 195 | res_vec.push(elapsed); 196 | } 197 | //flush!(); 198 | 199 | let ave_res: Vec = res_vec.iter().map(|x| *x as f64 / iteration as f64).collect(); 200 | 201 | for (i, res) in ave_res.iter().enumerate() { 202 | println!("Test number: {}, Elapsed time: {:.1} ns", i, res); 203 | } 204 | 205 | println!("Average time: {:.1} ns", ave_res.iter().sum::() / test_number as f64); 206 | 207 | Ok(()) 208 | } 209 | 210 | fn main() -> Result<()> { 211 | #[cfg(feature = "i32")] 212 | flashlog_i32(); 213 | 214 | #[cfg(feature = "arr")] 215 | flashlog_array_80bytes(); 216 | 217 | #[cfg(feature = "test")] 218 | test_logger(); 219 | 220 | Ok(()) 221 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # FlashLog 2 | //! 3 | //! A blazingly fast Rust logging library with lazy evaluation and compile-time filtering. 4 | //! 5 | //! [![Crates.io](https://img.shields.io/crates/v/flashlog.svg)](https://crates.io/crates/flashlog) 6 | //! [![Documentation](https://docs.rs/flashlog/badge.svg)](https://docs.rs/flashlog) 7 | //! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | //! 9 | //! ## Important Changes in Version 0.3.0 10 | //! 11 | //! 1. **Cloning in Compile-Time Macros**: In `flash_xxx_ct!` macros, cloning happens automatically inside the macro. Users don't need to clone data outside the macro, but be aware that implicit cloning still occurs in the worker thread. 12 | //! 13 | //! 2. **Timestamp Generation**: Timestamps are now generated in the logger thread rather than the worker thread. 14 | //! 15 | //! 3. **Compile-Time Feature Options**: New compile-time feature options have been added, compatible with the `flash_xxx_ct!` macros. The `flash_xxx!` macros are now deprecated in favor of the new compile-time filtered macros. 16 | //! 17 | //! ## Features 18 | //! 19 | //! - **Lazy Evaluation**: Most evaluations are performed in the logger thread, resulting in exceptional performance. 20 | //! - **Lazy String**: String interpolation in `flash_xxx!` macros is inherently lazy. 21 | //! - **Compile-Time Filtering**: Using `flash_xxx_ct!` macros and feature flags for efficient filtering at compile time. 22 | //! - **JSON Output**: Log messages are printed in `JSON` format for easy parsing and analysis. 23 | //! - **Customizable**: Flexible configuration options for file output, console reporting, buffer size, and more. 24 | //! - **Timezone Support**: Ability to set local or custom timezones for log timestamps. 25 | //! 26 | //! ## Quick Start 27 | //! 28 | //! Add FlashLog to your `Cargo.toml`: 29 | //! 30 | //! ```toml 31 | //! [dependencies] 32 | //! flashlog = {version = "0.3", features = ["max-level-info"]} 33 | //! ``` 34 | //! 35 | //! The compile time feature `max-level-info` is optional and can be omitted. It sets the maximum log level to `Info` at compile time. 36 | //! Users can still use runtime log level filtering, but the compile-time option takes precedence over runtime conditions. 37 | //! 38 | //! Available compile-time features are: 39 | //! - `max-level-off` 40 | //! - `max-level-error` 41 | //! - `max-level-warn` 42 | //! - `max-level-info` 43 | //! - `max-level-debug` 44 | //! - `max-level-trace` 45 | //! 46 | //! Basic usage example: 47 | //! 48 | //! ```rust 49 | //! use flashlog::{flash_info_ct, flash_debug_ct, flush, Logger, LogLevel, TimeZone, RollingPeriod}; 50 | //! 51 | //! pub enum Hello { 52 | //! FlashLog, 53 | //! World, 54 | //! } 55 | //! 56 | //! impl std::fmt::Display for Hello { 57 | //! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 | //! match self { 59 | //! Hello::FlashLog => write!(f, "FlashLog"), 60 | //! Hello::World => write!(f, "World"), 61 | //! } 62 | //! } 63 | //! } 64 | //! fn main() -> Result<(), Box> { 65 | //! let _logger = Logger::initialize() 66 | //! .with_file("logs", "message")? // without this the logger does not report a file 67 | //! .with_roll_period(RollingPeriod::Daily)? // Log file is rolled in daily basis 68 | //! .with_max_roll_files(10)? // Ten old file will remain. if compress is true, there will remain 10 gz file (older log) as well 69 | //! .with_compress(true)? // compress old log file 70 | //! .with_console_report(true) // true means it reports to console too 71 | //! .with_msg_flush_interval(2_000_000_000) // flushing interval is 2 billion nanoseconds = 2 seconds 72 | //! .with_msg_buffer_size(100) // messages are flushed when there are more than 100 messages 73 | //! //.with_max_log_level(LogLevel::Debug) // DEPRECATED! compile-time feature flags are recommended 74 | //! .with_timezone(TimeZone::Local) 75 | //! .include_unixnano(true) // include unixnano (u64) in the log as well as date and time 76 | //! .with_logger_core(1) // core for logger affinity 77 | //! .launch(); 78 | //! 79 | //! flash_info_ct!(Hello::FlashLog); 80 | //! // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:346","time":"20:34:30.684:921:877","topic":"World", "unixnano": 1741046422247135000} 81 | //! flash_info_ct!(Hello::World); 82 | //! // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:347","time":"20:34:30.684:922:238","topic":"FlashLog", "unixnano": 1741046422247135000} 83 | //! flash_info_ct!("Hello"); 84 | //! // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:348","time":"20:34:30.684:922:488","topic":"Hello", "unixnano": 1741046422247135000} 85 | //! flash_info_ct!("Hello"; "FlashLog"); 86 | //! // {"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:349","time":"20:34:30.684:922:739","topic":"Hello", "unixnano": 1741046422247135000} 87 | //! flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0"); 88 | //! // {"data":{"version":"0.1.0"},"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:350","time":"20:34:30.684:924:813","topic":"Hello", "unixnano": 1741046422247135000} 89 | //! flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 90 | //! // {"data":{"author":"John Doe","version":"0.1.0"},"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:351","time":"20:34:30.684:925:143","topic":"Hello", "unixnano": 1741046422247135000} 91 | //! flash_info_ct!(version = "0.1.0"); 92 | //! // {"data":{"version":"0.1.0"},"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:352","time":"20:34:30.684:925:394","topic":"", "unixnano": 1741046422247135000} 93 | //! flash_info_ct!(version = "0.1.0", author = "John Doe"); 94 | //! // {"data":{"author":"John Doe","version":"0.1.0"},"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:353","time":"20:34:30.684:925:654","topic":"", "unixnano": 1741046422247135000} 95 | //! flash_info_ct!("topic1"; "message {} {}", 1, 2); 96 | //! // {"data":"","date":"20240915","level":"Info","message":"message 1 2","offset":9,"src":"src\\logger_v2.rs:354","time":"20:34:30.684:925:955","topic":"topic1", "unixnano": 1741046422247135000} 97 | //! flash_info_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 98 | //! // {"data":{"struct_info":1,"struct_info2":2},"date":"20240915","level":"Info","message":"message 1 2","offset":9,"src":"src\\logger_v2.rs:355","time":"20:34:30.684:926:847","topic":"topic2", "unixnano": 1741046422247135000} 99 | //! flush!(); // this flushes regardless of the buffer size and flush interval 100 | //! 101 | //! Ok(()) 102 | //! } 103 | //! ``` 104 | //! 105 | //! # Benchmark Results 106 | //! 107 | //! Performance comparisons showing time taken per log message: 108 | //! 109 | //! ## Test machine: Ryzen 7 7700, 3.8 Ghz 110 | //! | Logger | i32 | 80 byte struct | 111 | //! | --------- | ------------- | --------------- | 112 | //! | flashlog | 30 ns | 40 ns | 113 | //! | ftlog | 260 ns | 480 ns | 114 | //! | fast_log | 410 ns | 358 ns | 115 | //! | slog | 250 ns | 452 ns | 116 | //! | fern | 3,813 ns | 3,962 ns | 117 | //! | tracing | 4,003 ns | 4,258 ns | 118 | //! 119 | //! ## Test machine: i5-14400F, 2.5Ghz 120 | //! 121 | //! | Logger | i32 | 80 byte struct | 122 | //! | --------- | ------------- | --------------- | 123 | //! | flashlog | 64 ns | 89 ns | 124 | //! | ftlog | 323 ns | 581 ns | 125 | //! | fast_log | 500 ns | 500 ns | 126 | //! | slog | 324 ns | 604 ns | 127 | //! | fern | 4,732 ns | 5,714 ns | 128 | //! | tracing | 5,177 ns | 6,190 ns | 129 | //! 130 | //! For more detailed information about each module and function, please refer to the individual documentation pages. 131 | 132 | 133 | pub mod timer; 134 | pub mod logger_v2; 135 | pub mod logger; 136 | pub mod rolling_file; 137 | pub mod compile_time; 138 | 139 | pub use crate::timer::{ 140 | get_unix_nano, 141 | convert_unix_nano_to_date_and_time, 142 | }; 143 | pub type UnixNano = u64; 144 | pub use crate::logger::{ 145 | LazyMessage, 146 | LogLevel, 147 | LogMessage, 148 | TimeZone, 149 | Logger, 150 | LOG_SENDER, 151 | TIMEZONE, 152 | MAX_LOG_LEVEL, 153 | }; 154 | pub use rolling_file::{ 155 | RollingConfig, 156 | RollingFileWriter, 157 | RollingPeriod, 158 | }; 159 | pub use serde_json; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlashLog 2 | 3 | A blazingly fast Rust logging library with lazy evaluation. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/flashlog.svg)](https://crates.io/crates/flashlog) 6 | [![Documentation](https://docs.rs/flashlog/badge.svg)](https://docs.rs/flashlog) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | 9 | ## Important Changes in Version 0.3.0 10 | 11 | 1. **Cloning in New Macros**: In `flash_xxx_ct!` macros, cloning happens automatically inside the macro. Users don't need to clone data outside the macro, but be aware that implicit cloning still occurs in the worker thread. 12 | 13 | 2. **Timestamp Generation**: Timestamps are now generated in the logger thread rather than the worker thread. 14 | 15 | 3. **Compile-Time Feature Options**: New compile-time feature options have been added, compatible with the `flash_xxx_ct!` macros. The `flash_xxx!` macros are now deprecated in favor of the new compile-time filtered macros. 16 | 17 | ## Features 18 | 19 | - **Lazy Evaluation**: Most evaluations are performed in the logger thread, resulting in exceptional performance. 20 | - **Lazy String**: String interpolation in `flash_xxx!` macros is inherently lazy. 21 | - **JSON Output**: Log messages are printed in `JSON` format for easy parsing and analysis. 22 | - **Customizable**: Flexible configuration options for file output, console reporting, buffer size, and more. 23 | - **Timezone Support**: Ability to set local or custom timezones for log timestamps. 24 | 25 | ## Quick Start 26 | 27 | Add FlashLog to your `Cargo.toml`: 28 | 29 | ```toml 30 | [dependencies] 31 | flashlog = {version = "0.3", features = ["max-level-info"]} 32 | ``` 33 | 34 | The compile time feature `max-level-info` is optional and can be omitted. It sets the maximum log level to `Info` at compile time. 35 | `flash_xxx_ct!` macros are filtered only by the compile-time feature not using the runtime configuration. 36 | 37 | Available compile-time features are: 38 | - `max-level-off` 39 | - `max-level-error` 40 | - `max-level-warn` 41 | - `max-level-info` 42 | - `max-level-debug` 43 | - `max-level-trace` 44 | 45 | Basic usage example: 46 | 47 | ```rust 48 | use flashlog::{Logger, LogLevel, flash_info_ct}; 49 | 50 | fn main() -> Result<(), Box> { 51 | let logger = Logger::initialize() 52 | .with_file("logs", "message")? 53 | .with_max_log_level(LogLevel::Info) 54 | .launch(); 55 | 56 | flash_info_ct!("Hello, FlashLog!"); 57 | 58 | Ok(()) 59 | } 60 | ``` 61 | 62 | ## Advanced Usage 63 | 64 | ### Logging Structs 65 | 66 | FlashLog can easily log custom structs: 67 | 68 | ```rust 69 | use serde::{Deserialize, Serialize}; 70 | use flashlog::{Logger, LogLevel, flash_info_ct}; 71 | 72 | #[derive(Debug, Serialize, Deserialize, Clone)] 73 | pub struct LogStruct { 74 | data: [u64; 10], 75 | } 76 | 77 | impl Default for LogStruct { 78 | fn default() -> Self { 79 | LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 80 | } 81 | } 82 | 83 | fn main() -> Result<(), Box> { 84 | let logger = Logger::initialize() 85 | .with_file("logs", "message")? 86 | .with_max_log_level(LogLevel::Info) 87 | .launch(); 88 | 89 | let log_struct = LogStruct::default(); 90 | flash_info_ct!("Log message"; log_struct = log_struct); 91 | 92 | Ok(()) 93 | } 94 | ``` 95 | 96 | ## Configuration and Log Interfaces 97 | 98 | Topic and message are optional and separated by a semicolon. In addition, messages can be added with key-value pairs 99 | 100 | ```rust 101 | use flashlog::{flash_info_ct, flush, Logger, LogLevel, TimeZone, RollingPeriod}; 102 | 103 | pub enum Hello { 104 | World, 105 | FlashLog, 106 | } 107 | 108 | impl std::fmt::Display for Hello { 109 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 110 | match self { 111 | Hello::World => write!(f, "World"), 112 | Hello::FlashLog => write!(f, "FlashLog"), 113 | } 114 | } 115 | } 116 | 117 | fn main() -> Result<(), Box> { 118 | let _logger = Logger::initialize() 119 | .with_file("logs", "message")? // Log to a file called "message" in the "logs" directory 120 | .with_roll_period(RollingPeriod::Daily)? // Log file is rolled in daily basis 121 | .with_max_roll_files(10)? // Ten old file will remain. if compress is true, there will remain 10 gz file (older log) as well 122 | .with_compress(false)? // compress old log file 123 | .with_console_report(true) // Enable logging to the console 124 | .with_msg_flush_interval(2_000_000_000) // Flush every 2 seconds 125 | .with_msg_buffer_size(10) // Flush when there are 10 more log messages 126 | //.with_max_log_level(LogLevel::Debug) // DEPRECATED! compile-time feature flags are recommended 127 | .with_timezone(TimeZone::Local) // Use local timezone for timestamps 128 | .include_unixnano(true) // Include unixnano in the log message 129 | .launch(); 130 | 131 | flash_info_ct!(Hello::FlashLog); 132 | // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:346","time":"20:34:30.684:921:877","topic":"World", "unixnano": 1741046422247135000} 133 | flash_info_ct!(Hello::World); 134 | // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:347","time":"20:34:30.684:922:238","topic":"FlashLog", "unixnano": 1741046422247135000} 135 | flash_info_ct!("Hello"); 136 | // {"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:348","time":"20:34:30.684:922:488","topic":"Hello", "unixnano": 1741046422247135000} 137 | flash_info_ct!("Hello"; "FlashLog"); 138 | // {"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:349","time":"20:34:30.684:922:739","topic":"Hello", "unixnano": 1741046422247135000} 139 | flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0"); 140 | // {"data":{"version":"0.1.0"},"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:350","time":"20:34:30.684:924:813","topic":"Hello", "unixnano": 1741046422247135000} 141 | flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 142 | // {"data":{"author":"John Doe","version":"0.1.0"},"date":"20240915","level":"Info","message":"FlashLog","offset":9,"src":"src\\logger_v2.rs:351","time":"20:34:30.684:925:143","topic":"Hello", "unixnano": 1741046422247135000} 143 | flash_info_ct!(version = "0.1.0"); 144 | // {"data":{"version":"0.1.0"},"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:352","time":"20:34:30.684:925:394","topic":"", "unixnano": 1741046422247135000} 145 | flash_info_ct!(version = "0.1.0", author = "John Doe"); 146 | // {"data":{"author":"John Doe","version":"0.1.0"},"date":"20240915","level":"Info","message":"","offset":9,"src":"src\\logger_v2.rs:353","time":"20:34:30.684:925:654","topic":"", "unixnano": 1741046422247135000} 147 | flash_info_ct!("topic1"; "message {} {}", 1, 2); 148 | // {"data":"","date":"20240915","level":"Info","message":"message 1 2","offset":9,"src":"src\\logger_v2.rs:354","time":"20:34:30.684:925:955","topic":"topic1", "unixnano": 1741046422247135000} 149 | flash_info_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 150 | // {"data":{"struct_info":1,"struct_info2":2},"date":"20240915","level":"Info","message":"message 1 2","offset":9,"src":"src\\logger_v2.rs:355","time":"20:34:30.684:926:847","topic":"topic2", "unixnano": 1741046422247135000} 151 | flush!(); // this flushes regardless of the buffer size and flush interval 152 | 153 | Ok(()) 154 | } 155 | ``` 156 | 157 | # Benchmark 158 | ## Test configurations 159 | Print 500,000 logs. Perform the test 5 times. Before each test, sleep for 2 seconds, then print a warm-up message, and then continuously print 500,000 messages. Test has been done on two types: i32 and 160 | 161 | ```Rust 162 | struct LogStruct { 163 | data: [u64; 10], 164 | } 165 | ``` 166 | ## message examples for the struct 167 | ### flashlog: [test-file](./examples/flashlog/src/main.rs) 168 | ```Json 169 | {"data":{"log_struct":{"data":[1,2,3,4,5,6,7,8,9,10]}},"date":"20240915","level":"Info","message":"","offset":9,"src":"src/main.rs:52","time":"20:52:02.998:044:806","topic":"Bench"} 170 | ``` 171 | 172 | ### ftlog: [test-file](./examples/ftlog/src/main.rs) 173 | ``` 174 | 2024-08-29 09:39:13.503+09 0ms INFO main [src/main.rs:57] Log message: LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 175 | ``` 176 | 177 | ### fast_log: [test-file](./examples/fast_log/src/main.rs) 178 | ``` 179 | 2024-08-29 10:31:16.7598955 [INFO] Log message: LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 180 | ``` 181 | 182 | ### slog: [test-file](./examples/slog/src/main.rs) 183 | ``` 184 | Aug 29 01:53:20.725 INFO Log message: LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 185 | ``` 186 | 187 | ### fern: [test-file](./examples/fern/src/main.rs) 188 | ``` 189 | [2024-08-29T05:59:56.608510100Z INFO example_fern] Log message: LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 190 | ``` 191 | 192 | ### tracing: [test-file](./examples/tracing/src/main.rs) 193 | ``` 194 | 2024-08-30T01:17:18.997070Z INFO example_tracing: Log message: LogStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 195 | ``` 196 | ## Performance comparisons 197 | 198 | ### Test machine: Ryzen 7 7700, 3.8 Ghz 199 | | Logger | i32 | 80 byte struct | 200 | | --------- | ------------- | --------------- | 201 | | flashlog | 30 ns | 40 ns | 202 | | ftlog | 260 ns | 480 ns | 203 | | fast_log | 410 ns | 358 ns | 204 | | slog | 250 ns | 452 ns | 205 | | fern | 3,813 ns | 3,962 ns | 206 | | tracing | 4,003 ns | 4,258 ns | 207 | 208 | ### Test machine: i5-14400F, 2.5Ghz 209 | 210 | | Logger | i32 | 80 byte struct | 211 | | --------- | ------------- | --------------- | 212 | | flashlog | 64 ns | 89 ns | 213 | | ftlog | 323 ns | 581 ns | 214 | | fast_log | 500 ns | 500 ns | 215 | | slog | 324 ns | 604 ns | 216 | | fern | 4,732 ns | 5,714 ns | 217 | | tracing | 5,177 ns | 6,190 ns | 218 | 219 | 220 | ## Contributing 221 | 222 | Contributions are welcome! Please feel free to submit a Pull Request. 223 | ## Authors 224 | 225 | - [Junbeom Lee](https://github.com/JunbeomL22) 226 | - [Youngjin Park](https://github.com/youngjin-create) 227 | 228 | ## License 229 | 230 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. -------------------------------------------------------------------------------- /examples/flashlog/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "1.1.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "android-tzdata" 22 | version = "0.1.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 25 | 26 | [[package]] 27 | name = "android_system_properties" 28 | version = "0.1.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 31 | dependencies = [ 32 | "libc", 33 | ] 34 | 35 | [[package]] 36 | name = "anyhow" 37 | version = "1.0.97" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 40 | 41 | [[package]] 42 | name = "autocfg" 43 | version = "1.4.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 46 | 47 | [[package]] 48 | name = "bitflags" 49 | version = "2.9.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 52 | 53 | [[package]] 54 | name = "bumpalo" 55 | version = "3.17.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.2.16" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" 64 | dependencies = [ 65 | "shlex", 66 | ] 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "1.0.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 73 | 74 | [[package]] 75 | name = "chrono" 76 | version = "0.4.40" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" 79 | dependencies = [ 80 | "android-tzdata", 81 | "iana-time-zone", 82 | "js-sys", 83 | "num-traits", 84 | "serde", 85 | "wasm-bindgen", 86 | "windows-link", 87 | ] 88 | 89 | [[package]] 90 | name = "chrono-tz" 91 | version = "0.10.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f" 94 | dependencies = [ 95 | "chrono", 96 | "chrono-tz-build", 97 | "phf", 98 | ] 99 | 100 | [[package]] 101 | name = "chrono-tz-build" 102 | version = "0.4.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" 105 | dependencies = [ 106 | "parse-zoneinfo", 107 | "phf_codegen", 108 | ] 109 | 110 | [[package]] 111 | name = "core-foundation-sys" 112 | version = "0.8.7" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 115 | 116 | [[package]] 117 | name = "core_affinity" 118 | version = "0.8.3" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "a034b3a7b624016c6e13f5df875747cc25f884156aad2abd12b6c46797971342" 121 | dependencies = [ 122 | "libc", 123 | "num_cpus", 124 | "winapi", 125 | ] 126 | 127 | [[package]] 128 | name = "crc32fast" 129 | version = "1.4.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 132 | dependencies = [ 133 | "cfg-if", 134 | ] 135 | 136 | [[package]] 137 | name = "crossbeam-channel" 138 | version = "0.5.14" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" 141 | dependencies = [ 142 | "crossbeam-utils", 143 | ] 144 | 145 | [[package]] 146 | name = "crossbeam-utils" 147 | version = "0.8.21" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 150 | 151 | [[package]] 152 | name = "deranged" 153 | version = "0.3.11" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 156 | dependencies = [ 157 | "powerfmt", 158 | "serde", 159 | ] 160 | 161 | [[package]] 162 | name = "example-flashlog" 163 | version = "0.1.0" 164 | dependencies = [ 165 | "anyhow", 166 | "flashlog", 167 | "serde", 168 | ] 169 | 170 | [[package]] 171 | name = "flashlog" 172 | version = "0.3.0" 173 | dependencies = [ 174 | "chrono", 175 | "chrono-tz", 176 | "core_affinity", 177 | "crossbeam-channel", 178 | "crossbeam-utils", 179 | "flate2", 180 | "lazy_static", 181 | "once_cell", 182 | "quanta", 183 | "serde", 184 | "serde_derive", 185 | "serde_json", 186 | "time", 187 | ] 188 | 189 | [[package]] 190 | name = "flate2" 191 | version = "1.1.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" 194 | dependencies = [ 195 | "crc32fast", 196 | "miniz_oxide", 197 | ] 198 | 199 | [[package]] 200 | name = "hermit-abi" 201 | version = "0.3.9" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 204 | 205 | [[package]] 206 | name = "iana-time-zone" 207 | version = "0.1.61" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 210 | dependencies = [ 211 | "android_system_properties", 212 | "core-foundation-sys", 213 | "iana-time-zone-haiku", 214 | "js-sys", 215 | "wasm-bindgen", 216 | "windows-core", 217 | ] 218 | 219 | [[package]] 220 | name = "iana-time-zone-haiku" 221 | version = "0.1.2" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 224 | dependencies = [ 225 | "cc", 226 | ] 227 | 228 | [[package]] 229 | name = "itoa" 230 | version = "1.0.15" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 233 | 234 | [[package]] 235 | name = "js-sys" 236 | version = "0.3.77" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 239 | dependencies = [ 240 | "once_cell", 241 | "wasm-bindgen", 242 | ] 243 | 244 | [[package]] 245 | name = "lazy_static" 246 | version = "1.5.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 249 | 250 | [[package]] 251 | name = "libc" 252 | version = "0.2.171" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 255 | 256 | [[package]] 257 | name = "log" 258 | version = "0.4.26" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 261 | 262 | [[package]] 263 | name = "memchr" 264 | version = "2.7.4" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 267 | 268 | [[package]] 269 | name = "miniz_oxide" 270 | version = "0.8.5" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 273 | dependencies = [ 274 | "adler2", 275 | ] 276 | 277 | [[package]] 278 | name = "num-conv" 279 | version = "0.1.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 282 | 283 | [[package]] 284 | name = "num-traits" 285 | version = "0.2.19" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 288 | dependencies = [ 289 | "autocfg", 290 | ] 291 | 292 | [[package]] 293 | name = "num_cpus" 294 | version = "1.16.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 297 | dependencies = [ 298 | "hermit-abi", 299 | "libc", 300 | ] 301 | 302 | [[package]] 303 | name = "num_threads" 304 | version = "0.1.7" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 307 | dependencies = [ 308 | "libc", 309 | ] 310 | 311 | [[package]] 312 | name = "once_cell" 313 | version = "1.21.1" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" 316 | 317 | [[package]] 318 | name = "parse-zoneinfo" 319 | version = "0.3.1" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 322 | dependencies = [ 323 | "regex", 324 | ] 325 | 326 | [[package]] 327 | name = "phf" 328 | version = "0.11.3" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 331 | dependencies = [ 332 | "phf_shared", 333 | ] 334 | 335 | [[package]] 336 | name = "phf_codegen" 337 | version = "0.11.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 340 | dependencies = [ 341 | "phf_generator", 342 | "phf_shared", 343 | ] 344 | 345 | [[package]] 346 | name = "phf_generator" 347 | version = "0.11.3" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 350 | dependencies = [ 351 | "phf_shared", 352 | "rand", 353 | ] 354 | 355 | [[package]] 356 | name = "phf_shared" 357 | version = "0.11.3" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 360 | dependencies = [ 361 | "siphasher", 362 | ] 363 | 364 | [[package]] 365 | name = "powerfmt" 366 | version = "0.2.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 369 | 370 | [[package]] 371 | name = "proc-macro2" 372 | version = "1.0.94" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 375 | dependencies = [ 376 | "unicode-ident", 377 | ] 378 | 379 | [[package]] 380 | name = "quanta" 381 | version = "0.12.5" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" 384 | dependencies = [ 385 | "crossbeam-utils", 386 | "libc", 387 | "once_cell", 388 | "raw-cpuid", 389 | "wasi", 390 | "web-sys", 391 | "winapi", 392 | ] 393 | 394 | [[package]] 395 | name = "quote" 396 | version = "1.0.40" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 399 | dependencies = [ 400 | "proc-macro2", 401 | ] 402 | 403 | [[package]] 404 | name = "rand" 405 | version = "0.8.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 408 | dependencies = [ 409 | "rand_core", 410 | ] 411 | 412 | [[package]] 413 | name = "rand_core" 414 | version = "0.6.4" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 417 | 418 | [[package]] 419 | name = "raw-cpuid" 420 | version = "11.5.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" 423 | dependencies = [ 424 | "bitflags", 425 | ] 426 | 427 | [[package]] 428 | name = "regex" 429 | version = "1.11.1" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 432 | dependencies = [ 433 | "aho-corasick", 434 | "memchr", 435 | "regex-automata", 436 | "regex-syntax", 437 | ] 438 | 439 | [[package]] 440 | name = "regex-automata" 441 | version = "0.4.9" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 444 | dependencies = [ 445 | "aho-corasick", 446 | "memchr", 447 | "regex-syntax", 448 | ] 449 | 450 | [[package]] 451 | name = "regex-syntax" 452 | version = "0.8.5" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 455 | 456 | [[package]] 457 | name = "rustversion" 458 | version = "1.0.20" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 461 | 462 | [[package]] 463 | name = "ryu" 464 | version = "1.0.20" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 467 | 468 | [[package]] 469 | name = "serde" 470 | version = "1.0.219" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 473 | dependencies = [ 474 | "serde_derive", 475 | ] 476 | 477 | [[package]] 478 | name = "serde_derive" 479 | version = "1.0.219" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 482 | dependencies = [ 483 | "proc-macro2", 484 | "quote", 485 | "syn", 486 | ] 487 | 488 | [[package]] 489 | name = "serde_json" 490 | version = "1.0.140" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 493 | dependencies = [ 494 | "itoa", 495 | "memchr", 496 | "ryu", 497 | "serde", 498 | ] 499 | 500 | [[package]] 501 | name = "shlex" 502 | version = "1.3.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 505 | 506 | [[package]] 507 | name = "siphasher" 508 | version = "1.0.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 511 | 512 | [[package]] 513 | name = "syn" 514 | version = "2.0.100" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 517 | dependencies = [ 518 | "proc-macro2", 519 | "quote", 520 | "unicode-ident", 521 | ] 522 | 523 | [[package]] 524 | name = "time" 525 | version = "0.3.39" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" 528 | dependencies = [ 529 | "deranged", 530 | "itoa", 531 | "libc", 532 | "num-conv", 533 | "num_threads", 534 | "powerfmt", 535 | "serde", 536 | "time-core", 537 | "time-macros", 538 | ] 539 | 540 | [[package]] 541 | name = "time-core" 542 | version = "0.1.3" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" 545 | 546 | [[package]] 547 | name = "time-macros" 548 | version = "0.2.20" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" 551 | dependencies = [ 552 | "num-conv", 553 | "time-core", 554 | ] 555 | 556 | [[package]] 557 | name = "unicode-ident" 558 | version = "1.0.18" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 561 | 562 | [[package]] 563 | name = "wasi" 564 | version = "0.11.0+wasi-snapshot-preview1" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 567 | 568 | [[package]] 569 | name = "wasm-bindgen" 570 | version = "0.2.100" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 573 | dependencies = [ 574 | "cfg-if", 575 | "once_cell", 576 | "rustversion", 577 | "wasm-bindgen-macro", 578 | ] 579 | 580 | [[package]] 581 | name = "wasm-bindgen-backend" 582 | version = "0.2.100" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 585 | dependencies = [ 586 | "bumpalo", 587 | "log", 588 | "proc-macro2", 589 | "quote", 590 | "syn", 591 | "wasm-bindgen-shared", 592 | ] 593 | 594 | [[package]] 595 | name = "wasm-bindgen-macro" 596 | version = "0.2.100" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 599 | dependencies = [ 600 | "quote", 601 | "wasm-bindgen-macro-support", 602 | ] 603 | 604 | [[package]] 605 | name = "wasm-bindgen-macro-support" 606 | version = "0.2.100" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 609 | dependencies = [ 610 | "proc-macro2", 611 | "quote", 612 | "syn", 613 | "wasm-bindgen-backend", 614 | "wasm-bindgen-shared", 615 | ] 616 | 617 | [[package]] 618 | name = "wasm-bindgen-shared" 619 | version = "0.2.100" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 622 | dependencies = [ 623 | "unicode-ident", 624 | ] 625 | 626 | [[package]] 627 | name = "web-sys" 628 | version = "0.3.77" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 631 | dependencies = [ 632 | "js-sys", 633 | "wasm-bindgen", 634 | ] 635 | 636 | [[package]] 637 | name = "winapi" 638 | version = "0.3.9" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 641 | dependencies = [ 642 | "winapi-i686-pc-windows-gnu", 643 | "winapi-x86_64-pc-windows-gnu", 644 | ] 645 | 646 | [[package]] 647 | name = "winapi-i686-pc-windows-gnu" 648 | version = "0.4.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 651 | 652 | [[package]] 653 | name = "winapi-x86_64-pc-windows-gnu" 654 | version = "0.4.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 657 | 658 | [[package]] 659 | name = "windows-core" 660 | version = "0.52.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 663 | dependencies = [ 664 | "windows-targets", 665 | ] 666 | 667 | [[package]] 668 | name = "windows-link" 669 | version = "0.1.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" 672 | 673 | [[package]] 674 | name = "windows-targets" 675 | version = "0.52.6" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 678 | dependencies = [ 679 | "windows_aarch64_gnullvm", 680 | "windows_aarch64_msvc", 681 | "windows_i686_gnu", 682 | "windows_i686_gnullvm", 683 | "windows_i686_msvc", 684 | "windows_x86_64_gnu", 685 | "windows_x86_64_gnullvm", 686 | "windows_x86_64_msvc", 687 | ] 688 | 689 | [[package]] 690 | name = "windows_aarch64_gnullvm" 691 | version = "0.52.6" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 694 | 695 | [[package]] 696 | name = "windows_aarch64_msvc" 697 | version = "0.52.6" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 700 | 701 | [[package]] 702 | name = "windows_i686_gnu" 703 | version = "0.52.6" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 706 | 707 | [[package]] 708 | name = "windows_i686_gnullvm" 709 | version = "0.52.6" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 712 | 713 | [[package]] 714 | name = "windows_i686_msvc" 715 | version = "0.52.6" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 718 | 719 | [[package]] 720 | name = "windows_x86_64_gnu" 721 | version = "0.52.6" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 724 | 725 | [[package]] 726 | name = "windows_x86_64_gnullvm" 727 | version = "0.52.6" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 730 | 731 | [[package]] 732 | name = "windows_x86_64_msvc" 733 | version = "0.52.6" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 736 | -------------------------------------------------------------------------------- /src/logger_v2.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! flush { 3 | () => {{ 4 | $crate::LOG_SENDER.try_send($crate::LogMessage::Flush).expect("Failed to send flush message"); 5 | }}; 6 | } 7 | 8 | #[macro_export] 9 | macro_rules! log_with_level { 10 | // Case 1: topic, format string argument arguments, and key-value pairs 11 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* ; $($key:ident = $value:expr),+ $(,)?) => {{ 12 | $crate::log_fn_json_v2!($level, $topic; $fmt, $($arg),*; $($key = $value),+); 13 | }}; 14 | 15 | // Case 2: topic and static string, and key-value pairs 16 | ($level:expr, $topic:expr; $msg:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 17 | $crate::log_fn_json_v2!($level, $topic; $msg; $($key = $value),+); 18 | }}; 19 | 20 | // Case 3: topic and key-value pairs 21 | ($level:expr, $topic:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 22 | $crate::log_fn_json_v2!($level, $topic; ""; $($key = $value),+); 23 | }}; 24 | 25 | // Case 4: topic and format string with arguments 26 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* $(,)?) => {{ 27 | $crate::log_fn_json_v2!($level, $topic; $fmt, $($arg),*); 28 | }}; 29 | 30 | // Case 5: topic and static string 31 | ($level:expr, $topic:expr; $msg:expr) => {{ 32 | $crate::log_fn_json_v2!($level, $topic; $msg); 33 | }}; 34 | 35 | // Case 6: Topic only 36 | ($level:expr, $topic:expr) => {{ 37 | $crate::log_fn_json_v2!($level, $topic; ""); 38 | }}; 39 | 40 | // **Case 7: Single key-value pair without topic** 41 | ($level:expr, $key:ident = $value:expr $(,)?) => {{ 42 | $crate::log_fn_json_v2!($level, $key = $value); 43 | }}; 44 | 45 | // **Case 8: Multiple key-value pairs without topic** 46 | ($level:expr, $($key:ident = $value:expr),+ $(,)?) => {{ 47 | $crate::log_fn_json_v2!($level, $($key = $value),+); 48 | }}; 49 | } 50 | 51 | #[deprecated(since = "0.3.0", note = "Use flash_trace_ct! instead")] 52 | #[macro_export] 53 | macro_rules! flash_trace { 54 | // Handle one or more key-value pairs without a topic 55 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 56 | $crate::log_with_level!($crate::LogLevel::Trace, ""; $( $key = $value ),+ ); 57 | }; 58 | 59 | // Handle all other cases (e.g., with topic, message, etc.) 60 | ( $($args:tt)* ) => { 61 | $crate::log_with_level!($crate::LogLevel::Trace, $($args)* ); 62 | }; 63 | } 64 | 65 | #[deprecated(since = "0.3.0", note = "Use flash_debug_ct! instead")] 66 | #[macro_export] 67 | macro_rules! flash_debug { 68 | // Handle one or more key-value pairs without a topic 69 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 70 | $crate::log_with_level!($crate::LogLevel::Debug, ""; $( $key = $value ),+ ) 71 | }; 72 | // Handle all other cases (e.g., with topic, message, etc.) 73 | ( $($args:tt)* ) => { 74 | $crate::log_with_level!($crate::LogLevel::Debug, $($args)* ) 75 | }; 76 | } 77 | 78 | #[deprecated(since = "0.3.0", note = "Use flash_info_ct! instead")] 79 | #[macro_export] 80 | macro_rules! flash_info { 81 | // Handle one or more key-value pairs without a topic 82 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 83 | $crate::log_with_level!($crate::LogLevel::Info, ""; $( $key = $value ),+ ); 84 | }; 85 | // Handle all other cases (e.g., with topic, message, etc.) 86 | ( $($args:tt)* ) => { 87 | $crate::log_with_level!($crate::LogLevel::Info, $($args)* ); 88 | }; 89 | } 90 | 91 | #[deprecated(since = "0.3.0", note = "Use flash_warn_ct! instead")] 92 | #[macro_export] 93 | macro_rules! flash_warn { 94 | // Handle one or more key-value pairs without a topic 95 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 96 | $crate::log_with_level!($crate::LogLevel::Warn, ""; $( $key = $value ),+ ) 97 | }; 98 | // Handle all other cases (e.g., with topic, message, etc.) 99 | ( $($args:tt)* ) => { 100 | $crate::log_with_level!($crate::LogLevel::Warn, $($args)* ) 101 | }; 102 | } 103 | 104 | #[deprecated(since = "0.3.0", note = "Use flash_error_ct! instead")] 105 | #[macro_export] 106 | macro_rules! flash_error { 107 | // Handle one or more key-value pairs without a topic 108 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 109 | $crate::log_with_level!($crate::LogLevel::Error, ""; $( $key = $value ),+ ) 110 | }; 111 | // Handle all other cases (e.g., with topic, message, etc.) 112 | ( $($args:tt)* ) => { 113 | $crate::log_with_level!($crate::LogLevel::Error, $($args)* ) 114 | }; 115 | } 116 | 117 | #[macro_export] 118 | macro_rules! log_fn_json_v2 { 119 | // Case 1: topic, format sring, kv 120 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),*; $($key:ident = $value:expr),+ $(,)?) => {{ 121 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 122 | let func = move || { 123 | let unixnano = $crate::get_unix_nano(); 124 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 125 | let json_obj = $crate::serde_json::json!({ 126 | $( 127 | stringify!($key): $value, 128 | )+ 129 | }); 130 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 131 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 132 | 133 | let json_msg = match include_unixnano { 134 | false => $crate::serde_json::json!({ 135 | "date": date, 136 | "time": time, 137 | "offset": timezone, 138 | "level": $level.to_string(), 139 | "src": format!("{}:{}", file!(), line!()), 140 | "topic": $topic, 141 | "data": json_obj, 142 | "message": format!($fmt, $($arg),*), 143 | }), 144 | true => $crate::serde_json::json!({ 145 | "date": date, 146 | "time": time, 147 | "offset": timezone, 148 | "level": $level.to_string(), 149 | "src": format!("{}:{}", file!(), line!()), 150 | "topic": $topic, 151 | "data": json_obj, 152 | "message": format!($fmt, $($arg),*), 153 | "unixnano": unixnano, 154 | }), 155 | }; 156 | 157 | json_msg.to_string() + "\n" 158 | }; 159 | 160 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 161 | } 162 | }}; 163 | 164 | // Case 2: topic, static string, kv 165 | ($level:expr, $topic:expr; $msg:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 166 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 167 | let func = move || { 168 | let unixnano = $crate::get_unix_nano(); 169 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 170 | let json_obj = $crate::serde_json::json!({ 171 | $( 172 | stringify!($key): $value, 173 | )+ 174 | }); 175 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 176 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 177 | let json_msg = match include_unixnano { 178 | false => $crate::serde_json::json!({ 179 | "date": date, 180 | "time": time, 181 | "offset": timezone, 182 | "level": $level.to_string(), 183 | "src": format!("{}:{}", file!(), line!()), 184 | "topic": $topic, 185 | "data": json_obj, 186 | "message": $msg, 187 | }), 188 | true => $crate::serde_json::json!({ 189 | "date": date, 190 | "time": time, 191 | "offset": timezone, 192 | "level": $level.to_string(), 193 | "src": format!("{}:{}", file!(), line!()), 194 | "topic": $topic, 195 | "data": json_obj, 196 | "message": $msg, 197 | "unixnano": unixnano, 198 | }), 199 | }; 200 | 201 | json_msg.to_string() + "\n" 202 | }; 203 | 204 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 205 | } 206 | }}; 207 | 208 | // Case 3: topic and formated string 209 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* $(,)?) => {{ 210 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 211 | let func = move || { 212 | let unixnano = $crate::get_unix_nano(); 213 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 214 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 215 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 216 | let json_msg = match include_unixnano { 217 | false => $crate::serde_json::json!({ 218 | "date": date, 219 | "time": time, 220 | "offset": timezone, 221 | "level": $level.to_string(), 222 | "src": format!("{}:{}", file!(), line!()), 223 | "topic": $topic, 224 | "message": format!($fmt, $($arg),*), 225 | "data": "", 226 | }), 227 | true => $crate::serde_json::json!({ 228 | "date": date, 229 | "time": time, 230 | "offset": timezone, 231 | "level": $level.to_string(), 232 | "src": format!("{}:{}", file!(), line!()), 233 | "topic": $topic, 234 | "message": format!($fmt, $($arg),*), 235 | "data": "", 236 | "unixnano": unixnano, 237 | }), 238 | }; 239 | json_msg.to_string() + "\n" 240 | }; 241 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 242 | } 243 | }}; 244 | 245 | // Case 4: topic and static string 246 | ($level:expr, $topic:expr; $msg:expr $(,)?) => {{ 247 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 248 | let func = move || { 249 | let unixnano = $crate::get_unix_nano(); 250 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 251 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 252 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 253 | let json_msg = match include_unixnano { 254 | false => $crate::serde_json::json!({ 255 | "date": date, 256 | "time": time, 257 | "offset": timezone, 258 | "level": $level.to_string(), 259 | "src": format!("{}:{}", file!(), line!()), 260 | "topic": $topic.to_string(), 261 | "message": $msg 262 | }), 263 | true => $crate::serde_json::json!({ 264 | "date": date, 265 | "time": time, 266 | "offset": timezone, 267 | "level": $level.to_string(), 268 | "src": format!("{}:{}", file!(), line!()), 269 | "topic": $topic.to_string(), 270 | "message": $msg, 271 | "unixnano": unixnano, 272 | }), 273 | }; 274 | 275 | json_msg.to_string() + "\n" 276 | }; 277 | 278 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 279 | } 280 | }}; 281 | 282 | // **Case 7: Single key-value pair without topic** 283 | ($level:expr, $key:ident = $value:expr) => {{ 284 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 285 | let func = move || { 286 | let unixnano = $crate::get_unix_nano(); 287 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 288 | let json_obj = $crate::serde_json::json!({ 289 | $( 290 | stringify!($key): $value, 291 | )+ 292 | }); 293 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 294 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 295 | let json_msg = match include_unixnano { 296 | false => $crate::serde_json::json!({ 297 | "date": date, 298 | "time": time, 299 | "offset": timezone, 300 | "level": $level.to_string(), 301 | "src": format!("{}:{}", file!(), line!()), 302 | "topic": "", 303 | "data": json_obj, 304 | "message": "", 305 | }), 306 | true => $crate::serde_json::json!({ 307 | "date": date, 308 | "time": time, 309 | "offset": timezone, 310 | "level": $level.to_string(), 311 | "src": format!("{}:{}", file!(), line!()), 312 | "topic": "", 313 | "data": json_obj, 314 | "message": "", 315 | "unixnano": unixnano, 316 | }), 317 | }; 318 | json_msg.to_string() + "\n" 319 | }; 320 | 321 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 322 | } 323 | }}; 324 | 325 | // **Case 8: Multiple key-value pairs without topic** 326 | ($level:expr, $($key:ident = $value:expr),+ $(,)?) => {{ 327 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 328 | let func = move || { 329 | let unixnano = $crate::get_unix_nano(); 330 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 331 | let json_obj = $crate::serde_json::json!({ 332 | $( 333 | stringify!($key): $value, 334 | )+ 335 | }); 336 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 337 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 338 | let json_msg = match include_unixnano { 339 | false => $crate::serde_json::json!({ 340 | "date": date, 341 | "time": time, 342 | "offset": timezone, 343 | "level": $level.to_string(), 344 | "src": format!("{}:{}", file!(), line!()), 345 | "data": json_obj, 346 | "topic": "", 347 | "message": "", 348 | }), 349 | true => $crate::serde_json::json!({ 350 | "date": date, 351 | "time": time, 352 | "offset": timezone, 353 | "level": $level.to_string(), 354 | "src": format!("{}:{}", file!(), line!()), 355 | "data": json_obj, 356 | "topic": "", 357 | "message": "", 358 | "unixnano": unixnano, 359 | }), 360 | }; 361 | json_msg.to_string() + "\n" 362 | }; 363 | 364 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).expect("Failed to send log message"); 365 | } 366 | }}; 367 | } 368 | 369 | #[cfg(test)] 370 | mod tests { 371 | use anyhow::Result; 372 | use serde::Serialize; 373 | use crate::{Logger, LogLevel}; 374 | use crate::TimeZone; 375 | 376 | #[derive(Debug, Clone, Serialize)] 377 | pub enum Hello { 378 | World, 379 | FlashLog, 380 | } 381 | 382 | impl std::fmt::Display for Hello { 383 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 384 | match self { 385 | Hello::World => write!(f, "World"), 386 | Hello::FlashLog => write!(f, "FlashLog"), 387 | } 388 | } 389 | } 390 | 391 | #[derive(Debug, Serialize, Clone)] 392 | pub struct TestStruct { 393 | pub test: i32, 394 | } 395 | 396 | #[derive(Debug, Serialize, Clone)] 397 | pub struct TestStruct2 { 398 | pub test: i32, 399 | } 400 | 401 | #[test] 402 | fn test_logger() -> Result<()> { 403 | let _guard = Logger::initialize() 404 | //.with_file("logs", "message")? 405 | .with_console_report(true) 406 | .with_msg_buffer_size(1_000_000) 407 | .with_msg_flush_interval(1_000_000_000) 408 | .with_max_log_level(LogLevel::Trace) 409 | .with_timezone(TimeZone::Local) 410 | .include_unixnano(false) 411 | .launch(); 412 | 413 | flash_error!(Hello::FlashLog); 414 | flash_error!(Hello::World); 415 | flash_error!("Hello"); 416 | flash_error!("Hello"; "FlashLog"); 417 | flash_error!("Hello"; "FlashLog"; version = "0.1.0"); 418 | flash_error!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 419 | flash_error!(version = "0.1.0"); 420 | flash_error!(version = "0.1.0", author = "John Doe"); 421 | flash_error!("topic1"; "message {} {}", 1, 2); 422 | flash_error!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 423 | flash_error!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 424 | let test_info = TestStruct { test: 1 }; 425 | let test_info2 = TestStruct2 { test: 2 }; 426 | flash_error!("topic2"; "message {} {}", 1, 2; TestStruct = test_info, TestStruct2 = test_info2); 427 | flush!(); // this flushes regardless of the buffer size and flush interval 428 | 429 | flash_warn!(Hello::World); 430 | flash_warn!("Hello"); 431 | flash_warn!("Hello"; "FlashLog"); 432 | flash_warn!("Hello"; "FlashLog"; version = "0.1.0"); 433 | flash_warn!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 434 | flash_warn!(version = "0.1.0"); 435 | flash_warn!(version = "0.1.0", author = "John Doe"); 436 | flash_warn!("topic1"; "message {} {}", 1, 2); 437 | flash_warn!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 438 | flash_warn!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 439 | let test_info = TestStruct { test: 1 }; 440 | flash_warn!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 441 | 442 | flush!(); // this flushes regardless of the buffer size and flush interval 443 | 444 | flash_info!(Hello::World); 445 | flash_info!(Hello::FlashLog); 446 | flash_info!("Hello"); 447 | flash_info!("Hello"; "FlashLog"); 448 | flash_info!("Hello"; "FlashLog"; version = "0.1.0"); 449 | flash_info!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 450 | flash_info!(version = "0.1.0"); 451 | flash_info!(version = "0.1.0", author = "John Doe"); 452 | flash_info!("topic1"; "message {} {}", 1, 2); 453 | flash_info!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 454 | flash_info!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 455 | let test_info = TestStruct { test: 1 }; 456 | flash_info!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 457 | 458 | flush!(); // this flushes regardless of the buffer size and flush interval 459 | 460 | flash_debug!(Hello::World); 461 | flash_debug!("Hello"); 462 | flash_debug!("Hello"; "FlashLog"); 463 | flash_debug!("Hello"; "FlashLog"; version = "0.1.0"); 464 | flash_debug!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 465 | flash_debug!(version = "0.1.0"); 466 | flash_debug!(version = "0.1.0", author = "John Doe"); 467 | flash_debug!("topic1"; "message {} {}", 1, 2); 468 | flash_debug!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 469 | flash_debug!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 470 | let test_info = TestStruct { test: 1 }; 471 | flash_debug!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 472 | 473 | flush!(); // this flushes regardless of the buffer size and flush interval 474 | 475 | flash_trace!(Hello::World); 476 | flash_trace!("Hello"); 477 | flash_trace!("Hello"; "FlashLog"); 478 | flash_trace!("Hello"; "FlashLog"; version = "0.1.0"); 479 | flash_trace!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 480 | flash_trace!(version = "0.1.0"); 481 | flash_trace!(version = "0.1.0", author = "John Doe"); 482 | flash_trace!("topic1"; "message {} {}", 1, 2); 483 | flash_trace!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 484 | flash_trace!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 485 | let test_info = TestStruct { test: 1 }; 486 | flash_trace!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 487 | 488 | crate::flush!(); 489 | 490 | assert!(true); 491 | 492 | Ok(()) 493 | } 494 | } -------------------------------------------------------------------------------- /src/compile_time.rs: -------------------------------------------------------------------------------- 1 | pub const TRACE: usize = 5; 2 | pub const DEBUG: usize = 4; 3 | pub const INFO: usize = 3; 4 | pub const WARN: usize = 2; 5 | pub const ERROR: usize = 1; 6 | pub const OFF: usize = 0; 7 | 8 | pub const MAX_LEVEL: usize = if cfg!(feature = "max-level-trace") { 9 | TRACE 10 | } else if cfg!(feature = "max-level-debug") { 11 | DEBUG 12 | } else if cfg!(feature = "max-level-info") { 13 | INFO 14 | } else if cfg!(feature = "max-level-warn") { 15 | WARN 16 | } else if cfg!(feature = "max-level-error") { 17 | ERROR 18 | } else if cfg!(feature = "max-level-off") { 19 | OFF 20 | } else { 21 | TRACE 22 | }; 23 | 24 | #[macro_export] 25 | macro_rules! log_with_level_ct { 26 | // Case 1: topic, format string argument arguments, and key-value pairs 27 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* ; $($key:ident = $value:expr),+ $(,)?) => {{ 28 | $crate::log_fn_json_v3!($level, $topic; $fmt, $($arg),*; $($key = $value),+); 29 | }}; 30 | 31 | // Case 2: topic and static string, and key-value pairs 32 | ($level:expr, $topic:expr; $msg:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 33 | $crate::log_fn_json_v3!($level, $topic; $msg; $($key = $value),+); 34 | }}; 35 | 36 | // Case 3: topic and key-value pairs 37 | ($level:expr, $topic:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 38 | $crate::log_fn_json_v3!($level, $topic; ""; $($key = $value),+); 39 | }}; 40 | 41 | // Case 4: topic and format string with arguments 42 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* $(,)?) => {{ 43 | $crate::log_fn_json_v3!($level, $topic; $fmt, $($arg),*); 44 | }}; 45 | 46 | // Case 5: topic and static string 47 | ($level:expr, $topic:expr; $msg:expr) => {{ 48 | $crate::log_fn_json_v3!($level, $topic; $msg); 49 | }}; 50 | 51 | // Case 6: Topic only 52 | ($level:expr, $topic:expr) => {{ 53 | $crate::log_fn_json_v3!($level, $topic; ""); 54 | }}; 55 | 56 | // **Case 7: Single key-value pair without topic** 57 | ($level:expr, $key:ident = $value:expr $(,)?) => {{ 58 | $crate::log_fn_json_v3!($level, $key = $value); 59 | }}; 60 | 61 | // **Case 8: Multiple key-value pairs without topic** 62 | ($level:expr, $($key:ident = $value:expr),+ $(,)?) => {{ 63 | $crate::log_fn_json_v3!($level, $($key = $value),+); 64 | }}; 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! flash_trace_ct { 69 | // Handle one or more key-value pairs without a topic 70 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 71 | $crate::log_with_level_ct!($crate::compile_time::TRACE, ""; $( $key = $value ),+ ); 72 | }; 73 | 74 | // Handle all other cases (e.g., with topic, message, etc.) 75 | ( $($args:tt)* ) => { 76 | $crate::log_with_level_ct!($crate::compile_time::TRACE, $($args)* ); 77 | }; 78 | } 79 | 80 | #[macro_export] 81 | macro_rules! flash_debug_ct { 82 | // Handle one or more key-value pairs without a topic 83 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 84 | $crate::log_with_level_ct!($crate::compile_time::DEBUG, ""; $( $key = $value ),+ ) 85 | }; 86 | // Handle all other cases (e.g., with topic, message, etc.) 87 | ( $($args:tt)* ) => { 88 | $crate::log_with_level_ct!($crate::compile_time::DEBUG, $($args)* ) 89 | }; 90 | } 91 | 92 | #[macro_export] 93 | macro_rules! flash_info_ct { 94 | // Handle one or more key-value pairs without a topic 95 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 96 | $crate::log_with_level_ct!($crate::compile_time::INFO, ""; $( $key = $value ),+ ); 97 | }; 98 | // Handle all other cases (e.g., with topic, message, etc.) 99 | ( $($args:tt)* ) => { 100 | $crate::log_with_level_ct!($crate::compile_time::INFO, $($args)* ); 101 | }; 102 | } 103 | 104 | #[macro_export] 105 | macro_rules! flash_warn_ct { 106 | // Handle one or more key-value pairs without a topic 107 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 108 | $crate::log_with_level_ct!($crate::compile_time::WARN, ""; $( $key = $value ),+ ) 109 | }; 110 | // Handle all other cases (e.g., with topic, message, etc.) 111 | ( $($args:tt)* ) => { 112 | $crate::log_with_level_ct!($crate::compile_time::WARN, $($args)* ) 113 | }; 114 | } 115 | 116 | #[macro_export] 117 | macro_rules! flash_error_ct { 118 | // Handle one or more key-value pairs without a topic 119 | ( $( $key:ident = $value:expr ),+ $(,)? ) => { 120 | $crate::log_with_level_ct!($crate::compile_time::ERROR, ""; $( $key = $value ),+ ) 121 | }; 122 | // Handle all other cases (e.g., with topic, message, etc.) 123 | ( $($args:tt)* ) => { 124 | $crate::log_with_level_ct!($crate::compile_time::ERROR, $($args)* ) 125 | }; 126 | } 127 | 128 | #[inline] 129 | pub fn usize_to_level(level: usize) -> &'static str { 130 | match level { 131 | 5 => "Trace", 132 | 4 => "Debug", 133 | 3 => "Info", 134 | 2 => "Warn", 135 | 1 => "Error", 136 | 0 => "Off", 137 | _ => "Unknown", 138 | } 139 | } 140 | 141 | #[macro_export] 142 | macro_rules! log_fn_json_v3 { 143 | // Case 1: topic, format sring, kv 144 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),*; $($key:ident = $value:expr),+ $(,)?) => {{ 145 | if $level <= $crate::compile_time::MAX_LEVEL { 146 | $( 147 | #[allow(non_snake_case)] 148 | let $key = $value.clone(); 149 | )+ 150 | 151 | let func = move || { 152 | let json_obj = $crate::serde_json::json!({ 153 | $( 154 | stringify!($key): $key, 155 | )+ 156 | }); 157 | let unixnano = $crate::get_unix_nano(); 158 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 159 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 160 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 161 | 162 | let json_msg = match include_unixnano { 163 | false => $crate::serde_json::json!({ 164 | "date": date, 165 | "time": time, 166 | "offset": timezone, 167 | "level": $crate::compile_time::usize_to_level($level), 168 | "src": format!("{}:{}", file!(), line!()), 169 | "topic": $topic, 170 | "data": json_obj, 171 | "message": format!($fmt, $($arg),*), 172 | }), 173 | true => $crate::serde_json::json!({ 174 | "date": date, 175 | "time": time, 176 | "offset": timezone, 177 | "level": $crate::compile_time::usize_to_level($level), 178 | "src": format!("{}:{}", file!(), line!()), 179 | "topic": $topic, 180 | "data": json_obj, 181 | "message": format!($fmt, $($arg),*), 182 | "unixnano": unixnano, 183 | }), 184 | }; 185 | 186 | json_msg.to_string() + "\n" 187 | }; 188 | 189 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 190 | } 191 | }}; 192 | 193 | // Case 2: topic, static string, kv 194 | ($level:expr, $topic:expr; $msg:expr; $($key:ident = $value:expr),+ $(,)?) => {{ 195 | if $level <= $crate::compile_time::MAX_LEVEL { 196 | $( 197 | #[allow(non_snake_case)] 198 | let $key = $value.clone(); 199 | )* 200 | let func = move || { 201 | let json_obj = $crate::serde_json::json!({ 202 | $( 203 | stringify!($key): $key, 204 | )+ 205 | }); 206 | let unixnano = $crate::get_unix_nano(); 207 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 208 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 209 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 210 | let json_msg = match include_unixnano { 211 | false => $crate::serde_json::json!({ 212 | "date": date, 213 | "time": time, 214 | "offset": timezone, 215 | "level": $crate::compile_time::usize_to_level($level), 216 | "src": format!("{}:{}", file!(), line!()), 217 | "topic": $topic, 218 | "data": json_obj, 219 | "message": $msg, 220 | }), 221 | true => $crate::serde_json::json!({ 222 | "date": date, 223 | "time": time, 224 | "offset": timezone, 225 | "level": $crate::compile_time::usize_to_level($level), 226 | "src": format!("{}:{}", file!(), line!()), 227 | "topic": $topic, 228 | "data": json_obj, 229 | "message": $msg, 230 | "unixnano": unixnano, 231 | }), 232 | }; 233 | 234 | json_msg.to_string() + "\n" 235 | }; 236 | 237 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))) 238 | .unwrap(); 239 | } 240 | }}; 241 | 242 | // Case 3: topic and formated string 243 | ($level:expr, $topic:expr; $fmt:expr, $($arg:expr),* $(,)?) => {{ 244 | if $level <= $crate::compile_time::MAX_LEVEL { 245 | let func = move || { 246 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 247 | let unixnano = $crate::get_unix_nano(); 248 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 249 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 250 | let json_msg = match include_unixnano { 251 | false => $crate::serde_json::json!({ 252 | "date": date, 253 | "time": time, 254 | "offset": timezone, 255 | "level": $crate::compile_time::usize_to_level($level), 256 | "src": format!("{}:{}", file!(), line!()), 257 | "topic": $topic, 258 | "message": format!($fmt, $($arg),*), 259 | "data": "", 260 | }), 261 | true => $crate::serde_json::json!({ 262 | "date": date, 263 | "time": time, 264 | "offset": timezone, 265 | "level": $crate::compile_time::usize_to_level($level), 266 | "src": format!("{}:{}", file!(), line!()), 267 | "topic": $topic, 268 | "message": format!($fmt, $($arg),*), 269 | "data": "", 270 | "unixnano": unixnano, 271 | }), 272 | }; 273 | json_msg.to_string() + "\n" 274 | }; 275 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 276 | } 277 | }}; 278 | 279 | // Case 4: topic and static string 280 | ($level:expr, $topic:expr; $msg:expr $(,)?) => {{ 281 | if $level <= $crate::compile_time::MAX_LEVEL { 282 | let func = move || { 283 | let unixnano = $crate::get_unix_nano(); 284 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 285 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 286 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 287 | let json_msg = match include_unixnano { 288 | false => $crate::serde_json::json!({ 289 | "date": date, 290 | "time": time, 291 | "offset": timezone, 292 | "level": $crate::compile_time::usize_to_level($level), 293 | "src": format!("{}:{}", file!(), line!()), 294 | "topic": $topic.to_string(), 295 | "message": $msg 296 | }), 297 | true => $crate::serde_json::json!({ 298 | "date": date, 299 | "time": time, 300 | "offset": timezone, 301 | "level": $crate::compile_time::usize_to_level($level), 302 | "src": format!("{}:{}", file!(), line!()), 303 | "topic": $topic.to_string(), 304 | "message": $msg, 305 | "unixnano": unixnano, 306 | }), 307 | }; 308 | 309 | json_msg.to_string() + "\n" 310 | }; 311 | 312 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 313 | } 314 | }}; 315 | 316 | // **Case 7: Single key-value pair without topic** 317 | ($level:expr, $key:ident = $value:expr) => {{ 318 | if $level <= $crate::compile_time::MAX_LEVEL { 319 | $( 320 | #[allow(non_snake_case)] 321 | let $key = $value.clone(); 322 | )* 323 | let func = move || { 324 | let unixnano = $crate::get_unix_nano(); 325 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 326 | let json_obj = $crate::serde_json::json!({ 327 | $( 328 | stringify!($key): $key, 329 | )+ 330 | }); 331 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 332 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 333 | let json_msg = match include_unixnano { 334 | false => $crate::serde_json::json!({ 335 | "date": date, 336 | "time": time, 337 | "offset": timezone, 338 | "level": $crate::compile_time::usize_to_level($level), 339 | "src": format!("{}:{}", file!(), line!()), 340 | "topic": "", 341 | "data": json_obj, 342 | "message": "", 343 | }), 344 | true => $crate::serde_json::json!({ 345 | "date": date, 346 | "time": time, 347 | "offset": timezone, 348 | "level": $crate::compile_time::usize_to_level($level), 349 | "src": format!("{}:{}", file!(), line!()), 350 | "topic": "", 351 | "data": json_obj, 352 | "message": "", 353 | "unixnano": unixnano, 354 | }), 355 | }; 356 | json_msg.to_string() + "\n" 357 | }; 358 | 359 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 360 | } 361 | }}; 362 | 363 | // **Case 8: Multiple key-value pairs without topic** 364 | ($level:expr, $($key:ident = $value:expr),+ $(,)?) => {{ 365 | if $level <= $crate::compile_time::MAX_LEVEL { 366 | $( 367 | #[allow(non_snake_case)] 368 | let $key = $value.clone(); 369 | )* 370 | let func = move || { 371 | let unixnano = $crate::get_unix_nano(); 372 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 373 | let json_obj = $crate::serde_json::json!({ 374 | $( 375 | stringify!($key): $key, 376 | )+ 377 | }); 378 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 379 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 380 | let json_msg = match include_unixnano { 381 | false => $crate::serde_json::json!({ 382 | "date": date, 383 | "time": time, 384 | "offset": timezone, 385 | "level": $crate::compile_time::usize_to_level($level), 386 | "src": format!("{}:{}", file!(), line!()), 387 | "data": json_obj, 388 | "topic": "", 389 | "message": "", 390 | }), 391 | true => $crate::serde_json::json!({ 392 | "date": date, 393 | "time": time, 394 | "offset": timezone, 395 | "level": $crate::compile_time::usize_to_level($level), 396 | "src": format!("{}:{}", file!(), line!()), 397 | "data": json_obj, 398 | "topic": "", 399 | "message": "", 400 | "unixnano": unixnano, 401 | }), 402 | }; 403 | json_msg.to_string() + "\n" 404 | }; 405 | 406 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 407 | } 408 | }}; 409 | } 410 | 411 | #[cfg(test)] 412 | mod tests { 413 | use crate::logger::Logger; 414 | use crate::logger::LogLevel; 415 | use crate::logger::TimeZone; 416 | use anyhow::Result; 417 | use serde::Serialize; 418 | 419 | #[derive(Debug, Clone, Serialize)] 420 | pub enum Hello { 421 | FlashLog, 422 | World, 423 | } 424 | 425 | impl std::fmt::Display for Hello { 426 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 427 | match self { 428 | Hello::FlashLog => write!(f, "FlashLog"), 429 | Hello::World => write!(f, "World"), 430 | } 431 | } 432 | } 433 | 434 | #[derive(Debug, Clone, Serialize)] 435 | pub struct TestStruct { 436 | test: i32, 437 | } 438 | 439 | #[derive(Debug, Clone, Serialize)] 440 | pub struct TestStruct2 { 441 | test: i32, 442 | } 443 | 444 | #[test] 445 | fn test_ct() -> Result<()> { 446 | let _guard = Logger::initialize() 447 | //.with_file("logs", "message")? 448 | .with_console_report(true) 449 | .with_msg_buffer_size(1_000_000) 450 | .with_msg_flush_interval(1_000_000_000) 451 | .with_max_log_level(LogLevel::Trace) 452 | .with_timezone(TimeZone::Local) 453 | .include_unixnano(false) 454 | .launch(); 455 | 456 | flash_error_ct!(Hello::FlashLog); 457 | flash_error_ct!(Hello::World); 458 | flash_error_ct!("Hello"); 459 | flash_error_ct!("Hello"; "FlashLog"); 460 | flash_error_ct!("Hello"; "FlashLog"; version = "0.1.0"); 461 | flash_error_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 462 | flash_error_ct!(version = "0.1.0"); 463 | flash_error_ct!(version = "0.1.0", author = "John Doe"); 464 | flash_error_ct!("topic1"; "message {} {}", 1, 2); 465 | flash_error_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 466 | flash_error_ct!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 467 | let test_info = TestStruct { test: 1 }; 468 | let test_info2 = TestStruct2 { test: 2 }; 469 | flash_error_ct!("topic2"; "message {} {}", 1, 2; TestStruct = test_info, TestStruct2 = test_info2); 470 | println!("{:?}", test_info); // still alive 471 | crate::flush!(); // this flushes regardless of the buffer size and flush interval 472 | 473 | flash_warn_ct!(Hello::World); 474 | flash_warn_ct!("Hello"); 475 | flash_warn_ct!("Hello"; "FlashLog"); 476 | flash_warn_ct!("Hello"; "FlashLog"; version = "0.1.0"); 477 | flash_warn_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 478 | flash_warn_ct!(version = "0.1.0"); 479 | flash_warn_ct!(version = "0.1.0", author = "John Doe"); 480 | flash_warn_ct!("topic1"; "message {} {}", 1, 2); 481 | flash_warn_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 482 | flash_warn_ct!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 483 | let test_info = TestStruct { test: 1 }; 484 | flash_warn_ct!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 485 | println!("{:?}", test_info); 486 | 487 | crate::flush!(); // this flushes regardless of the buffer size and flush interval 488 | 489 | flash_info_ct!(Hello::World); 490 | flash_info_ct!(Hello::FlashLog); 491 | flash_info_ct!("Hello"); 492 | flash_info_ct!("Hello"; "FlashLog"); 493 | flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0"); 494 | flash_info_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 495 | flash_info_ct!(version = "0.1.0"); 496 | flash_info_ct!(version = "0.1.0", author = "John Doe"); 497 | flash_info_ct!("topic1"; "message {} {}", 1, 2); 498 | flash_info_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 499 | flash_info_ct!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 500 | let test_info = TestStruct { test: 1 }; 501 | flash_info_ct!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 502 | println!("{:?}", test_info); 503 | 504 | crate::flush!(); // this flushes regardless of the buffer size and flush interval 505 | 506 | flash_debug_ct!(Hello::World); 507 | flash_debug_ct!("Hello"); 508 | flash_debug_ct!("Hello"; "FlashLog"); 509 | flash_debug_ct!("Hello"; "FlashLog"; version = "0.1.0"); 510 | flash_debug_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 511 | flash_debug_ct!(version = "0.1.0"); 512 | flash_debug_ct!(version = "0.1.0", author = "John Doe"); 513 | flash_debug_ct!("topic1"; "message {} {}", 1, 2); 514 | flash_debug_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 515 | flash_debug_ct!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 516 | let test_info = TestStruct { test: 1 }; 517 | flash_debug_ct!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 518 | println!("{:?}", test_info); 519 | 520 | crate::flush!(); // this flushes regardless of the buffer size and flush interval 521 | 522 | flash_trace_ct!(Hello::World); 523 | flash_trace_ct!("Hello"); 524 | flash_trace_ct!("Hello"; "FlashLog"); 525 | flash_trace_ct!("Hello"; "FlashLog"; version = "0.1.0"); 526 | flash_trace_ct!("Hello"; "FlashLog"; version = "0.1.0", author = "John Doe"); 527 | flash_trace_ct!(version = "0.1.0"); 528 | flash_trace_ct!(version = "0.1.0", author = "John Doe"); 529 | flash_trace_ct!("topic1"; "message {} {}", 1, 2); 530 | flash_trace_ct!("topic2"; "message {} {}", 1, 2; struct_info = 1, struct_info2 = 2); 531 | flash_trace_ct!("topic2"; "message {} {}", 1, 2; Hello = Hello::FlashLog); 532 | let test_info = TestStruct { test: 1 }; 533 | flash_trace_ct!("topic2"; "message {} {}", 1, 2; TestStruct = test_info); 534 | println!("{:?}", test_info); 535 | 536 | crate::flush!(); 537 | 538 | assert!(true); 539 | 540 | Ok(()) 541 | } 542 | } -------------------------------------------------------------------------------- /src/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::flash_trace; 2 | use crate::timer::get_unix_nano; 3 | use crate::rolling_file::{ 4 | RollingFileWriter, 5 | RollingConfig, 6 | RollingPeriod, 7 | }; 8 | // 9 | //use anyhow::{anyhow, Ok, Result}; 10 | use chrono; 11 | use core_affinity; 12 | use crossbeam_channel::{unbounded, Sender}; 13 | use once_cell::sync::Lazy; 14 | use std::path::PathBuf; 15 | use std::{ 16 | sync::{ 17 | atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering, AtomicU64}, 18 | Mutex, 19 | }, 20 | thread, 21 | }; 22 | 23 | pub static LOG_MESSAGE_BUFFER_SIZE: Lazy = Lazy::new(|| AtomicUsize::new(0)); 24 | //2_000_000; // string length 25 | pub static LOG_MESSAGE_FLUSH_INTERVAL: Lazy = Lazy::new(|| AtomicU64::new(0)); 26 | //2_000_000_000; // 2 second 27 | 28 | pub static INCLUDE_UNIXNANO: Lazy = Lazy::new(|| AtomicBool::new(false)); 29 | pub static MAX_LOG_LEVEL: Lazy = Lazy::new(|| AtomicUsize::new(LogLevel::NIL.as_usize())); 30 | pub static TIMEZONE: Lazy = Lazy::new(|| AtomicI32::new(TimeZone::Local as i32)); 31 | pub static CONSOLE_REPORT: Lazy = Lazy::new(|| AtomicBool::new(false)); 32 | pub static FILE_REPORT: Lazy = Lazy::new(|| AtomicBool::new(false)); 33 | pub static LOGGER_HANDLER: Lazy>>> =Lazy::new(|| Mutex::new(None)); 34 | pub static LOGGER_CORE: Lazy = Lazy::new(|| AtomicI32::new(-1)); // -1 means that setting affinity to any remaining core 35 | 36 | pub static LOG_SENDER: Lazy> = Lazy::new(|| { 37 | let (sender, receiver) = unbounded(); 38 | 39 | let mut message_queue: Vec = Vec::with_capacity(LOG_MESSAGE_BUFFER_SIZE.load(Ordering::SeqCst).max(10)); 40 | let mut last_flush_time = get_unix_nano(); 41 | 42 | let mut msg_buffer_size = LOG_MESSAGE_BUFFER_SIZE.load(Ordering::SeqCst); 43 | let mut msg_flush_interval = LOG_MESSAGE_FLUSH_INTERVAL.load(Ordering::SeqCst); 44 | let mut file_report = FILE_REPORT.load(Ordering::Relaxed); 45 | let mut console_report = CONSOLE_REPORT.load(Ordering::Relaxed); 46 | 47 | let affinity_core = LOGGER_CORE.load(Ordering::SeqCst); 48 | 49 | *LOGGER_HANDLER.lock().expect("Logger hander lock") = Some(thread::spawn(move || { 50 | let mut rolling_writer: Option = None; 51 | while let Ok(msg) = receiver.recv() { 52 | match msg { 53 | LogMessage::LazyMessage(lazy_message) => { 54 | let message = lazy_message.eval(); 55 | let current_timestamp = get_unix_nano(); 56 | message_queue.push(message); 57 | 58 | if msg_buffer_size == 0 || msg_flush_interval == 0 || (message_queue.len() >= msg_buffer_size) || (current_timestamp >= msg_flush_interval + last_flush_time) { 59 | let output = message_queue.join(""); 60 | 61 | if file_report { 62 | if let Some(ref mut writer) = rolling_writer { 63 | writer.write_all(output.as_bytes()).unwrap(); 64 | } 65 | } 66 | 67 | if console_report { println!("{}", output); } 68 | 69 | message_queue.clear(); 70 | 71 | last_flush_time = current_timestamp; 72 | } 73 | } 74 | LogMessage::FlushingMessage(lazy_message) => { 75 | let message = lazy_message.eval(); 76 | message_queue.push(message); 77 | 78 | let output = message_queue.join(""); 79 | if file_report { 80 | if let Some(ref mut writer) = rolling_writer { 81 | writer.write_all(output.as_bytes()).unwrap(); 82 | } 83 | } 84 | if console_report { 85 | println!("{}", output); 86 | } 87 | 88 | message_queue.clear(); 89 | last_flush_time = get_unix_nano(); 90 | } 91 | LogMessage::StaticString(message) => { 92 | let buffer_size = message_queue.len(); 93 | let timestamp = get_unix_nano(); 94 | message_queue.push(message.to_string()); 95 | 96 | if (buffer_size + message.len() >= msg_buffer_size) 97 | || (timestamp >= msg_flush_interval + last_flush_time) 98 | { 99 | let output = message_queue.join(""); 100 | if file_report { 101 | if let Some(ref mut writer) = rolling_writer { 102 | writer.write_all(output.as_bytes()).unwrap(); 103 | } 104 | } 105 | 106 | if console_report { 107 | println!("{}", output); 108 | } 109 | } 110 | } 111 | LogMessage::SetFile(config) => { 112 | if let Some(ref mut writer) = rolling_writer { 113 | writer.flush().expect("Failed to flush log file writer"); 114 | let _ = writer.sync_all(); 115 | } else { 116 | let writer = RollingFileWriter::new(config).expect("Failed to create RollingFileWriter"); 117 | rolling_writer = Some(writer); 118 | } 119 | } 120 | LogMessage::Flush => { 121 | let output = message_queue.join(""); 122 | if file_report { 123 | if let Some(ref mut writer) = rolling_writer { 124 | writer.write_all(output.as_bytes()).unwrap(); 125 | writer.flush().expect("Failed to flush log file writer"); 126 | let _ = writer.sync_all(); 127 | } 128 | } 129 | if console_report { 130 | println!("{}", output); 131 | } 132 | message_queue.clear(); 133 | last_flush_time = get_unix_nano(); 134 | } 135 | LogMessage::SetCore => { 136 | let available_core_ids = core_affinity::get_core_ids().expect("Failed to get available core IDs"); 137 | let core_id = if affinity_core == -1 { 138 | available_core_ids.first().cloned() 139 | } else { 140 | let core_id = core_affinity::CoreId { id: affinity_core as usize }; 141 | if available_core_ids.contains(&core_id) { 142 | Some(core_id) 143 | } else { 144 | available_core_ids.first().cloned() 145 | } 146 | }; 147 | 148 | if let Some(core_id) = core_id { 149 | core_affinity::set_for_current(core_id); 150 | } 151 | } 152 | LogMessage::Close => { 153 | let output = message_queue.join(""); 154 | if file_report { 155 | if let Some(ref mut writer) = rolling_writer { 156 | writer.write_all(output.as_bytes()).unwrap(); 157 | writer.flush().expect("Failed to flush log file writer in Close"); 158 | let _ = writer.sync_all(); 159 | } 160 | } 161 | if console_report { 162 | println!("{}", output); 163 | } 164 | break; 165 | } 166 | LogMessage::SetConfig => { 167 | msg_buffer_size = LOG_MESSAGE_BUFFER_SIZE.load(Ordering::Relaxed); 168 | msg_flush_interval = LOG_MESSAGE_FLUSH_INTERVAL.load(Ordering::Relaxed); 169 | file_report = FILE_REPORT.load(Ordering::Relaxed); 170 | console_report = CONSOLE_REPORT.load(Ordering::Relaxed); 171 | } 172 | } 173 | } 174 | })); 175 | sender 176 | }); 177 | 178 | pub enum TimeZone { 179 | Local, 180 | Seoul, 181 | Japan, 182 | NewYork, 183 | } 184 | 185 | impl TimeZone { 186 | #[inline] 187 | pub fn as_offset_hour(&self) -> i32 { 188 | match self { 189 | TimeZone::Local => { 190 | let local = chrono::Local::now(); 191 | let offset = local.offset().local_minus_utc() / 3600; 192 | offset 193 | } 194 | TimeZone::Seoul => 9, 195 | TimeZone::Japan => 9, 196 | TimeZone::NewYork => -4, 197 | } 198 | } 199 | } 200 | 201 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 202 | pub enum LogLevel { 203 | NIL = 0, 204 | Error = 1, 205 | Warn = 2, 206 | Info = 3, 207 | Debug = 4, 208 | Trace = 5, 209 | } 210 | 211 | impl LogLevel { 212 | #[inline] 213 | pub fn as_usize(&self) -> usize { 214 | match self { 215 | LogLevel::NIL => 0, 216 | LogLevel::Error => 1, 217 | LogLevel::Warn => 2, 218 | LogLevel::Info => 3, 219 | LogLevel::Debug => 4, 220 | LogLevel::Trace => 5, 221 | } 222 | } 223 | 224 | #[inline] 225 | pub fn from_usize(level: usize) -> Result { 226 | match level { 227 | 0 => Ok(LogLevel::NIL), 228 | 1 => Ok(LogLevel::Error), 229 | 2 => Ok(LogLevel::Warn), 230 | 3 => Ok(LogLevel::Info), 231 | 4 => Ok(LogLevel::Debug), 232 | 5 => Ok(LogLevel::Trace), 233 | _ => { 234 | Err("Invalid log level") 235 | } 236 | } 237 | } 238 | } 239 | 240 | impl std::fmt::Display for LogLevel { 241 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 242 | match self { 243 | LogLevel::NIL => write!(f, "Nil"), 244 | LogLevel::Trace => write!(f, "Trace"), 245 | LogLevel::Debug => write!(f, "Debug"), 246 | LogLevel::Info => write!(f, "Info"), 247 | LogLevel::Error => write!(f, "Error"), 248 | LogLevel::Warn => write!(f, "Warn"), 249 | } 250 | } 251 | } 252 | 253 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_trace! instead")] 254 | #[macro_export] 255 | macro_rules! trace { 256 | ($($arg:tt)*) => {{ 257 | $crate::log_fn_json!($crate::LogLevel::Trace, "not given", text = format!($($arg)*)); 258 | }}; 259 | } 260 | 261 | 262 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_debug! instead")] 263 | #[macro_export] 264 | macro_rules! debug { 265 | ($($arg:tt)*) => {{ 266 | $crate::log_fn_json!($crate::LogLevel::Debug, "not given", text = format!($($arg)*)); 267 | }}; 268 | } 269 | 270 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_info! instead")] 271 | #[macro_export] 272 | macro_rules! info { 273 | ($($arg:tt)*) => {{ 274 | $crate::log_fn_json!($crate::LogLevel::Info, "not given", text = format!($($arg)*)); 275 | }}; 276 | } 277 | 278 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_warn! instead")] 279 | #[macro_export] 280 | macro_rules! warn { 281 | ($($arg:tt)*) => {{ 282 | $crate::log_fn_json!($crate::LogLevel::Warn, "not given", text = format!($($arg)*)); 283 | }}; 284 | } 285 | 286 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_error! instead")] 287 | #[macro_export] 288 | macro_rules! error { 289 | ($($arg:tt)*) => {{ 290 | $crate::log_fn_json!($crate::LogLevel::Error, "not given", text = format!($($arg)*)); 291 | }}; 292 | } 293 | 294 | //--------- 295 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_trace! instead")] 296 | #[macro_export] 297 | macro_rules! log_trace { 298 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 299 | $crate::log_fn_json!($crate::LogLevel::Trace, $topic, $($key=$value),+); 300 | }}; 301 | 302 | ($topic:expr, $struct:expr) => {{ 303 | $crate::log_fn_json!($crate::LogLevel::Trace, $topic, $struct); 304 | }}; 305 | } 306 | 307 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_debug! instead")] 308 | #[macro_export] 309 | macro_rules! log_debug { 310 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 311 | $crate::log_fn_json!($crate::LogLevel::Debug, $topic, $($key=$value),+); 312 | }}; 313 | ($topic:expr, $struct:expr) => {{ 314 | $crate::log_fn_json!($crate::LogLevel::Debug, $topic, $struct); 315 | }}; 316 | } 317 | 318 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_info! instead")] 319 | #[macro_export] 320 | macro_rules! log_info { 321 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 322 | $crate::log_fn_json!($crate::LogLevel::Info, $topic, $($key=$value),+); 323 | }}; 324 | ($topic:expr, $struct:expr) => {{ 325 | $crate::log_fn_json!($crate::LogLevel::Info, $topic, $struct); 326 | }}; 327 | } 328 | 329 | 330 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_warn! instead")] 331 | #[macro_export] 332 | macro_rules! log_warn { 333 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 334 | $crate::log_fn_json!($crate::LogLevel::Warn, $topic, $($key=$value),+); 335 | }}; 336 | ($topic:expr, $struct:expr) => {{ 337 | $crate::log_fn_json!($crate::LogLevel::Warn, $topic, $struct); 338 | }}; 339 | } 340 | 341 | 342 | #[deprecated(since = "0.2.0", note = "Use flashlog::flash_error! instead")] 343 | #[macro_export] 344 | macro_rules! log_error { 345 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 346 | $crate::log_fn_json!($crate::LogLevel::Error, $topic, $($key=$value),+); 347 | }}; 348 | ($topic:expr, $struct:expr) => {{ 349 | $crate::log_fn_json!($crate::LogLevel::Error, $topic, $struct); 350 | }}; 351 | } 352 | 353 | #[macro_export] 354 | macro_rules! log_fn_json { 355 | ($level:expr, $topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 356 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 357 | let unixnano = $crate::get_unix_nano(); 358 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 359 | // 360 | $( 361 | #[allow(non_snake_case)] 362 | let $key = $value.clone(); 363 | )* 364 | let func = move || { 365 | let json_obj = $crate::serde_json::json!({ 366 | $( 367 | stringify!($key): $key, 368 | )+ 369 | }); 370 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 371 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 372 | let json_msg = match include_unixnano { 373 | false => $crate::serde_json::json!({ 374 | "date": date, 375 | "time": time, 376 | "offset": timezone, 377 | "level": $level.to_string(), 378 | "src": format!("{}:{}", file!(), line!()), 379 | "topic": $topic, 380 | "data": json_obj, 381 | }), 382 | true => $crate::serde_json::json!({ 383 | "date": date, 384 | "time": time, 385 | "offset": timezone, 386 | "level": $level.to_string(), 387 | "src": format!("{}:{}", file!(), line!()), 388 | "topic": $topic, 389 | "data": json_obj, 390 | "unixnano": unixnano, 391 | }), 392 | }; 393 | 394 | json_msg.to_string() + "\n" 395 | }; 396 | 397 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 398 | } 399 | }}; 400 | 401 | // In case of structs 402 | ($level:expr, $topic:expr, $struct:expr) => {{ 403 | if $level <= $crate::LogLevel::from_usize($crate::LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 404 | let unixnano = $crate::get_unix_nano(); 405 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 406 | #[allow(non_snake_case)] 407 | let struct_clone = $struct.clone(); 408 | let func = move || { 409 | let json_obj = $crate::serde_json::to_value(struct_clone).unwrap_or_else(|e| { 410 | $crate::serde_json::json!({ "error": format!("serialization error: {}", e) }) 411 | }); 412 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 413 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 414 | let json_msg = match include_unixnano { 415 | false => $crate::serde_json::json!({ 416 | "date": date, 417 | "time": time, 418 | "offset": timezone, 419 | "level": $level.to_string(), 420 | "src": format!("{}:{}", file!(), line!()), 421 | "topic": $topic, 422 | "data": json_obj, 423 | }), 424 | true => $crate::serde_json::json!({ 425 | "date": date, 426 | "time": time, 427 | "offset": timezone, 428 | "level": $level.to_string(), 429 | "src": format!("{}:{}", file!(), line!()), 430 | "topic": $topic, 431 | "data": json_obj, 432 | "unixnano": unixnano, 433 | }), 434 | }; 435 | 436 | json_msg.to_string() + "\n" 437 | }; 438 | 439 | $crate::LOG_SENDER.try_send($crate::LogMessage::LazyMessage($crate::LazyMessage::new(func))).unwrap(); 440 | } 441 | }}; 442 | } 443 | 444 | #[deprecated(since = "0.2.0", note = "Use flashlog::flush! instead")] 445 | #[macro_export] 446 | macro_rules! flushing_log_info { 447 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 448 | $crate::flushing_log_fn_json!($crate::LogLevel::Info, $topic, $($key=$value),+); 449 | }}; 450 | ($topic:expr, $struct:expr) => {{ 451 | $crate::flushing_log_fn_json!($crate::LogLevel::Info, $topic, $struct); 452 | }}; 453 | } 454 | 455 | #[deprecated(since = "0.2.0", note = "Use flashlog::flush! instead")] 456 | #[macro_export] 457 | macro_rules! flushing_log_debug { 458 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 459 | $crate::flushing_log_fn_json!($crate::LogLevel::Debug, $topic, $($key=$value),+); 460 | }}; 461 | ($topic:expr, $struct:expr) => {{ 462 | $crate::flushing_log_fn_json!($crate::LogLevel::Debug, $topic, $struct); 463 | }}; 464 | } 465 | 466 | #[deprecated(since = "0.2.0", note = "Use flashlog::flush! instead")] 467 | #[macro_export] 468 | macro_rules! flushing_log_error { 469 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 470 | $crate::flushing_log_fn_json!($crate::LogLevel::Error, $topic, $($key=$value),+); 471 | }}; 472 | ($topic:expr, $struct:expr) => {{ 473 | $crate::flushing_log_fn_json!($crate::LogLevel::Error, $topic, $struct); 474 | }}; 475 | } 476 | 477 | #[deprecated(since = "0.2.0", note = "Use flashlog::flush! instead")] 478 | #[macro_export] 479 | macro_rules! flushing_log_trace { 480 | ($topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 481 | $crate::flushing_log_fn_json!($crate::LogLevel::Trace, $topic, $($key=$value),+); 482 | }}; 483 | ($topic:expr, $struct:expr) => {{ 484 | $crate::flushing_log_fn_json!($crate::LogLevel::Trace, $topic, $struct); 485 | }}; 486 | } 487 | 488 | #[macro_export] 489 | macro_rules! flushing_log_fn_json { 490 | ($level:expr, $topic:expr, $($key:ident=$value:expr),+ $(,)?) => {{ 491 | if $level <= $crate::LogLevel::from_usize($crate::MAX_LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).expect("Invalid log level") { 492 | let unixnano = $crate::get_unix_nano(); 493 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 494 | let func = move || { 495 | let json_obj = $crate::serde_json::json!({ 496 | $( 497 | stringify!($key): $value, 498 | )+ 499 | }); 500 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 501 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(unixnano, timezone); 502 | let json_msg = match include_unixnano { 503 | true => $crate::serde_json::json!({ 504 | "date": date, 505 | "time": time, 506 | "offset": timezone, 507 | "level": $level.to_string(), 508 | "src": format!("{}:{}", file!(), line!()), 509 | "topic": $topic, 510 | "data": json_obj, 511 | "unixnano": unixnano, 512 | }), 513 | false => $crate::serde_json::json!({ 514 | "date": date, 515 | "time": time, 516 | "offset": timezone, 517 | "level": $level.to_string(), 518 | "src": format!("{}:{}", file!(), line!()), 519 | "topic": $topic, 520 | "data": json_obj, 521 | }), 522 | }; 523 | json_msg.to_string() + "\n" 524 | }; 525 | 526 | $crate::LOG_SENDER.try_send($crate::LogMessage::FlushingMessage($crate::LazyMessage::new(func))).unwrap(); 527 | } 528 | }}; 529 | 530 | // In case of structs 531 | ($level:expr, $topic:expr, $struct:expr) => {{ 532 | if $level <= $crate::LogLevel::from_usize($crate::LOG_LEVEL.load(std::sync::atomic::Ordering::Relaxed)).unwrap() { 533 | let unixnano = $crate::get_unix_nano(); 534 | let include_unixnano = $crate::logger::INCLUDE_UNIXNANO.load(std::sync::atomic::Ordering::Relaxed); 535 | let func = move || { 536 | let json_obj = $crate::serde_json::to_value($struct).unwrap_or_else(|e| { 537 | $crate::serde_json::json!({ "error": format!("serialization error: {}", e) }) 538 | }); 539 | let timezone = $crate::TIMEZONE.load(std::sync::atomic::Ordering::Relaxed); 540 | let (date, time) = $crate::convert_unix_nano_to_date_and_time(timestamp, timezone); 541 | match include_unixnano { 542 | true => { 543 | let json_msg = $crate::serde_json::json!({ 544 | "date": date, 545 | "time": time, 546 | "offset": timezone, 547 | "level": $level.to_string(), 548 | "src": format!("{}:{}", file!(), line!()), 549 | "topic": $topic, 550 | "data": json_obj, 551 | "unixnano": unixnano, 552 | }); 553 | json_msg.to_string() + "\n" 554 | } 555 | false => { 556 | let json_msg = $crate::serde_json::json!({ 557 | "date": date, 558 | "time": time, 559 | "offset": timezone, 560 | "level": $level.to_string(), 561 | "src": format!("{}:{}", file!(), line!()), 562 | "topic": $topic, 563 | "data": json_obj, 564 | }); 565 | json_msg.to_string() + "\n" 566 | } 567 | } 568 | }; 569 | $crate::LOG_SENDER.try_send($crate::LogMessage::FlushingMessage($crate::LazyMessage::new(func))).unwrap(); 570 | } 571 | }}; 572 | } 573 | 574 | pub struct LoggerGuard; 575 | 576 | impl Drop for LoggerGuard { 577 | fn drop(&mut self) { 578 | flash_trace!("LoggerGuard"; "LoggerGuard is dropped"); 579 | Logger::finalize(); 580 | } 581 | } 582 | 583 | pub struct Logger { 584 | file_config: Option, 585 | } 586 | 587 | 588 | #[derive(Debug)] 589 | pub enum LoggerError { 590 | UnsetFile, 591 | } 592 | 593 | impl std::fmt::Display for LoggerError { 594 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 595 | match self { 596 | LoggerError::UnsetFile => write!(f, "File config is not set. Use with_file first"), 597 | } 598 | } 599 | } 600 | 601 | impl std::error::Error for LoggerError {} 602 | 603 | impl Logger { 604 | pub fn finalize() { 605 | let _ = LOG_SENDER.try_send(LogMessage::Close); 606 | if let Some(handler) = LOGGER_HANDLER.lock().expect("Failed to lock LOGGER_HANDLER").take() { 607 | let _ = handler.join(); 608 | } 609 | } 610 | 611 | pub fn initialize() -> Logger { 612 | let _ = get_unix_nano(); 613 | LOG_MESSAGE_BUFFER_SIZE.store(1_000_000, Ordering::Relaxed); 614 | LOG_MESSAGE_FLUSH_INTERVAL.store(1_000_000, Ordering::Relaxed); 615 | Logger { file_config: None } 616 | } 617 | 618 | pub fn with_file(mut self, file_path: &str, file_name: &str) -> Result { 619 | std::fs::create_dir_all(file_path)?; 620 | 621 | let config = RollingConfig { 622 | base_path: PathBuf::from(file_path), 623 | file_name_prefix: file_name.to_string(), 624 | roll_period: Some(RollingPeriod::Daily), 625 | max_roll_files: Some(10), 626 | compress: false, 627 | }; 628 | 629 | self.file_config = Some(config); 630 | FILE_REPORT.store(true, Ordering::SeqCst); 631 | 632 | Ok(self) 633 | } 634 | 635 | pub fn with_compress(mut self, compress: bool) -> Result { 636 | if let Some(ref mut config) = self.file_config { 637 | config.compress = compress; 638 | Ok(self) 639 | } else { 640 | Err(LoggerError::UnsetFile) 641 | } 642 | } 643 | 644 | pub fn with_logger_core(self, core: i32) -> Logger { 645 | LOGGER_CORE.store(core, Ordering::SeqCst); 646 | self 647 | } 648 | 649 | pub fn with_roll_period(mut self, period: RollingPeriod) -> Result { 650 | if let Some(ref mut config) = self.file_config { 651 | config.roll_period = Some(period); 652 | Ok(self) 653 | } else { 654 | Err(LoggerError::UnsetFile) 655 | } 656 | } 657 | 658 | pub fn include_unixnano(self, include: bool) -> Logger { 659 | INCLUDE_UNIXNANO.store(include, Ordering::Relaxed); 660 | self 661 | } 662 | 663 | pub fn with_max_roll_files(mut self, max_roll_files: usize) -> Result { 664 | if let Some(ref mut config) = self.file_config { 665 | config.max_roll_files = Some(max_roll_files); 666 | Ok(self) 667 | } else { 668 | Err(LoggerError::UnsetFile) 669 | } 670 | } 671 | 672 | pub fn with_console_report(self, console_report: bool) -> Logger { 673 | CONSOLE_REPORT.store(console_report, Ordering::Relaxed); 674 | self 675 | } 676 | 677 | pub fn with_msg_buffer_size(self, size: usize) -> Logger { 678 | LOG_MESSAGE_BUFFER_SIZE.store(size, Ordering::Relaxed); 679 | self 680 | } 681 | 682 | pub fn with_msg_flush_interval(self, interval: u64) -> Logger { 683 | LOG_MESSAGE_FLUSH_INTERVAL.store(interval, Ordering::Relaxed); 684 | self 685 | } 686 | 687 | #[deprecated(since = "0.3.0", note = "it is recommended to use compile time filter options and use flash_xxxx_ct! instead")] 688 | pub fn with_max_log_level(self, level: LogLevel) -> Logger { 689 | MAX_LOG_LEVEL.store(level.as_usize(), Ordering::Relaxed); 690 | self 691 | } 692 | 693 | pub fn with_timezone(self, timezone: TimeZone) -> Logger { 694 | TIMEZONE.store(timezone.as_offset_hour(), Ordering::Relaxed); 695 | self 696 | } 697 | 698 | pub fn launch(self) -> LoggerGuard { 699 | let rolling_config = self.file_config.clone(); 700 | let _ = LOG_SENDER.send(LogMessage::SetCore); 701 | let _ = LOG_SENDER.send(LogMessage::SetConfig); 702 | if let Some(config) = rolling_config { 703 | let _ = LOG_SENDER.send(LogMessage::SetFile(config)); 704 | } 705 | LoggerGuard {} 706 | } 707 | } 708 | 709 | pub enum LogMessage { 710 | LazyMessage(LazyMessage), 711 | FlushingMessage(LazyMessage), 712 | StaticString(&'static str), 713 | SetFile(RollingConfig), 714 | Flush, 715 | SetCore, 716 | SetConfig, 717 | Close, 718 | } 719 | 720 | pub struct LazyMessage { 721 | data: Box String) + Send + 'static>, 722 | } 723 | 724 | impl LazyMessage { 725 | pub fn new(data: F) -> LazyMessage 726 | where 727 | F: (FnOnce() -> String) + Send + 'static, 728 | { 729 | LazyMessage { 730 | data: Box::new(data), 731 | } 732 | } 733 | 734 | pub fn eval(self) -> String { 735 | (self.data)() 736 | } 737 | } -------------------------------------------------------------------------------- /examples/fast_log/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.8.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 16 | dependencies = [ 17 | "cfg-if", 18 | "once_cell", 19 | "version_check", 20 | "zerocopy", 21 | ] 22 | 23 | [[package]] 24 | name = "aho-corasick" 25 | version = "1.1.3" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 28 | dependencies = [ 29 | "memchr", 30 | ] 31 | 32 | [[package]] 33 | name = "allocator-api2" 34 | version = "0.2.18" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 37 | 38 | [[package]] 39 | name = "android-tzdata" 40 | version = "0.1.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 43 | 44 | [[package]] 45 | name = "android_system_properties" 46 | version = "0.1.5" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 49 | dependencies = [ 50 | "libc", 51 | ] 52 | 53 | [[package]] 54 | name = "anyhow" 55 | version = "1.0.86" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 58 | 59 | [[package]] 60 | name = "arc-swap" 61 | version = "1.7.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" 64 | 65 | [[package]] 66 | name = "autocfg" 67 | version = "1.3.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 70 | 71 | [[package]] 72 | name = "bitflags" 73 | version = "2.6.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 76 | 77 | [[package]] 78 | name = "bumpalo" 79 | version = "3.16.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 82 | 83 | [[package]] 84 | name = "cc" 85 | version = "1.1.15" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" 88 | dependencies = [ 89 | "shlex", 90 | ] 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "1.0.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 97 | 98 | [[package]] 99 | name = "chrono" 100 | version = "0.4.38" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 103 | dependencies = [ 104 | "android-tzdata", 105 | "iana-time-zone", 106 | "js-sys", 107 | "num-traits", 108 | "serde", 109 | "wasm-bindgen", 110 | "windows-targets", 111 | ] 112 | 113 | [[package]] 114 | name = "chrono-tz" 115 | version = "0.10.1" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f" 118 | dependencies = [ 119 | "chrono", 120 | "chrono-tz-build", 121 | "phf", 122 | ] 123 | 124 | [[package]] 125 | name = "chrono-tz-build" 126 | version = "0.4.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" 129 | dependencies = [ 130 | "parse-zoneinfo", 131 | "phf_codegen", 132 | ] 133 | 134 | [[package]] 135 | name = "const_fn" 136 | version = "0.4.10" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" 139 | 140 | [[package]] 141 | name = "core-foundation-sys" 142 | version = "0.8.7" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 145 | 146 | [[package]] 147 | name = "core_affinity" 148 | version = "0.8.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "622892f5635ce1fc38c8f16dfc938553ed64af482edb5e150bf4caedbfcb2304" 151 | dependencies = [ 152 | "libc", 153 | "num_cpus", 154 | "winapi", 155 | ] 156 | 157 | [[package]] 158 | name = "crc32fast" 159 | version = "1.4.2" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 162 | dependencies = [ 163 | "cfg-if", 164 | ] 165 | 166 | [[package]] 167 | name = "crossbeam" 168 | version = "0.8.4" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" 171 | dependencies = [ 172 | "crossbeam-channel", 173 | "crossbeam-deque", 174 | "crossbeam-epoch", 175 | "crossbeam-queue", 176 | "crossbeam-utils", 177 | ] 178 | 179 | [[package]] 180 | name = "crossbeam-channel" 181 | version = "0.5.13" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 184 | dependencies = [ 185 | "crossbeam-utils", 186 | ] 187 | 188 | [[package]] 189 | name = "crossbeam-deque" 190 | version = "0.8.5" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 193 | dependencies = [ 194 | "crossbeam-epoch", 195 | "crossbeam-utils", 196 | ] 197 | 198 | [[package]] 199 | name = "crossbeam-epoch" 200 | version = "0.9.18" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 203 | dependencies = [ 204 | "crossbeam-utils", 205 | ] 206 | 207 | [[package]] 208 | name = "crossbeam-queue" 209 | version = "0.3.11" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" 212 | dependencies = [ 213 | "crossbeam-utils", 214 | ] 215 | 216 | [[package]] 217 | name = "crossbeam-utils" 218 | version = "0.8.20" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 221 | 222 | [[package]] 223 | name = "dark-std" 224 | version = "0.2.16" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "f32345b50634ef57042cbad8d67666efca2809c93f761823b274d6d69de62e8d" 227 | dependencies = [ 228 | "flume", 229 | "indexmap", 230 | "parking_lot", 231 | "serde", 232 | ] 233 | 234 | [[package]] 235 | name = "deranged" 236 | version = "0.3.11" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 239 | dependencies = [ 240 | "powerfmt", 241 | "serde", 242 | ] 243 | 244 | [[package]] 245 | name = "equivalent" 246 | version = "1.0.1" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 249 | 250 | [[package]] 251 | name = "example-fast-log" 252 | version = "0.1.0" 253 | dependencies = [ 254 | "anyhow", 255 | "fast_log", 256 | "flashlog", 257 | "ftlog", 258 | "log", 259 | "serde", 260 | "time", 261 | ] 262 | 263 | [[package]] 264 | name = "fast_log" 265 | version = "1.7.3" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "5fb4ef9b3db48f702dfa55a4d28e7c40280a81f4a2ace3e2d82577356fcaedba" 268 | dependencies = [ 269 | "crossbeam", 270 | "crossbeam-channel", 271 | "crossbeam-utils", 272 | "dark-std", 273 | "fastdate", 274 | "log", 275 | "once_cell", 276 | "parking_lot", 277 | ] 278 | 279 | [[package]] 280 | name = "fastdate" 281 | version = "0.3.32" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "65f69cd2c5a40eba2f7d1746526b42226d257e1afa0d7e25322402d6e29521f9" 284 | dependencies = [ 285 | "libc", 286 | "serde", 287 | "time", 288 | "windows-sys", 289 | ] 290 | 291 | [[package]] 292 | name = "fastrand" 293 | version = "2.1.1" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 296 | 297 | [[package]] 298 | name = "flashlog" 299 | version = "0.3.0" 300 | dependencies = [ 301 | "chrono", 302 | "chrono-tz", 303 | "core_affinity", 304 | "crossbeam-channel", 305 | "crossbeam-utils", 306 | "flate2", 307 | "lazy_static", 308 | "once_cell", 309 | "quanta", 310 | "serde", 311 | "serde_derive", 312 | "serde_json", 313 | "time", 314 | ] 315 | 316 | [[package]] 317 | name = "flate2" 318 | version = "1.1.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" 321 | dependencies = [ 322 | "crc32fast", 323 | "miniz_oxide", 324 | ] 325 | 326 | [[package]] 327 | name = "flume" 328 | version = "0.11.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" 331 | dependencies = [ 332 | "futures-core", 333 | "futures-sink", 334 | "spin", 335 | ] 336 | 337 | [[package]] 338 | name = "ftlog" 339 | version = "0.2.14" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "c820f28f9ca2a2951ced0fe283b9c31a9d8c8780345e30c152e193f7f128f523" 342 | dependencies = [ 343 | "arc-swap", 344 | "crossbeam-channel", 345 | "fastrand", 346 | "hashbrown", 347 | "log", 348 | "nohash-hasher", 349 | "time", 350 | "typed-builder", 351 | "tz-rs", 352 | ] 353 | 354 | [[package]] 355 | name = "futures-core" 356 | version = "0.3.30" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 359 | 360 | [[package]] 361 | name = "futures-sink" 362 | version = "0.3.30" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 365 | 366 | [[package]] 367 | name = "hashbrown" 368 | version = "0.14.5" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 371 | dependencies = [ 372 | "ahash", 373 | "allocator-api2", 374 | ] 375 | 376 | [[package]] 377 | name = "hermit-abi" 378 | version = "0.3.9" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 381 | 382 | [[package]] 383 | name = "iana-time-zone" 384 | version = "0.1.60" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 387 | dependencies = [ 388 | "android_system_properties", 389 | "core-foundation-sys", 390 | "iana-time-zone-haiku", 391 | "js-sys", 392 | "wasm-bindgen", 393 | "windows-core", 394 | ] 395 | 396 | [[package]] 397 | name = "iana-time-zone-haiku" 398 | version = "0.1.2" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 401 | dependencies = [ 402 | "cc", 403 | ] 404 | 405 | [[package]] 406 | name = "indexmap" 407 | version = "2.4.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" 410 | dependencies = [ 411 | "equivalent", 412 | "hashbrown", 413 | "serde", 414 | ] 415 | 416 | [[package]] 417 | name = "itoa" 418 | version = "1.0.11" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 421 | 422 | [[package]] 423 | name = "js-sys" 424 | version = "0.3.70" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 427 | dependencies = [ 428 | "wasm-bindgen", 429 | ] 430 | 431 | [[package]] 432 | name = "lazy_static" 433 | version = "1.5.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 436 | 437 | [[package]] 438 | name = "libc" 439 | version = "0.2.158" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 442 | 443 | [[package]] 444 | name = "lock_api" 445 | version = "0.4.12" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 448 | dependencies = [ 449 | "autocfg", 450 | "scopeguard", 451 | ] 452 | 453 | [[package]] 454 | name = "log" 455 | version = "0.4.22" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 458 | dependencies = [ 459 | "value-bag", 460 | ] 461 | 462 | [[package]] 463 | name = "memchr" 464 | version = "2.7.4" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 467 | 468 | [[package]] 469 | name = "miniz_oxide" 470 | version = "0.8.5" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 473 | dependencies = [ 474 | "adler2", 475 | ] 476 | 477 | [[package]] 478 | name = "nohash-hasher" 479 | version = "0.2.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" 482 | 483 | [[package]] 484 | name = "num-conv" 485 | version = "0.1.0" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 488 | 489 | [[package]] 490 | name = "num-traits" 491 | version = "0.2.19" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 494 | dependencies = [ 495 | "autocfg", 496 | ] 497 | 498 | [[package]] 499 | name = "num_cpus" 500 | version = "1.16.0" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 503 | dependencies = [ 504 | "hermit-abi", 505 | "libc", 506 | ] 507 | 508 | [[package]] 509 | name = "num_threads" 510 | version = "0.1.7" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 513 | dependencies = [ 514 | "libc", 515 | ] 516 | 517 | [[package]] 518 | name = "once_cell" 519 | version = "1.19.0" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 522 | 523 | [[package]] 524 | name = "parking_lot" 525 | version = "0.12.3" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 528 | dependencies = [ 529 | "lock_api", 530 | "parking_lot_core", 531 | ] 532 | 533 | [[package]] 534 | name = "parking_lot_core" 535 | version = "0.9.10" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 538 | dependencies = [ 539 | "cfg-if", 540 | "libc", 541 | "redox_syscall", 542 | "smallvec", 543 | "windows-targets", 544 | ] 545 | 546 | [[package]] 547 | name = "parse-zoneinfo" 548 | version = "0.3.1" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 551 | dependencies = [ 552 | "regex", 553 | ] 554 | 555 | [[package]] 556 | name = "phf" 557 | version = "0.11.2" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" 560 | dependencies = [ 561 | "phf_shared", 562 | ] 563 | 564 | [[package]] 565 | name = "phf_codegen" 566 | version = "0.11.2" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" 569 | dependencies = [ 570 | "phf_generator", 571 | "phf_shared", 572 | ] 573 | 574 | [[package]] 575 | name = "phf_generator" 576 | version = "0.11.2" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" 579 | dependencies = [ 580 | "phf_shared", 581 | "rand", 582 | ] 583 | 584 | [[package]] 585 | name = "phf_shared" 586 | version = "0.11.2" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" 589 | dependencies = [ 590 | "siphasher", 591 | ] 592 | 593 | [[package]] 594 | name = "powerfmt" 595 | version = "0.2.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 598 | 599 | [[package]] 600 | name = "proc-macro2" 601 | version = "1.0.86" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 604 | dependencies = [ 605 | "unicode-ident", 606 | ] 607 | 608 | [[package]] 609 | name = "quanta" 610 | version = "0.12.3" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" 613 | dependencies = [ 614 | "crossbeam-utils", 615 | "libc", 616 | "once_cell", 617 | "raw-cpuid", 618 | "wasi", 619 | "web-sys", 620 | "winapi", 621 | ] 622 | 623 | [[package]] 624 | name = "quote" 625 | version = "1.0.37" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 628 | dependencies = [ 629 | "proc-macro2", 630 | ] 631 | 632 | [[package]] 633 | name = "rand" 634 | version = "0.8.5" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 637 | dependencies = [ 638 | "rand_core", 639 | ] 640 | 641 | [[package]] 642 | name = "rand_core" 643 | version = "0.6.4" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 646 | 647 | [[package]] 648 | name = "raw-cpuid" 649 | version = "11.1.0" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" 652 | dependencies = [ 653 | "bitflags", 654 | ] 655 | 656 | [[package]] 657 | name = "redox_syscall" 658 | version = "0.5.3" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" 661 | dependencies = [ 662 | "bitflags", 663 | ] 664 | 665 | [[package]] 666 | name = "regex" 667 | version = "1.10.6" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" 670 | dependencies = [ 671 | "aho-corasick", 672 | "memchr", 673 | "regex-automata", 674 | "regex-syntax", 675 | ] 676 | 677 | [[package]] 678 | name = "regex-automata" 679 | version = "0.4.7" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 682 | dependencies = [ 683 | "aho-corasick", 684 | "memchr", 685 | "regex-syntax", 686 | ] 687 | 688 | [[package]] 689 | name = "regex-syntax" 690 | version = "0.8.4" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 693 | 694 | [[package]] 695 | name = "ryu" 696 | version = "1.0.18" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 699 | 700 | [[package]] 701 | name = "scopeguard" 702 | version = "1.2.0" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 705 | 706 | [[package]] 707 | name = "serde" 708 | version = "1.0.209" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" 711 | dependencies = [ 712 | "serde_derive", 713 | ] 714 | 715 | [[package]] 716 | name = "serde_derive" 717 | version = "1.0.209" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" 720 | dependencies = [ 721 | "proc-macro2", 722 | "quote", 723 | "syn", 724 | ] 725 | 726 | [[package]] 727 | name = "serde_json" 728 | version = "1.0.127" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" 731 | dependencies = [ 732 | "itoa", 733 | "memchr", 734 | "ryu", 735 | "serde", 736 | ] 737 | 738 | [[package]] 739 | name = "shlex" 740 | version = "1.3.0" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 743 | 744 | [[package]] 745 | name = "siphasher" 746 | version = "0.3.11" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 749 | 750 | [[package]] 751 | name = "smallvec" 752 | version = "1.13.2" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 755 | 756 | [[package]] 757 | name = "spin" 758 | version = "0.9.8" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 761 | dependencies = [ 762 | "lock_api", 763 | ] 764 | 765 | [[package]] 766 | name = "syn" 767 | version = "2.0.76" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" 770 | dependencies = [ 771 | "proc-macro2", 772 | "quote", 773 | "unicode-ident", 774 | ] 775 | 776 | [[package]] 777 | name = "time" 778 | version = "0.3.36" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 781 | dependencies = [ 782 | "deranged", 783 | "itoa", 784 | "libc", 785 | "num-conv", 786 | "num_threads", 787 | "powerfmt", 788 | "serde", 789 | "time-core", 790 | "time-macros", 791 | ] 792 | 793 | [[package]] 794 | name = "time-core" 795 | version = "0.1.2" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 798 | 799 | [[package]] 800 | name = "time-macros" 801 | version = "0.2.18" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 804 | dependencies = [ 805 | "num-conv", 806 | "time-core", 807 | ] 808 | 809 | [[package]] 810 | name = "typed-builder" 811 | version = "0.16.2" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "34085c17941e36627a879208083e25d357243812c30e7d7387c3b954f30ade16" 814 | dependencies = [ 815 | "typed-builder-macro", 816 | ] 817 | 818 | [[package]] 819 | name = "typed-builder-macro" 820 | version = "0.16.2" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "f03ca4cb38206e2bef0700092660bb74d696f808514dae47fa1467cbfe26e96e" 823 | dependencies = [ 824 | "proc-macro2", 825 | "quote", 826 | "syn", 827 | ] 828 | 829 | [[package]] 830 | name = "tz-rs" 831 | version = "0.6.14" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" 834 | dependencies = [ 835 | "const_fn", 836 | ] 837 | 838 | [[package]] 839 | name = "unicode-ident" 840 | version = "1.0.12" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 843 | 844 | [[package]] 845 | name = "value-bag" 846 | version = "1.9.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" 849 | 850 | [[package]] 851 | name = "version_check" 852 | version = "0.9.5" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 855 | 856 | [[package]] 857 | name = "wasi" 858 | version = "0.11.0+wasi-snapshot-preview1" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 861 | 862 | [[package]] 863 | name = "wasm-bindgen" 864 | version = "0.2.93" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 867 | dependencies = [ 868 | "cfg-if", 869 | "once_cell", 870 | "wasm-bindgen-macro", 871 | ] 872 | 873 | [[package]] 874 | name = "wasm-bindgen-backend" 875 | version = "0.2.93" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 878 | dependencies = [ 879 | "bumpalo", 880 | "log", 881 | "once_cell", 882 | "proc-macro2", 883 | "quote", 884 | "syn", 885 | "wasm-bindgen-shared", 886 | ] 887 | 888 | [[package]] 889 | name = "wasm-bindgen-macro" 890 | version = "0.2.93" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 893 | dependencies = [ 894 | "quote", 895 | "wasm-bindgen-macro-support", 896 | ] 897 | 898 | [[package]] 899 | name = "wasm-bindgen-macro-support" 900 | version = "0.2.93" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 903 | dependencies = [ 904 | "proc-macro2", 905 | "quote", 906 | "syn", 907 | "wasm-bindgen-backend", 908 | "wasm-bindgen-shared", 909 | ] 910 | 911 | [[package]] 912 | name = "wasm-bindgen-shared" 913 | version = "0.2.93" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 916 | 917 | [[package]] 918 | name = "web-sys" 919 | version = "0.3.70" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 922 | dependencies = [ 923 | "js-sys", 924 | "wasm-bindgen", 925 | ] 926 | 927 | [[package]] 928 | name = "winapi" 929 | version = "0.3.9" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 932 | dependencies = [ 933 | "winapi-i686-pc-windows-gnu", 934 | "winapi-x86_64-pc-windows-gnu", 935 | ] 936 | 937 | [[package]] 938 | name = "winapi-i686-pc-windows-gnu" 939 | version = "0.4.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 942 | 943 | [[package]] 944 | name = "winapi-x86_64-pc-windows-gnu" 945 | version = "0.4.0" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 948 | 949 | [[package]] 950 | name = "windows-core" 951 | version = "0.52.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 954 | dependencies = [ 955 | "windows-targets", 956 | ] 957 | 958 | [[package]] 959 | name = "windows-sys" 960 | version = "0.52.0" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 963 | dependencies = [ 964 | "windows-targets", 965 | ] 966 | 967 | [[package]] 968 | name = "windows-targets" 969 | version = "0.52.6" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 972 | dependencies = [ 973 | "windows_aarch64_gnullvm", 974 | "windows_aarch64_msvc", 975 | "windows_i686_gnu", 976 | "windows_i686_gnullvm", 977 | "windows_i686_msvc", 978 | "windows_x86_64_gnu", 979 | "windows_x86_64_gnullvm", 980 | "windows_x86_64_msvc", 981 | ] 982 | 983 | [[package]] 984 | name = "windows_aarch64_gnullvm" 985 | version = "0.52.6" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 988 | 989 | [[package]] 990 | name = "windows_aarch64_msvc" 991 | version = "0.52.6" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 994 | 995 | [[package]] 996 | name = "windows_i686_gnu" 997 | version = "0.52.6" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1000 | 1001 | [[package]] 1002 | name = "windows_i686_gnullvm" 1003 | version = "0.52.6" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1006 | 1007 | [[package]] 1008 | name = "windows_i686_msvc" 1009 | version = "0.52.6" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1012 | 1013 | [[package]] 1014 | name = "windows_x86_64_gnu" 1015 | version = "0.52.6" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1018 | 1019 | [[package]] 1020 | name = "windows_x86_64_gnullvm" 1021 | version = "0.52.6" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1024 | 1025 | [[package]] 1026 | name = "windows_x86_64_msvc" 1027 | version = "0.52.6" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1030 | 1031 | [[package]] 1032 | name = "zerocopy" 1033 | version = "0.7.35" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1036 | dependencies = [ 1037 | "zerocopy-derive", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "zerocopy-derive" 1042 | version = "0.7.35" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1045 | dependencies = [ 1046 | "proc-macro2", 1047 | "quote", 1048 | "syn", 1049 | ] 1050 | --------------------------------------------------------------------------------