Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rolling Appender #21

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
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
76 changes: 69 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,49 @@ 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 (stat(ev->ra.file_name, &buf) < 0) {
sprintf(msg, "Unable to stat log file: %s", ev->ra.file_name);
perror(msg);
free(msg);
return;
}

free(msg);

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 +164,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 +219,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
26 changes: 24 additions & 2 deletions src/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,28 @@
#ifndef LOG_H
#define LOG_H

#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif

#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.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 +39,7 @@ typedef struct {
void *udata;
int line;
int level;
rolling_appender ra;
} log_Event;

typedef void (*log_LogFn)(log_Event *ev);
Expand All @@ -42,8 +59,13 @@ 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, ...);

#ifdef __cplusplus
}
#endif

#endif