Rust has flexible type system and metaprogramming capabilities, allowing to build both efficient and highly reusable log system. The idea is very similar to serde and is introduced in a widely used log, slog and tracing crates.
log crate represents a single unified frontend interface (facade) which is used by all libraries at the same time, but is backed by one actual backend implementation on your choice. This allows to control all the logs (of application and its dependencies) from a single place and in a unified manner: opt-in and opt-out logs of libraries, separate logs by destinations, etc.
- Libraries should link only to the
logcrate, and use the provided macros to log whatever information will be useful to downstream consumers.- Executables should choose a logger implementation and initialize it early in the runtime of the program. Logging implementations will typically include a function to do this.
One interesting part is that log levels can be disabled at compile time, thus have no runtime performance impact at all, unless you're debugging.
For better understanding and familiarity with log's design, concepts, usage, and features, read through the following articles:
For structured logging there is the excellent slog crate in Rust ecosystem.
The ambition is to be The Logging Library for Rust.
slogshould accommodate a variety of logging features and requirements. If there is a feature that you need and standardlogcrate is missing,slogshould have it.
It's backward and forward compatible with log crate, extending its ideas and is baked with an excellent performance.
For better understanding and familiarity with slog's design, concepts, usage, and features, read through the following articles:
The famous tracing crate is fabulous at both tracing and structured logging.
tracingexpands upon logging-style diagnostics by allowing libraries and applications to record structured events with additional information about temporality and causality — unlike a log message, a span intracinghas a beginning and end time, may be entered and exited by the flow of execution, and may exist within a nested tree of similar spans. In addition,tracingspans are structured, with the ability to record typed data as well as textual messages.
Its "killer feature", undoubtedly, is spans functionality, so people tend to prefer it over slog even for usual logging. It's also backward and forward compatible with log crate.
Speaking of tracing, the tracing crate has good integrations with OpenTelemetry-compatible distributed tracing systems (and similar ones). All this allows to reuse the same solution both for logging, tracing (like Jaeger, Zipkin), profiling (like coz, Tracy), error reporting (like Sentry), etc.
For better understanding and familiarity with tracing's design, concepts, usage, and features, read through the following articles:
- Official
tracingcrate docs - Joshua Mo: Getting Started with Tracing in Rust
- Yoav Danieli: Guide to OpenTelemetry Distributed Tracing in Rust
- Tokio Blog: Diagnostics with Tracing
- Hayden Stainsby: debugging tokio instrumentation
Estimated time: 1 day
Implement two loggers:
- Global main
app.loglogger which prints all its logs toSTDOUT, butWARNlevel (and higher) logs toSTDERR. - Local
access.loglogger which writes all its logs toaccess.logfile.
All logs should be structured and logged in a JSON format, and have time field with nanoseconds (RFC 3339 formatted).
Examples:
{"lvl":"ERROR","file":"app.log","time":"2018-07-30T12:14:14.196483657Z","msg":"Error occurred"}
{"lvl":"INFO","file":"access.log","time":"2018-07-30T12:17:18.721127239Z","msg":"http","method":"POST","path":"/some"}After completing everything above, you should be able to answer (and understand why) the following questions:
- How does
logcrate achieve its reusability over ecosystem? What are the ideas behind it? - Why logging is preferred over printing (
println!usage)? When it's not? - What is structured logging? What benefits does it provide?
- Why
tracingcrate is good for logging? What makes it preferred overslogandlogcrates? - What is tracing? Why is it beneficial for observability?