Crates.io | trivial_log |
lib.rs | trivial_log |
version | 0.1.0 |
source | src |
created_at | 2025-02-18 06:11:23.314895+00 |
updated_at | 2025-02-18 06:11:23.314895+00 |
description | No-bloat leak free implementation for log |
homepage | |
repository | https://github.com/tiipotto/trivial_log |
max_upload_size | |
id | 1559624 |
size | 58,750 |
This is intended to be a no-bloat implementation for log. It includes simple defaults while still providing good flexibility for more advanced use cases.
The original motivation was logging while running valgrind tests that treat "possibly leaked" as errors.
Unlike many other implementations, this crate intends to have no possible memory leaks (even 'static).
All examples should have All heap blocks were freed -- no leaks are possible
.
One line to get started with sane defaults for std logs (warn and below to stdout, errors to stderr).
fn main() {
trivial_log::init_std(LevelFilter::Trace).unwrap();
error!("An error has occurred, please help!");
}
This will cause messages like these to appear:
[E] - 17 Feb 2025 22:27:20.956 UTC - ThreadId(1) - An error has occurred, please help!
A more advanced configuration that includes both stdout and logging to a file
fn main() {
trivial_log::builder()
.default_format(|builder| {
builder
//
.appender_range(Level::Info, Level::Error, Path::new("mylog.log"))
.appender_range(Level::Trace, Level::Error, |msg: &String| print!("{}", msg))
})
.init()
.unwrap();
error!("An error has occurred, please help!");
}
An "advanced" configuration that shows how to log normal human-readable messages to stdout but shows how to prepare a json object for "processing" (eg to a log server).
/// This serves as an example of how your json entity may look like.
/// You may design this struct in any ways you see fit.
#[derive(Serialize, Deserialize)]
struct LogEntity {
message: String,
level: String,
thread: String,
timestamp: u128,
}
fn main() {
trivial_log::builder()
.default_format(|builder| builder.appender_range(Level::Trace, Level::Error, |msg: &String| print!("{}", msg)))
.format(|now: std::time::SystemTime, record: &log::Record| {
Some(LogEntity {
message: record.args().to_string(),
level: record.level().to_string(),
thread: format!("{:?}", std::thread::current().id()),
timestamp: now.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis(),
})
}, |builder| builder.appender_range(Level::Trace, Level::Error, |msg: &LogEntity| print!("{}", serde_json::to_string(msg).unwrap())))
.init()
.unwrap();
}
An "advanced" configuration, showing how to implement a custom Appender Implementation, by logging into a SQLite database.
On ANSI terminals you can write colors. This examples uses the ansi_term crate, but you can also create the ansi escape codes manually without any dependencies.
fn main() {
use ansi_term::Color;
trivial_log::builder()
.format(
|_time, rec| {
let prefix = match rec.level() {
Level::Error => Color::Red.paint("E"),
Level::Warn => Color::Yellow.paint("W"),
Level::Info => Color::Green.paint("I"),
Level::Debug => Color::Purple.paint("D"),
Level::Trace => Color::White.paint("T"),
};
Some(format!(
"{}{}{} {}\n",
Color::Blue.paint("["),
prefix,
Color::Blue.paint("]"),
rec.args()
))
},
|builder| builder.appender_filter(LevelFilter::Trace, |msg: &String| print!("{}", msg)),
)
.init()
.ok();
}
The logging is split into 2 parts.
There is no limit for how many format functions or appenders you can use.
Each log always processes all formats and appenders when the log level matches. When a format has multiple appenders, the format fn only gets called once.
std::io::BufWriter<T> where T: Write + Send
- io errors are ignoredstd::path::Path
- inability to open or create the file will panic! Other io errors are ignored.std::fs::File
- io errors are ignoredstd::sync::mpsc::Sender<T> where T: Send+Clone
- if the receiver dies then this appender becomes a noop.std::sync::mpsc::SyncSender<T> where T: Send+Clone
- if the receiver dies then this appender becomes a noop.
The appender only uses the send method to send datalog!
from inside the appender that can lead to a stack overflow.log!
function.
Either use panic=abort, or prevent/catch panics in the appender impl as the caller of log!
is unlikely to expect it to panic.trivial_log::free()
before the process exits. It is memory safe (no UB) to not call this.