Repository navigation
Sinks and Threading
A sink receives the formatted output:
using SinkFn = void (*)(const char *data, size_t size);By default, output goes to stderr. On POSIX, writes are retried on EINTR and on partial writes. set_sink(fn) redirects it, and reset_sink() or set_sink(nullptr) restores stderr.
SinkFn is a plain function pointer, so a lambda passed to it cannot capture anything. Keep sink state in variables with static storage duration:
static FILE *log_file = std::fopen("app.log", "w");
coretrace::set_sink([](const char *data, size_t size) {
std::fwrite(data, 1, size, log_file);
});
// ...
coretrace::reset_sink();
std::fclose(log_file); // Safe: see "Changing the sink" below.Each log() line reaches the sink in one call containing the whole line: prefix, message and your trailing \n. The sink is chosen once per line, so a line never ends up split between two sinks.
Thread-safe mode is on by default (set_thread_safe(true)). In this mode:
- Lines are serialized: the sink is never called for two lines at once, so a sink can append to a
std::stringor aFILE*without its own lock. - The format-error fallback (
coretrace: log format error) is serialized like any other line. - The output lock is reentrant. A sink may call
log(), for example to report its own error, and may callset_sink(); the new sink applies to the next line.
set_thread_safe(false) removes the lock, for single-threaded hot paths. The sink can then be called from several threads at once, and the guarantees below no longer hold.
On POSIX, fork() waits for a line another thread is writing, and for the logger's initialization. The child therefore inherits neither half done, and can log right away, even when threads of the parent were logging at the time of the fork. Its lines carry its own PID.
- A sink that waits for the thread calling
fork()deadlocks it. - A sink may call
fork(), and the child can log. - Fork handlers registered with
pthread_atfork(), before or after the logger's, may log. -
fork()keeps working while the program exits, from anatexit()handler or a static destructor.
With thread safety enabled, set_sink() and reset_sink() wait for a line that is still being written. Once they return, log() no longer calls the previous sink, so its resources (file, socket, buffer) can be released.
write_raw(), write_str(), write_dec(), write_hex() and write_prefix() write straight to the current sink. Unlike log(), they are not serialized with other lines, even in thread-safe mode. Use them from one thread, or accept that their output may interleave with other lines.
Whether lines include ANSI colors depends on stderr, not on the sink (see Configuration). A program started from a terminal therefore writes escape sequences into a file sink. Set NO_COLOR=1 to avoid that.
Using the library
Working on the library