Skip to content

Commit 417f8ee

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 417f8ee

File tree

3 files changed

+107
-9
lines changed

3 files changed

+107
-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: 69 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,49 @@ 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 (stat(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+
free(msg);
115+
116+
if (buf.st_size >= ev->ra.max_log_size) {
117+
char* old = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
118+
char* new = (char*)calloc(strlen(ev->ra.file_name)+10, sizeof(char));
119+
fclose(ev->udata);
120+
ev->udata = NULL;
121+
for (unsigned int i = ev->ra.max_logs-1; i >= 1; i--) {
122+
sprintf(old, "%s.%u", ev->ra.file_name, i);
123+
sprintf(new, "%s.%u", ev->ra.file_name, i+1);
124+
rename(old, new);
125+
}
126+
sprintf(new, "%s.1", ev->ra.file_name);
127+
rename(ev->ra.file_name, new);
128+
129+
free(old);
130+
free(new);
131+
}
132+
}
133+
134+
84135
static void lock(void) {
85136
if (L.lock) { L.lock(true, L.udata); }
86137
}
@@ -113,13 +164,22 @@ void log_set_quiet(bool enable) {
113164

114165

115166
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;
167+
int nac = get_next_available_callback();
168+
if (nac < 0)
169+
return nac;
170+
171+
L.callbacks[nac] = (Callback) { fn, udata, level, {0} };
172+
return 0;
173+
}
174+
175+
176+
int log_add_rolling_appender(rolling_appender ra, int level) {
177+
int nac = get_next_available_callback();
178+
if (nac < 0)
179+
return nac;
180+
181+
L.callbacks[nac] = (Callback) { rolling_appender_callback, NULL, level, ra };
182+
return 0;
123183
}
124184

125185

@@ -159,7 +219,9 @@ void log_log(int level, const char *file, int line, const char *fmt, ...) {
159219
if (level >= cb->level) {
160220
init_event(&ev, cb->udata);
161221
va_start(ev.ap, fmt);
222+
ev.ra = cb->ra;
162223
cb->fn(&ev);
224+
cb->udata = ev.udata;
163225
va_end(ev.ap);
164226
}
165227
}

src/log.h

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

11-
#include <stdio.h>
11+
#ifdef __cplusplus
12+
extern "C"
13+
{
14+
#endif
15+
1216
#include <stdarg.h>
17+
#include <stdio.h>
1318
#include <stdbool.h>
19+
#include <stdlib.h>
20+
#include <string.h>
21+
#include <sys/stat.h>
22+
#include <sys/types.h>
1423
#include <time.h>
24+
#include <unistd.h>
1525

16-
#define LOG_VERSION "0.1.0"
26+
#define LOG_VERSION "0.2.0"
27+
28+
typedef struct {
29+
char* file_name;
30+
off_t max_log_size;
31+
unsigned int max_logs;
32+
} rolling_appender;
1733

1834
typedef struct {
1935
va_list ap;
@@ -23,6 +39,7 @@ typedef struct {
2339
void *udata;
2440
int line;
2541
int level;
42+
rolling_appender ra;
2643
} log_Event;
2744

2845
typedef void (*log_LogFn)(log_Event *ev);
@@ -42,8 +59,13 @@ void log_set_lock(log_LockFn fn, void *udata);
4259
void log_set_level(int level);
4360
void log_set_quiet(bool enable);
4461
int log_add_callback(log_LogFn fn, void *udata, int level);
62+
int log_add_rolling_appender(rolling_appender ra, int level);
4563
int log_add_fp(FILE *fp, int level);
4664

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

67+
#ifdef __cplusplus
68+
}
69+
#endif
70+
4971
#endif

0 commit comments

Comments
 (0)