I'm learning Rust by implementing a structured logging API similar to the one found in Serilog. In systems programming, this style of logging is most often found in Windows' ETW. Web and distributed applications use similar techniques to improve machine-readabililty when dealing with large event volumes.
The implementation here is currently a proof-of-concept.
"Emitted" log events consist of a format and list of named properties, as in the emit!()
call below.
```rust
extern crate emit;
extern crate log; extern crate env_logger;
use std::env; use emit::pipeline; use emit::collectors::seq;
fn main() { env_logger::init().unwrap(); let _flush = pipeline::init(seq::SeqCollector::local());
emit!("Hello, {}!", name: env::var("USERNAME").unwrap());
} ```
These end up in JSON payloads like:
json
{
"Timestamp": "2016-03-17T00:17:01Z",
"MessageTemplate": "Hello, {name}!",
"Properties": {
"name": "nblumhardt"
}
}
Which can be rendered out to text or searched/sorted/filtered based on the event properties:
I'm using Seq and its JSON format while I design the crate, but like Serilog this should eventually be pluggable to other log collectors and formats.
What about the log
crate?
The log!()
macros are obviously the best way to capture diagnostic events in Rust as it stands. However, log
destructively renders events into text:
rust
info!("Hello, {}!", env::var("USERNAME").unwrap());
There's no way for a log processing system to later pull the username value from this message, except through handwritten parsers/regular expressions.
The idea of emit
is that rendering can happen at any point - but the original values are preserved for easy machine processing as well.
To keep these two worlds in harmony, emit
pushes all events through the log
crate as well.