1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <time.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <errno.h>
11
12 #include "slog.h"
13 #include "server.h"
14 #include "conf.h"
15
16 #if SLOG_MSG_MAX_LEN < 64
17 #error "SLOG_MSG_MAX_LEN must be at least 64"
18 #endif
19
20 /**
21 * Initialize log writer.
22 */
23 void
slog_init(struct server * s)24 slog_init(struct server *s) {
25
26 s->log.self = getpid();
27
28 if(s->cfg->logfile) {
29
30 int old_fd = s->log.fd;
31
32 s->log.fd = open(s->cfg->logfile,
33 O_WRONLY | O_APPEND | O_CREAT, S_IRUSR|S_IWUSR);
34
35 /* close old log */
36 if (old_fd != -1) {
37 close(old_fd);
38 }
39
40 if (s->log.fd != -1)
41 return;
42
43 fprintf(stderr, "Could not open %s: %s\n", s->cfg->logfile,
44 strerror(errno));
45 }
46 s->log.fd = 2; /* stderr */
47 }
48
49 static void
slog_fsync_tick(int fd,short event,void * data)50 slog_fsync_tick(int fd, short event, void *data) {
51 struct server *s = data;
52 int ret = fsync(s->log.fd);
53 (void)fd;
54 (void)event;
55 (void)ret;
56 }
57
58 void
slog_fsync_init(struct server * s)59 slog_fsync_init(struct server *s) {
60 if (s->cfg->log_fsync.mode != LOG_FSYNC_MILLIS) {
61 return;
62 }
63 /* install a libevent timer for handling fsync */
64 s->log.fsync_ev = event_new(s->base, -1, EV_PERSIST, slog_fsync_tick, s);
65 if(s->log.fsync_ev == NULL) {
66 const char evnew_error[] = "fsync timer could not be created";
67 slog(s, WEBDIS_ERROR, evnew_error, sizeof(evnew_error)-1);
68 return;
69 }
70
71 int period_usec =s->cfg->log_fsync.period_millis * 1000;
72 s->log.fsync_tv.tv_sec = period_usec / 1000000;
73 s->log.fsync_tv.tv_usec = period_usec % 1000000;
74 int ret_ta = evtimer_add(s->log.fsync_ev, &s->log.fsync_tv);
75 if(ret_ta != 0) {
76 const char reason[] = "fsync timer could not be added: %d";
77 char error_msg[sizeof(reason) + 16]; /* plenty of extra space */
78 int error_len = snprintf(error_msg, sizeof(error_msg), reason, ret_ta);
79 slog(s, WEBDIS_ERROR, error_msg, (size_t)error_len);
80 }
81 }
82
83 /**
84 * Returns whether this log level is enabled.
85 */
86 int
slog_enabled(struct server * s,log_level level)87 slog_enabled(struct server *s, log_level level) {
88 return level <= s->cfg->verbosity ? 1 : 0;
89 }
90
91 /**
92 * Write log message to disk, or stderr.
93 */
94 static void
slog_internal(struct server * s,log_level level,const char * body,size_t sz)95 slog_internal(struct server *s, log_level level,
96 const char *body, size_t sz) {
97
98 const char *c = "EWNIDT";
99 time_t now;
100 struct tm now_tm, *lt_ret;
101 char time_buf[64];
102 char msg[1 + SLOG_MSG_MAX_LEN];
103 char line[2 * SLOG_MSG_MAX_LEN]; /* bounds are checked. */
104 int line_sz, ret;
105
106 if(!s->log.fd) return;
107
108 /* limit message size */
109 sz = sz ? sz:strlen(body);
110 snprintf(msg, sz + 1 > sizeof(msg) ? sizeof(msg) : sz + 1, "%s", body);
111
112 /* get current time */
113 now = time(NULL);
114 lt_ret = localtime_r(&now, &now_tm);
115 if(lt_ret) {
116 strftime(time_buf, sizeof(time_buf), "%d %b %H:%M:%S", lt_ret);
117 } else {
118 const char err_msg[] = "(NO TIME AVAILABLE)";
119 memcpy(time_buf, err_msg, sizeof(err_msg));
120 }
121
122 /* generate output line. */
123 char letter = (level == WEBDIS_TRACE ? 5 : c[level]);
124 line_sz = snprintf(line, sizeof(line),
125 "[%d] %s %c %s\n", (int)s->log.self, time_buf, letter, msg);
126
127 /* write to log and maybe flush to disk. */
128 ret = write(s->log.fd, line, line_sz);
129 if(s->cfg->log_fsync.mode == LOG_FSYNC_ALL) {
130 ret = fsync(s->log.fd);
131 }
132
133 (void)ret;
134 }
135
136 /**
137 * Thin wrapper around slog_internal that first checks the log level.
138 */
139 void
slog(struct server * s,log_level level,const char * body,size_t sz)140 slog(struct server *s, log_level level,
141 const char *body, size_t sz) {
142 if(level <= s->cfg->verbosity) { /* check log level first */
143 slog_internal(s, level, body, sz);
144 }
145 }
146