1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #ifdef __gnu_linux__
30 #define _GNU_SOURCE
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <aio.h>
38 #include <time.h>
39 #include <pthread.h>
40
41 #include "log.h"
42 #include "../util/strbuf.h"
43 #include "../util/io.h"
44 #include "../util/atomic.h"
45
46 #include <cx/hash_map.h>
47 #include <cx/linked_list.h>
48 #include <cx/compare.h>
49
50 static int is_initialized =
0;
51
52 static int log_file_fd;
53 static int log_level =
0;
54
55 static uint32_t log_dup_count =
0;
56 static CxList *log_dup_list =
NULL;
57 static pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
58
59 WSBool main_is_daemon(
void);
60
61
62
63
64 static sbuf_t *ui_buffer =
NULL;
65
66
67
68
69 static CxMap *access_log_files;
70
71
72 static char *log_date_month[] = {
73 "Jan",
74 "Feb",
75 "Mar",
76 "Apr",
77 "May",
78 "Jun",
79 "Jul",
80 "Aug",
81 "Sep",
82 "Oct",
83 "Nov",
84 "Dec"
85 };
86
87 static const char *log_levels[] = {
88 "warning",
89 "config",
90 "security",
91 "failure",
92 "catastrophe",
93 "info",
94 "verbose",
95 "debug"
96 };
97
98 static int can_log[] = {
99 1,
100 1,
101 1,
102 1,
103 1,
104 1,
105 0,
106 0
107 };
108
109 int init_logging(
void) {
110 log_dup_list = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
111 return log_dup_list ==
NULL;
112 }
113
114 int init_log_file(LogConfig *cfg) {
115 if(is_initialized) {
116 return 0;
117 }
118
119
120 mode_t mode =
S_IRUSR |
S_IWUSR |
S_IRGRP |
S_IROTH;
121 log_file_fd = open(cfg->file,
O_WRONLY |
O_CREAT |
O_APPEND, mode);
122 if(log_file_fd == -
1) {
123 log_ereport(
LOG_FAILURE,
"Cannot open log file %s: %s", cfg->file, strerror(errno));
124 return -
1;
125 }
126
127 if(!strcmp(cfg->level,
"ERROR")) {
128 can_log[
LOG_WARN] =
0;
129 can_log[
LOG_INFORM] =
0;
130 }
else if(!strcmp(cfg->level,
"WARNING")) {
131 can_log[
LOG_INFORM] =
0;
132 }
else if(!strcmp(cfg->level,
"VERBOSE")) {
133 can_log[
LOG_VERBOSE] =
1;
134 }
else if(!strcmp(cfg->level,
"DEBUG")) {
135 can_log[
LOG_VERBOSE] =
1;
136 can_log[
LOG_DEBUG] =
1;
137 }
138
139 if(cfg->log_stdout) {
140
141 }
142 if(cfg->log_stderr) {
143
144 }
145
146
147 is_initialized =
1;
148
149
150 if(ui_buffer) {
151 size_t len = ui_buffer->length;
152 size_t r;
153 while(len >
0) {
154 r = write(log_file_fd, ui_buffer->ptr, ui_buffer->length);
155 len -= r;
156 }
157
158 sbuf_free(ui_buffer);
159 }
160
161 return 0;
162 }
163
164 void log_uninitialized_writeln(
char *str,
size_t len) {
165 if(ui_buffer ==
NULL) {
166 ui_buffer = sbuf_new(
1024);
167 if(ui_buffer ==
NULL) {
168 return;
169 }
170 }
171
172 cxstring s;
173 s.ptr = str;
174 s.length = len;
175
176 sbuf_append(ui_buffer, s);
177 sbuf_put(ui_buffer,
'\n');
178 }
179
180 void log_file_writeln(
char *str,
size_t len) {
181 struct iovec io[] = {
182 { str, len },
183 {
"\n",
1}
184 };
185
186 WSBool write_to_stdout = !main_is_daemon();
187 if(is_initialized) {
188 writev(log_file_fd, io,
2);
189 }
else {
190
191 log_uninitialized_writeln(str, len);
192 }
193
194 if(write_to_stdout) {
195 writev(
STDOUT_FILENO, io,
2);
196 }
197
198 if(log_dup_count >
0) {
199 char *msg = malloc(len +
1);
200 memcpy(msg, str, len);
201 msg[len] =
'\n';
202
203 pthread_mutex_lock(&mutex);
204 CxIterator i = cxListIterator(log_dup_list);
205 cx_foreach(LogDup *, dup, i) {
206 dup->write(dup->cookie, msg, len +
1);
207 }
208 pthread_mutex_unlock(&mutex);
209
210 free(msg);
211 }
212 }
213
214 cxmutstr log_get_prefix_str(
const char *level) {
215 time_t t = time(
NULL);
216
217 cxmutstr d;
218 d.ptr =
NULL;
219 d.length =
0;
220
221 struct tm date;
222 localtime_r(&t, &date);
223
224 char *buf = malloc(
64);
225 int len = snprintf(
226 buf,
227 64,
228 "[%02d/%s/%d:%02d:%02d:%02d] %s : ",
229 date.tm_mday,
230 log_date_month[date.tm_mon],
231 1900 + date.tm_year,
232 date.tm_hour,
233 date.tm_min,
234 date.tm_sec,
235 level);
236
237 if(len >
0) {
238 d.ptr = buf;
239 d.length = len;
240 }
241
242 return d;
243 }
244
245 cxmutstr log_get_prefix(
int level) {
246 return log_get_prefix_str(log_levels[level]);
247 }
248
249 void log_add_logdup(LogDup *dup) {
250 pthread_mutex_lock(&mutex);
251 cxListAdd(log_dup_list, dup);
252 ws_atomic_inc32(&log_dup_count);
253 pthread_mutex_unlock(&mutex);
254 }
255
256 void log_remove_logdup(LogDup *ldup) {
257 pthread_mutex_lock(&mutex);
258 CxMutIterator i = cxListMutIterator(log_dup_list);
259 WSBool finished =
0;
260 cx_foreach(LogDup *, dup, i) {
261 if(finished)
break;
262 if(dup == ldup) {
263 cxIteratorFlagRemoval(i);
264 finished =
1;
265 ws_atomic_dec32(&log_dup_count);
266 }
267 }
268 pthread_mutex_unlock(&mutex);
269 }
270
271
272
273
274
275
276 int log_ereport(
int degree,
const char *format, ...) {
277 va_list args;
278 va_start(args, format);
279 int ret = log_ereport_v(degree, format, args);
280 va_end(args);
281 return ret;
282 }
283
284 int log_ereport_v(
int degree,
const char *format, va_list args) {
285 if(degree <
0 || degree >
7) {
286 return 0;
287 }
288 if(!can_log[degree]) {
289 return 0;
290 }
291
292 cxmutstr lmsg;
293 lmsg.ptr =
NULL;
294
295
296 cxmutstr lpre = log_get_prefix(degree);
297
298
299 int len = vasprintf(&lmsg.ptr, format, args);
300 lmsg.length = len;
301
302
303 cxmutstr message = cx_strcat(
2, lpre, lmsg);
304
305
306 log_file_writeln(message.ptr, message.length);
307
308
309 free(lmsg.ptr);
310 free(lpre.ptr);
311 free(message.ptr);
312
313 return 0;
314 }
315
316 int log_error(
int degree,
const char *func, Session *sn, Request *rq,
317 const char *format, ...)
318 {
319 va_list args;
320 va_start(args, format);
321 int ret = log_error_v(degree, func, sn, rq, format, args);
322 va_end(args);
323 return ret;
324 }
325
326 int log_error_v(
int degree,
const char *func, Session *sn, Request *rq,
327 const char *format, va_list args)
328 {
329
330 return log_ereport(degree, format, args);
331 }
332
333 int log_message(
const char *degree,
const char *format, ...) {
334 va_list args;
335 va_start(args, format);
336 int ret = log_message_v(degree, format, args);
337 va_end(args);
338 return ret;
339 }
340
341 int log_message_v(
const char *degree,
const char *format, va_list args) {
342 cxmutstr lmsg;
343 lmsg.ptr =
NULL;
344
345 cxmutstr lpre = log_get_prefix_str(degree);
346
347
348 int len = vasprintf(&lmsg.ptr, format, args);
349 lmsg.length = len;
350
351
352 cxmutstr message = cx_strcat(
2, lpre, lmsg);
353
354
355 log_file_writeln(message.ptr, message.length);
356
357
358 free(lmsg.ptr);
359 free(lpre.ptr);
360 free(message.ptr);
361
362 return 0;
363 }
364
365
366 void ws_log_assert(
const char *file,
const char *func,
int line) {
367 log_ereport(
368 LOG_CATASTROPHE,
369 "assertion failed: %s: %s:%d",
370 file,
371 func,
372 line);
373 exit(-
1);
374 }
375
376
377
378
379
380
381
382 LogFile* get_access_log_file(cxstring file) {
383
384
385 if(!access_log_files) {
386 access_log_files = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
4);
387 }
388
389 if(file.ptr ==
NULL || file.length ==
0) {
390 return NULL;
391 }
392
393 CxHashKey key = cx_hash_key_bytes((
unsigned const char*)file.ptr, file.length);
394 LogFile *log = cxMapGet(access_log_files, key);
395 if(log !=
NULL) {
396 ws_atomic_inc32(&log->ref);
397 return log;
398 }
399
400
401
402 FILE *out = fopen(file.ptr,
"a");
403 if(out ==
NULL) {
404 return NULL;
405 }
406
407
408 log = calloc(
1,
sizeof(LogFile));
409 log->file = out;
410 log->ref =
1;
411
412
413 cxMapPut(access_log_files, key, log);
414
415 return log;
416 }
417
418