Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 87 additions & 99 deletions src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,144 +25,132 @@
#define MAX_CALLBACKS 32

typedef struct {
log_LogFn fn;
void *udata;
int level;
log_LogFn fn;
void *udata;
int level;
} Callback;

static struct {
void *udata;
log_LockFn lock;
int level;
bool quiet;
Callback callbacks[MAX_CALLBACKS];
} L;
void *udata;
log_LockFn lock;
int level;
bool quiet;
Callback callbacks[MAX_CALLBACKS];
} L = {
NULL, NULL, RXI_LOGC_DEFAULT_LEVEL, false, {0},
};

static const char *level_strings[] = {"[[DEBUG]]", "[[TRACE]]", "[[ INFO]]",
"[[ WARN]]", "[[ERROR]]", "[[FATAL]]"};

static const char *level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
static inline const char *get_level_string(int level) {
return level_strings[(level + 32) / 32];
}

#ifdef LOG_USE_COLOR
static const char *level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
};
#endif
static const char *level_colors[] = {"\x1b[36m", "\x1b[94m", "\x1b[32m",
"\x1b[33m", "\x1b[31m", "\x1b[35m"};

static inline const char *get_level_color(int level) {
return level_colors[(level + 32) / 32];
}
#endif

static void stdout_callback(log_Event *ev) {
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
#ifdef LOG_USE_COLOR
fprintf(
ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
buf, level_colors[ev->level], level_strings[ev->level],
ev->file, ev->line);
fprintf(ev->udata, "%s %s%-7s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf,
get_level_color(ev->level), get_level_string(ev->level), ev->file,
ev->line);
#else
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
fprintf(ev->udata, "%s %-7s %s:%d: ", buf, get_level_string(ev->level),
ev->file, ev->line);
#endif
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}


static void file_callback(log_Event *ev) {
char buf[64];
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
char buf[64];
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
fprintf(ev->udata, "%s %-7s %s:%d: ", buf, get_level_string(ev->level),
ev->file, ev->line);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}


static void lock(void) {
if (L.lock) { L.lock(true, L.udata); }
static void lock(void) {
if (L.lock) {
L.lock(true, L.udata);
}
}


static void unlock(void) {
if (L.lock) { L.lock(false, L.udata); }
}


const char* log_level_string(int level) {
return level_strings[level];
if (L.lock) {
L.lock(false, L.udata);
}
}


void log_set_lock(log_LockFn fn, void *udata) {
L.lock = fn;
L.udata = udata;
L.lock = fn;
L.udata = udata;
}

void log_set_level(int level) { L.level = level; }

void log_set_level(int level) {
L.level = level;
}


void log_set_quiet(bool enable) {
L.quiet = enable;
}

void log_set_quiet(bool enable) { L.quiet = enable; }

int log_add_callback(log_LogFn fn, void *udata, int level) {
for (int i = 0; i < MAX_CALLBACKS; i++) {
if (!L.callbacks[i].fn) {
L.callbacks[i] = (Callback) { fn, udata, level };
return 0;
for (int i = 0; i < MAX_CALLBACKS; i++) {
if (!L.callbacks[i].fn) {
L.callbacks[i] = (Callback){fn, udata, level};
return 0;
}
}
}
return -1;
return -1;
}


int log_add_fp(FILE *fp, int level) {
return log_add_callback(file_callback, fp, level);
return log_add_callback(file_callback, fp, level);
}


static void init_event(log_Event *ev, void *udata) {
if (!ev->time) {
time_t t = time(NULL);
ev->time = localtime(&t);
}
ev->udata = udata;
if (!ev->time) {
time_t t = time(NULL);
ev->time = localtime_r(&t, &ev->time_buf);
}
ev->udata = udata;
}


void log_log(int level, const char *file, int line, const char *fmt, ...) {
log_Event ev = {
.fmt = fmt,
.file = file,
.line = line,
.level = level,
};

lock();

if (!L.quiet && level >= L.level) {
init_event(&ev, stderr);
va_start(ev.ap, fmt);
stdout_callback(&ev);
va_end(ev.ap);
}

for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
Callback *cb = &L.callbacks[i];
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
cb->fn(&ev);
va_end(ev.ap);
log_Event ev = {
.fmt = fmt,
.file = file,
.line = line,
.level = level,
};

lock();

if (!L.quiet && level >= L.level) {
init_event(&ev, stderr);
va_start(ev.ap, fmt);
stdout_callback(&ev);
va_end(ev.ap);
}

for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
Callback *cb = &L.callbacks[i];
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
cb->fn(&ev);
va_end(ev.ap);
}
}
}

unlock();
unlock();
}
77 changes: 60 additions & 17 deletions src/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,78 @@

#define LOG_VERSION "0.1.0"

#ifndef RXI_LOGC_DEFAULT_LEVEL
#define RXI_LOGC_DEFAULT_LEVEL LOG_TRACE
#endif

#if defined __GNUC__
#define RXI_LOGC_PRINTF_ATTRIB(n, m) __attribute__((format(printf, n, m)))
#else
#define RXI_LOGC_PRINTF_ATTRIB(n, m)
#endif

enum {
LOG_DEBUG = -32,
LOG_TRACE = 0,
LOG_INFO = 32,
LOG_WARN = 64,
LOG_ERROR = 96,
LOG_FATAL = 128,
};

typedef struct {
va_list ap;
const char *fmt;
const char *file;
struct tm *time;
void *udata;
int line;
int level;
va_list ap;
const char *fmt;
const char *file;
struct tm *time;
struct tm time_buf;
void *udata;
int line;
int level;
} log_Event;

typedef void (*log_LogFn)(log_Event *ev);
typedef void (*log_LockFn)(bool lock, void *udata);

enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };

#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) \
do { \
if (LOG_DEBUG >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#define log_trace(...) \
do { \
if (LOG_TRACE >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#define log_info(...) \
do { \
if (LOG_INFO >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#define log_warn(...) \
do { \
if (LOG_WARN >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#define log_error(...) \
do { \
if (LOG_ERROR >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#define log_fatal(...) \
do { \
if (LOG_FATAL >= RXI_LOGC_DEFAULT_LEVEL) \
log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)

const char* log_level_string(int level);
const char *log_level_string(int level);
void log_set_lock(log_LockFn fn, void *udata);
void log_set_level(int level);
void log_set_quiet(bool enable);
int log_add_callback(log_LogFn fn, void *udata, int level);
int log_add_fp(FILE *fp, int level);

void log_log(int level, const char *file, int line, const char *fmt, ...);
void log_log(int level, const char *file, int line, const char *fmt, ...)
RXI_LOGC_PRINTF_ATTRIB(4, 5);

#endif