Skip to content

Commit

Permalink
Rolling Appender
Browse files Browse the repository at this point in the history
Added a rolling appender function for basic log file management.

Most applications that are concerned with this behavior should consider sending
messages to syslog and use traditional logrotate mechanisms to manage log
files. However, there is a niche of applications that could benefit from this
feature in a compact logging library.
  • Loading branch information
chuckwolber committed Aug 8, 2020
1 parent f9ea349 commit 0b83ec0
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 9 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ function is passed a `log_Event` structure containing the `line` number,
`filename`, `fmt` string, `va` printf va\_list, `level` and the given `udata`.


#### log_add_rolling_appender(rolling_appender ra, int level)
One or more rolling log appenders. This is functionally equivalent to
`log_add_fp()` from a logging standpoint. The function also handles log _file_
management based on the values provided by the `rolling_appender` struct
argument.

After a log entry pushes the log size over the `ra.max_log_size` value, your
active log is automatically rolled.

Rolled logs appear in the same directory as the active log, and retain the same
name as the active log, except a numeric value from `1` to `ra.max_logs`
is appended to the end of the file name.


#### log_set_lock(log_LockFn fn, void *udata)
If the log will be written to from multiple threads a lock function can be set.
The function is passed the boolean `true` if the lock should be acquired or
Expand Down
74 changes: 67 additions & 7 deletions src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ typedef struct {
log_LogFn fn;
void *udata;
int level;
rolling_appender ra;
} Callback;

static struct {
Expand All @@ -50,6 +51,13 @@ static const char *level_colors[] = {
#endif


int get_next_available_callback() {
for (int i = 0; i < MAX_CALLBACKS; i++)
if (!L.callbacks[i].fn)
return i;
return -1;
}

static void stdout_callback(log_Event *ev) {
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
Expand Down Expand Up @@ -81,6 +89,47 @@ static void file_callback(log_Event *ev) {
}


static void rolling_appender_callback(log_Event *ev) {
char* msg = (char*)calloc(strlen(ev->ra.file_name)+1024, sizeof(char));

if (ev->udata == NULL)
ev->udata = fopen(ev->ra.file_name, "a");
if (ev->udata == NULL) {
sprintf(msg, "Unable to open log file: %s", ev->ra.file_name);
perror(msg);
free(msg);
return;
}

file_callback(ev);

struct stat buf;
if (lstat(ev->ra.file_name, &buf) < 0) {
sprintf(msg, "Unable to stat log file: %s", ev->ra.file_name);
perror(msg);
free(msg);
return;
}

if (buf.st_size >= ev->ra.max_log_size) {
char* old = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
char* new = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
fclose(ev->udata);
ev->udata = NULL;
for (unsigned int i = ev->ra.max_logs-1; i >= 1; i--) {
sprintf(old, "%s.%u", ev->ra.file_name, i);
sprintf(new, "%s.%u", ev->ra.file_name, i+1);
rename(old, new);
}
sprintf(new, "%s.1", ev->ra.file_name);
rename(ev->ra.file_name, new);

free(old);
free(new);
}
}


static void lock(void) {
if (L.lock) { L.lock(true, L.udata); }
}
Expand Down Expand Up @@ -113,13 +162,22 @@ void log_set_quiet(bool 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;
}
}
return -1;
int nac = get_next_available_callback();
if (nac < 0)
return nac;

L.callbacks[nac] = (Callback) { fn, udata, level, {0} };
return 0;
}


int log_add_rolling_appender(rolling_appender ra, int level) {
int nac = get_next_available_callback();
if (nac < 0)
return nac;

L.callbacks[nac] = (Callback) { rolling_appender_callback, NULL, level, ra };
return 0;
}


Expand Down Expand Up @@ -159,7 +217,9 @@ void log_log(int level, const char *file, int line, const char *fmt, ...) {
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
ev.ra = cb->ra;
cb->fn(&ev);
cb->udata = ev.udata;
va_end(ev.ap);
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@
#ifndef LOG_H
#define LOG_H

#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#define LOG_VERSION "0.1.0"
#define LOG_VERSION "0.2.0"

typedef struct {
char* file_name;
off_t max_log_size;
unsigned int max_logs;
} rolling_appender;

typedef struct {
va_list ap;
Expand All @@ -23,6 +33,7 @@ typedef struct {
void *udata;
int line;
int level;
rolling_appender ra;
} log_Event;

typedef void (*log_LogFn)(log_Event *ev);
Expand All @@ -42,6 +53,7 @@ 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_rolling_appender(rolling_appender ra, int level);
int log_add_fp(FILE *fp, int level);

void log_log(int level, const char *file, int line, const char *fmt, ...);
Expand Down

0 comments on commit 0b83ec0

Please sign in to comment.