triangular_arbitrage_bot/src/log.c

100 lines
2.7 KiB
C

/*
* 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 <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
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);
}
}