rust-cli / env_logger

A logging implementation for `log` which is configured via an environment variable.
https://docs.rs/env_logger
Apache License 2.0
797 stars 124 forks source link

Is this library thread-safe? #269

Closed your-diary closed 7 months ago

your-diary commented 1 year ago

Is this library thread-safe?

README.md and the official documentation say nothing about that.

Is this code safe?

use log::error;
use std::thread;

fn main() {
    env_logger::init();

    let handle = thread::spawn(|| {
        error!("hello");
    });

    error!("world");

    handle.join().unwrap();
}
matthiasbeyer commented 1 year ago

What exactly do you mean by "thread safe"?

It won't print hewollrlod, but it will either print "hello\nworld\n" or "world\nhello\n"!

your-diary commented 1 year ago

@matthiasbeyer

What exactly do you mean by "thread safe"?

I wanted to mean "can this library print hewollrlod"?

It won't print hewollrlod

Is this behavior guaranteed? If so, to confirm it, do I have to read the source code? Or is it written in some documentation?

epage commented 1 year ago

Maybe its a bias of mine but that would be expected within the Rust ecosystem. Just like println! will not interleave text (but multiple printlns will), a logged event should not be interleaved.

your-diary commented 1 year ago

@epage

As far as I understand, println!() is thread-safe because it always locks and unlocks /dev/stdout (unofficial source).

So I think it is possible this library avoids println!() family to improve performance because locking / unlocking is relatively slow. Such an implementation is clearly problematic in using multi-threaded context, but safe in single-threaded context.

epage commented 1 year ago

std only exposes stdout / stderr with locking. This is independent of the println macro.. You can explicitly acquire the lock to be able to make multiple calls, but there is always a lock. You'd need to pull in something like libc to do output without a lock which is enough off the beaten path that it isn't a general assumption when working within Rust code.

The bigger risk is making multiple calls to stdout / stderr without grabbing the lock. This is both slower (multiple aquire/releases) and can lead to interleaved output.

And to get back to my point, the general expectation within the Rust community is that println and println-like macros, like the log macros, will not interleave.