Skip to content

Commit 0b83ec0

Browse files
committed
Rolling Appender
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.
1 parent f9ea349 commit 0b83ec0

File tree

3 files changed

+95
-9
lines changed

3 files changed

+95
-9
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ function is passed a `log_Event` structure containing the `line` number,
6363
`filename`, `fmt` string, `va` printf va\_list, `level` and the given `udata`.
6464

6565

66+
#### log_add_rolling_appender(rolling_appender ra, int level)
67+
One or more rolling log appenders. This is functionally equivalent to
68+
`log_add_fp()` from a logging standpoint. The function also handles log _file_
69+
management based on the values provided by the `rolling_appender` struct
70+
argument.
71+
72+
After a log entry pushes the log size over the `ra.max_log_size` value, your
73+
active log is automatically rolled.
74+
75+
Rolled logs appear in the same directory as the active log, and retain the same
76+
name as the active log, except a numeric value from `1` to `ra.max_logs`
77+
is appended to the end of the file name.
78+
79+
6680
#### log_set_lock(log_LockFn fn, void *udata)
6781
If the log will be written to from multiple threads a lock function can be set.
6882
The function is passed the boolean `true` if the lock should be acquired or

src/log.c

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ typedef struct {
2828
log_LogFn fn;
2929
void *udata;
3030
int level;
31+
rolling_appender ra;
3132
} Callback;
3233

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

5253

54+
int get_next_available_callback() {
55+
for (int i = 0; i < MAX_CALLBACKS; i++)
56+
if (!L.callbacks[i].fn)
57+
return i;
58+
return -1;
59+
}
60+
5361
static void stdout_callback(log_Event *ev) {
5462
char buf[16];
5563
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
@@ -81,6 +89,47 @@ static void file_callback(log_Event *ev) {
8189
}
8290

8391

92+
static void rolling_appender_callback(log_Event *ev) {
93+
char* msg = (char*)calloc(strlen(ev->ra.file_name)+1024, sizeof(char));
94+
95+
if (ev->udata == NULL)
96+
ev->udata = fopen(ev->ra.file_name, "a");
97+
if (ev->udata == NULL) {
98+
sprintf(msg, "Unable to open log file: %s", ev->ra.file_name);
99+
perror(msg);
100+
free(msg);
101+
return;
102+
}
103+
104+
file_callback(ev);
105+
106+
struct stat buf;
107+
if (lstat(ev->ra.file_name, &buf) < 0) {
108+
sprintf(msg, "Unable to stat log file: %s", ev->ra.file_name);
109+
perror(msg);
110+
free(msg);
111+
return;
112+
}
113+
114+
if (buf.st_size >= ev->ra.max_log_size) {
115+
char* old = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
116+
char* new = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
117+
fclose(ev->udata);
118+
ev->udata = NULL;
119+
for (unsigned int i = ev->ra.max_logs-1; i >= 1; i--) {
120+
sprintf(old, "%s.%u", ev->ra.file_name, i);
121+
sprintf(new, "%s.%u", ev->ra.file_name, i+1);
122+
rename(old, new);
123+
}
124+
sprintf(new, "%s.1", ev->ra.file_name);
125+
rename(ev->ra.file_name, new);
126+
127+
free(old);
128+
free(new);
129+
}
130+
}
131+
132+
84133
static void lock(void) {
85134
if (L.lock) { L.lock(true, L.udata); }
86135
}
@@ -113,13 +162,22 @@ void log_set_quiet(bool enable) {
113162

114163

115164
int log_add_callback(log_LogFn fn, void *udata, int level) {
116-
for (int i = 0; i < MAX_CALLBACKS; i++) {
117-
if (!L.callbacks[i].fn) {
118-
L.callbacks[i] = (Callback) { fn, udata, level };
119-
return 0;
120-
}
121-
}
122-
return -1;
165+
int nac = get_next_available_callback();
166+
if (nac < 0)
167+
return nac;
168+
169+
L.callbacks[nac] = (Callback) { fn, udata, level, {0} };
170+
return 0;
171+
}
172+
173+
174+
int log_add_rolling_appender(rolling_appender ra, int level) {
175+
int nac = get_next_available_callback();
176+
if (nac < 0)
177+
return nac;
178+
179+
L.callbacks[nac] = (Callback) { rolling_appender_callback, NULL, level, ra };
180+
return 0;
123181
}
124182

125183

@@ -159,7 +217,9 @@ void log_log(int level, const char *file, int line, const char *fmt, ...) {
159217
if (level >= cb->level) {
160218
init_event(&ev, cb->udata);
161219
va_start(ev.ap, fmt);
220+
ev.ra = cb->ra;
162221
cb->fn(&ev);
222+
cb->udata = ev.udata;
163223
va_end(ev.ap);
164224
}
165225
}

src/log.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@
88
#ifndef LOG_H
99
#define LOG_H
1010

11-
#include <stdio.h>
1211
#include <stdarg.h>
12+
#include <stdio.h>
1313
#include <stdbool.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <sys/stat.h>
1417
#include <time.h>
18+
#include <unistd.h>
1519

16-
#define LOG_VERSION "0.1.0"
20+
#define LOG_VERSION "0.2.0"
21+
22+
typedef struct {
23+
char* file_name;
24+
off_t max_log_size;
25+
unsigned int max_logs;
26+
} rolling_appender;
1727

1828
typedef struct {
1929
va_list ap;
@@ -23,6 +33,7 @@ typedef struct {
2333
void *udata;
2434
int line;
2535
int level;
36+
rolling_appender ra;
2637
} log_Event;
2738

2839
typedef void (*log_LogFn)(log_Event *ev);
@@ -42,6 +53,7 @@ void log_set_lock(log_LockFn fn, void *udata);
4253
void log_set_level(int level);
4354
void log_set_quiet(bool enable);
4455
int log_add_callback(log_LogFn fn, void *udata, int level);
56+
int log_add_rolling_appender(rolling_appender ra, int level);
4557
int log_add_fp(FILE *fp, int level);
4658

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

0 commit comments

Comments
 (0)