/* * log.c - Non-blocking timestamped stderr logger * * Formats messages into a pipe with O_NONBLOCK so the hot path never * blocks on I/O. A background thread drains the pipe and writes to * stderr. When the pipe buffer is full messages are silently dropped. */ #define _GNU_SOURCE #include "log.h" #include #include #include #include #include #include #include #include #include #include static int log_pipe[2] = {-1, -1}; static pthread_t log_thread; static atomic_bool log_running = false; /* Background thread: drains the pipe and writes each chunk to stderr. Spins on EAGAIN with 100us sleep when the pipe is empty. */ static void *log_worker(void *arg) { (void)arg; char buf[4096]; while (atomic_load(&log_running)) { ssize_t n = read(log_pipe[0], buf, sizeof(buf)); if (n > 0) { write(STDERR_FILENO, buf, (size_t)n); } else if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { usleep(100); } else { break; } } else { break; } } return NULL; } /* Create a non-blocking pipe and start the background drain thread. */ void log_init(void) { if (pipe2(log_pipe, O_NONBLOCK) != 0) { log_pipe[0] = log_pipe[1] = -1; return; } atomic_store(&log_running, true); pthread_create(&log_thread, NULL, log_worker, NULL); } /* Stop the worker thread and close both pipe ends. */ void log_shutdown(void) { atomic_store(&log_running, false); if (log_thread) { pthread_join(log_thread, NULL); } if (log_pipe[0] >= 0) { close(log_pipe[0]); log_pipe[0] = -1; } if (log_pipe[1] >= 0) { close(log_pipe[1]); log_pipe[1] = -1; } } /* Non-blocking log write. Formats a timestamped message into the pipe. Falls back to sync stderr write if the pipe has not been initialised. */ void log_write(const char *fmt, ...) { if (log_pipe[1] < 0) { /* fallback: sync write to stderr */ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); return; } char ts[32]; time_t t = time(NULL); struct tm tm; localtime_r(&t, &tm); strftime(ts, sizeof(ts), "[%Y/%m/%d %H:%M:%S] ", &tm); char buf[1536]; int ts_len = (int)strlen(ts); memcpy(buf, ts, (size_t)ts_len); va_list ap; va_start(ap, fmt); int msg_len = vsnprintf(buf + ts_len, sizeof(buf) - (size_t)ts_len, fmt, ap); va_end(ap); int total = ts_len + (msg_len > 0 ? msg_len : 0); if (total > 0) { write(log_pipe[1], buf, (size_t)total); } }