3 unstable releases

0.2.0 Nov 11, 2024
0.1.1 Oct 22, 2024
0.1.0 Oct 14, 2024

#134 in Debugging

Download history 227/week @ 2024-10-14 168/week @ 2024-10-21 13/week @ 2024-10-28 24/week @ 2024-11-04 153/week @ 2024-11-11

368 downloads per month
Used in 5 crates (4 directly)

MIT/Apache

120KB
888 lines

logwise

logo

logwise is an opinionated logging library for Rust.

Development status

logwise is experimental and the API may change.

The problem

Typical logging crates, such as log, offer small set of generic (that is, vague) log levels (error, warn, info, debug, trace). But these are so vague and the implementations of loggers so varied that I am never really sure which one to use.

Here are some problems:

  • Suppose I am doing print-style debugging in my library. Is debug the right level to use? Or is debug where I put messages for users of my library to debug their own code?
  • How can I compile-out expensive logs by default but collect them from users when they report a bug?
  • What's the appropriate log level for "This is slow and should be optimized"?

These problems cannot be solved within the ecosystem-wide common-denominator API, so here we are.

An analogy

There is a simple analogy to module visibility. There are usecases to make a module private. There are usecases to make it public. To make it pub(crate). To make it pub(super), except if it's a release build, and so on. logwise provides tools like that, but for logging.

The facade

logwise provides an opinionated set of log levels for defined set of usecases.

Name Usecase Build type required Conditions
trace Detailed debugging debug builds only Must turn on per-thread
debuginternal print-style debugging debug builds only On by default in the current crate. Must turn on per-thread in downstream crates.
info Supporting downstream crates debug builds only On by default
perfwarn Log performance problems, with analysis all all
warning Suspicious condition all all
error logging the error in a Result all all
panic logging a programmer error all all

(More levels may be added).

The implementation

logwise currently logs all messages to stderr. In the future, other logging backends may be added.

The API

For example,

        logwise::debuginternal_sync!("Hello {world}!",world=val);

See the docs for more information. Each log level has a synchronous and asynchronous version. The synchronous version can be used from any context. The asynchronous version allows the logging to be deferred to your async executor for better performance in some cases.

The API supports a simple key-value syntax for structured logging. The right-hand side of the expression is only evaluated if the log message is actually printed, and is compiled out when necessary.

Privacy

Consider the following log message:

Completed job 23 named 'Gift for Alice' in 3.4 seconds.

Another gripe I have about the log crate's API is there is no way to represent which parts of the log may contain sensitive user data (in this case, the name of the job). It may be useful to collect this log, but it may be undesirable to include the sensitive variable in the log message.

logwise's design is designed each variable in the log message conforms to the Loggable trait, which defines a public and private representation of the variable. This allows different versions of the log to be generated from the same log message, depending on the desired privacy level.

Multithreading

logwise has out-of-the-box support for scoped logging in a stack-based, thread-local model. If you are e.g. spawning a child thread, writing an async executor or similar, consider using the logwise::context APIs to propagate the logging context to child threads.

Dependencies

~0–2.3MB
~43K SLoC