1 /*
2 * Copyright 2008-2014 Arsen Chaloyan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * $Id: apt_log.c 2198 2014-10-16 01:41:19Z achaloyan@gmail.com $
17 */
18
19 #include <apr_time.h>
20 #include <apr_file_io.h>
21 #include <apr_portable.h>
22 #include <apr_xml.h>
23 #include "apt_log.h"
24
25 #define MAX_LOG_ENTRY_SIZE 4096
26 #define MAX_PRIORITY_NAME_LENGTH 9
27
28 static const char priority_snames[APT_PRIO_COUNT][MAX_PRIORITY_NAME_LENGTH+1] =
29 {
30 "[EMERG] ",
31 "[ALERT] ",
32 "[CRITIC] ",
33 "[ERROR] ",
34 "[WARN] ",
35 "[NOTICE] ",
36 "[INFO] ",
37 "[DEBUG] "
38 };
39
40 typedef struct apt_log_file_data_t apt_log_file_data_t;
41
42 struct apt_log_file_data_t {
43 const char *log_dir_path;
44 const char *log_file_name;
45 FILE *file;
46 apr_size_t cur_size;
47 apr_size_t max_size;
48 apr_size_t cur_file_index;
49 apr_size_t max_file_count;
50 apt_bool_t append;
51 apr_thread_mutex_t *mutex;
52 apr_pool_t *pool;
53 };
54
55 struct apt_logger_t {
56 apt_log_output_e mode;
57 apt_log_priority_e priority;
58 int header;
59 apt_log_ext_handler_f ext_handler;
60 apt_log_file_data_t *file_data;
61 apt_log_masking_e masking;
62 };
63
64 static apt_logger_t *apt_logger = NULL;
65
66 static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr);
67
68 static const char* apt_log_file_path_make(apt_log_file_data_t *file_data);
69 static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size);
70 static apr_xml_doc* apt_log_doc_parse(const char *file_path, apr_pool_t *pool);
71 static apr_size_t apt_log_file_get_size(apt_log_file_data_t *file_data);
72 static apr_byte_t apt_log_file_exist(apt_log_file_data_t *file_data);
73
apt_log_instance_alloc(apr_pool_t * pool)74 static apt_logger_t* apt_log_instance_alloc(apr_pool_t *pool)
75 {
76 apt_logger_t *logger = apr_palloc(pool,sizeof(apt_logger_t));
77 logger->mode = APT_LOG_OUTPUT_CONSOLE;
78 logger->priority = APT_PRIO_INFO;
79 logger->header = APT_LOG_HEADER_DEFAULT;
80 logger->ext_handler = NULL;
81 logger->file_data = NULL;
82 logger->masking = APT_LOG_MASKING_NONE;
83 return logger;
84 }
85
apt_log_instance_create(apt_log_output_e mode,apt_log_priority_e priority,apr_pool_t * pool)86 APT_DECLARE(apt_bool_t) apt_log_instance_create(apt_log_output_e mode, apt_log_priority_e priority, apr_pool_t *pool)
87 {
88 if(apt_logger) {
89 return FALSE;
90 }
91 apt_logger = apt_log_instance_alloc(pool);
92 apt_logger->mode = mode;
93 apt_logger->priority = priority;
94 return TRUE;
95 }
96
apt_log_instance_load(const char * config_file,apr_pool_t * pool)97 APT_DECLARE(apt_bool_t) apt_log_instance_load(const char *config_file, apr_pool_t *pool)
98 {
99 apr_xml_doc *doc;
100 const apr_xml_elem *elem;
101 const apr_xml_elem *root;
102 char *text;
103
104 if(apt_logger) {
105 return FALSE;
106 }
107 apt_logger = apt_log_instance_alloc(pool);
108
109 /* Parse XML document */
110 doc = apt_log_doc_parse(config_file,pool);
111 if(!doc) {
112 return FALSE;
113 }
114
115 root = doc->root;
116
117 /* Match document name */
118 if(!root || strcasecmp(root->name,"aptlogger") != 0) {
119 /* Unknown document */
120 return FALSE;
121 }
122
123 /* Navigate through document */
124 for(elem = root->first_child; elem; elem = elem->next) {
125 if(!elem->first_cdata.first || !elem->first_cdata.first->text)
126 continue;
127
128 text = apr_pstrdup(pool,elem->first_cdata.first->text);
129 apr_collapse_spaces(text,text);
130
131 if(strcasecmp(elem->name,"priority") == 0) {
132 apt_logger->priority = apt_log_priority_translate(text);
133 }
134 else if(strcasecmp(elem->name,"output") == 0) {
135 apt_logger->mode = apt_log_output_mode_translate(text);
136 }
137 else if(strcasecmp(elem->name,"headers") == 0) {
138 apt_logger->header = apt_log_header_translate(text);
139 }
140 else if(strcasecmp(elem->name,"masking") == 0) {
141 apt_logger->masking = apt_log_masking_translate(text);
142 }
143 else {
144 /* Unknown element */
145 }
146 }
147 return TRUE;
148 }
149
apt_log_instance_destroy()150 APT_DECLARE(apt_bool_t) apt_log_instance_destroy()
151 {
152 if(!apt_logger) {
153 return FALSE;
154 }
155
156 if(apt_logger->file_data) {
157 apt_log_file_close();
158 }
159 apt_logger = NULL;
160 return TRUE;
161 }
162
apt_log_instance_get()163 APT_DECLARE(apt_logger_t*) apt_log_instance_get()
164 {
165 return apt_logger;
166 }
167
apt_log_instance_set(apt_logger_t * logger)168 APT_DECLARE(apt_bool_t) apt_log_instance_set(apt_logger_t *logger)
169 {
170 if(apt_logger){
171 return FALSE;
172 }
173 apt_logger = logger;
174 return TRUE;
175 }
176
apt_log_file_open(const char * dir_path,const char * file_name,apr_size_t max_file_size,apr_size_t max_file_count,apt_bool_t append,apr_pool_t * pool)177 APT_DECLARE(apt_bool_t) apt_log_file_open(
178 const char *dir_path,
179 const char *file_name,
180 apr_size_t max_file_size,
181 apr_size_t max_file_count,
182 apt_bool_t append,
183 apr_pool_t *pool)
184 {
185 const char *log_file_path;
186 apt_log_file_data_t *file_data;
187 if(!apt_logger || !dir_path || !file_name) {
188 return FALSE;
189 }
190
191 if(apt_logger->file_data) {
192 return FALSE;
193 }
194
195 file_data = apr_palloc(pool,sizeof(apt_log_file_data_t));
196 file_data->log_dir_path = apr_pstrdup(pool,dir_path);
197 file_data->log_file_name = apr_pstrdup(pool,file_name);
198 file_data->cur_file_index = 0;
199 file_data->cur_size = 0;
200 file_data->max_file_count = max_file_count;
201 file_data->max_size = max_file_size;
202 file_data->append = append;
203 file_data->mutex = NULL;
204 file_data->pool = pool;
205
206 if(!file_data->max_size) {
207 file_data->max_size = MAX_LOG_FILE_SIZE;
208 }
209 if(!file_data->max_file_count) {
210 file_data->max_file_count = MAX_LOG_FILE_COUNT;
211 }
212
213 if(file_data->append == TRUE) {
214 /* iteratively find the last created file */
215 while(file_data->cur_file_index<file_data->max_file_count)
216 {
217 if(apt_log_file_exist(file_data) == 0)
218 {
219 if(file_data->cur_file_index > 0)
220 file_data->cur_file_index--;
221 file_data->cur_size = apt_log_file_get_size(file_data);
222 break;
223 }
224 file_data->cur_file_index++;
225 }
226
227 /* if all the files have been created start rewriting from beginning */
228 if(file_data->cur_file_index>=file_data->max_file_count)
229 {
230 file_data->cur_file_index=0;
231 file_data->cur_size=0;
232 log_file_path = apt_log_file_path_make(file_data);
233 file_data->file = fopen(log_file_path,"wb"); /* truncate the first file to zero length */
234 fclose(file_data->file);
235 }
236 }
237
238 /* create mutex */
239 if(apr_thread_mutex_create(&file_data->mutex,APR_THREAD_MUTEX_DEFAULT,pool) != APR_SUCCESS) {
240 return FALSE;
241 }
242 /* open log file */
243 log_file_path = apt_log_file_path_make(file_data);
244 file_data->file = fopen(log_file_path,file_data->append == TRUE ? "ab" : "wb");
245 if(!file_data->file) {
246 apr_thread_mutex_destroy(file_data->mutex);
247 return FALSE;
248 }
249
250 apt_logger->file_data = file_data;
251 return TRUE;
252 }
253
apt_log_file_close()254 APT_DECLARE(apt_bool_t) apt_log_file_close()
255 {
256 apt_log_file_data_t *file_data;
257 if(!apt_logger || !apt_logger->file_data) {
258 return FALSE;
259 }
260 file_data = apt_logger->file_data;
261 if(file_data->file) {
262 /* close log file */
263 fclose(file_data->file);
264 file_data->file = NULL;
265 /* destroy mutex */
266 apr_thread_mutex_destroy(file_data->mutex);
267 file_data->mutex = NULL;
268 }
269 apt_logger->file_data = NULL;
270 return TRUE;
271 }
272
apt_log_output_mode_set(apt_log_output_e mode)273 APT_DECLARE(apt_bool_t) apt_log_output_mode_set(apt_log_output_e mode)
274 {
275 if(!apt_logger) {
276 return FALSE;
277 }
278 apt_logger->mode = mode;
279 return TRUE;
280 }
281
apt_log_output_mode_check(apt_log_output_e mode)282 APT_DECLARE(apt_bool_t) apt_log_output_mode_check(apt_log_output_e mode)
283 {
284 if(!apt_logger) {
285 return FALSE;
286 }
287 return (apt_logger->mode & mode) ? TRUE : FALSE;
288 }
289
apt_log_output_mode_translate(char * str)290 APT_DECLARE(int) apt_log_output_mode_translate(char *str)
291 {
292 int mode = APT_LOG_OUTPUT_NONE;
293 char *name;
294 char *last;
295 name = apr_strtok(str, ",", &last);
296 while(name) {
297 if(strcasecmp(name, "CONSOLE") == 0)
298 mode |= APT_LOG_OUTPUT_CONSOLE;
299 else if(strcasecmp(name, "FILE") == 0)
300 mode |= APT_LOG_OUTPUT_FILE;
301
302 name = apr_strtok(NULL, ",", &last);
303 }
304 return mode;
305 }
306
apt_log_priority_set(apt_log_priority_e priority)307 APT_DECLARE(apt_bool_t) apt_log_priority_set(apt_log_priority_e priority)
308 {
309 if(!apt_logger || priority >= APT_PRIO_COUNT) {
310 return FALSE;
311 }
312 apt_logger->priority = priority;
313 return TRUE;
314 }
315
apt_log_priority_translate(const char * str)316 APT_DECLARE(apt_log_priority_e) apt_log_priority_translate(const char *str)
317 {
318 if(strcasecmp(str, "EMERGENCY") == 0)
319 return APT_PRIO_EMERGENCY;
320 else if(strcasecmp(str, "ALERT") == 0)
321 return APT_PRIO_ALERT;
322 else if(strcasecmp(str, "CRITICAL") == 0)
323 return APT_PRIO_CRITICAL;
324 else if(strcasecmp(str, "ERROR") == 0)
325 return APT_PRIO_ERROR;
326 else if(strcasecmp(str, "WARNING") == 0)
327 return APT_PRIO_WARNING;
328 else if(strcasecmp(str, "NOTICE") == 0)
329 return APT_PRIO_NOTICE;
330 else if(strcasecmp(str, "INFO") == 0)
331 return APT_PRIO_INFO;
332 else if(strcasecmp(str, "DEBUG") == 0)
333 return APT_PRIO_DEBUG;
334
335 return APT_PRIO_DEBUG;
336 }
337
apt_log_header_set(int header)338 APT_DECLARE(apt_bool_t) apt_log_header_set(int header)
339 {
340 if(!apt_logger) {
341 return FALSE;
342 }
343 apt_logger->header = header;
344 return TRUE;
345 }
346
apt_log_header_translate(char * str)347 APT_DECLARE(int) apt_log_header_translate(char *str)
348 {
349 int header = APT_LOG_OUTPUT_NONE;
350 char *name;
351 char *last;
352 name = apr_strtok(str, ",", &last);
353 while(name) {
354 if(strcasecmp(name, "DATE") == 0)
355 header |= APT_LOG_HEADER_DATE;
356 else if(strcasecmp(name, "TIME") == 0)
357 header |= APT_LOG_HEADER_TIME;
358 else if(strcasecmp(name, "PRIORITY") == 0)
359 header |= APT_LOG_HEADER_PRIORITY;
360 else if(strcasecmp(name, "MARK") == 0)
361 header |= APT_LOG_HEADER_MARK;
362 else if(strcasecmp(name, "THREAD") == 0)
363 header |= APT_LOG_HEADER_THREAD;
364
365 name = apr_strtok(NULL, ",", &last);
366 }
367 return header;
368 }
369
apt_log_masking_set(apt_log_masking_e masking)370 APT_DECLARE(apt_bool_t) apt_log_masking_set(apt_log_masking_e masking)
371 {
372 if(!apt_logger) {
373 return FALSE;
374 }
375 apt_logger->masking = masking;
376 return TRUE;
377 }
378
apt_log_masking_get(void)379 APT_DECLARE(apt_log_masking_e) apt_log_masking_get(void)
380 {
381 if(!apt_logger) {
382 return APT_LOG_MASKING_NONE;
383 }
384 return apt_logger->masking;
385 }
386
apt_log_masking_translate(const char * str)387 APT_DECLARE(apt_log_masking_e) apt_log_masking_translate(const char *str)
388 {
389 if(strcasecmp(str, "COMPLETE") == 0)
390 return APT_LOG_MASKING_COMPLETE;
391 else if(strcasecmp(str, "ENCRYPTED") == 0)
392 return APT_LOG_MASKING_ENCRYPTED;
393 return APT_LOG_MASKING_NONE;
394 }
395
396 #define APT_MASKED_CONTENT "*** masked ***"
397
apt_log_data_mask(const char * data_in,apr_size_t * length,apr_pool_t * pool)398 APT_DECLARE(const char*) apt_log_data_mask(const char *data_in, apr_size_t *length, apr_pool_t *pool)
399 {
400 if(!apt_logger) {
401 return NULL;
402 }
403 if(apt_logger->masking == APT_LOG_MASKING_COMPLETE) {
404 *length = sizeof(APT_MASKED_CONTENT) - 1;
405 return APT_MASKED_CONTENT;
406 }
407 return data_in;
408 }
409
apt_log_ext_handler_set(apt_log_ext_handler_f handler)410 APT_DECLARE(apt_bool_t) apt_log_ext_handler_set(apt_log_ext_handler_f handler)
411 {
412 if(!apt_logger) {
413 return FALSE;
414 }
415 apt_logger->ext_handler = handler;
416 return TRUE;
417 }
418
apt_log(const char * file,int line,apt_log_priority_e priority,const char * format,...)419 APT_DECLARE(apt_bool_t) apt_log(const char *file, int line, apt_log_priority_e priority, const char *format, ...)
420 {
421 apt_bool_t status = TRUE;
422 if(!apt_logger) {
423 return FALSE;
424 }
425 if(priority <= apt_logger->priority) {
426 va_list arg_ptr;
427 va_start(arg_ptr, format);
428 if(apt_logger->ext_handler) {
429 status = apt_logger->ext_handler(file,line,NULL,priority,format,arg_ptr);
430 }
431 else {
432 status = apt_do_log(file,line,priority,format,arg_ptr);
433 }
434 va_end(arg_ptr);
435 }
436 return status;
437 }
438
apt_obj_log(const char * file,int line,apt_log_priority_e priority,void * obj,const char * format,...)439 APT_DECLARE(apt_bool_t) apt_obj_log(const char *file, int line, apt_log_priority_e priority, void *obj, const char *format, ...)
440 {
441 apt_bool_t status = TRUE;
442 if(!apt_logger) {
443 return FALSE;
444 }
445 if(priority <= apt_logger->priority) {
446 va_list arg_ptr;
447 va_start(arg_ptr, format);
448 if(apt_logger->ext_handler) {
449 status = apt_logger->ext_handler(file,line,obj,priority,format,arg_ptr);
450 }
451 else {
452 status = apt_do_log(file,line,priority,format,arg_ptr);
453 }
454 va_end(arg_ptr);
455 }
456 return status;
457 }
458
apt_va_log(const char * file,int line,apt_log_priority_e priority,const char * format,va_list arg_ptr)459 APT_DECLARE(apt_bool_t) apt_va_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr)
460 {
461 apt_bool_t status = TRUE;
462 if(!apt_logger) {
463 return FALSE;
464 }
465 if(priority <= apt_logger->priority) {
466 if(apt_logger->ext_handler) {
467 status = apt_logger->ext_handler(file,line,NULL,priority,format,arg_ptr);
468 }
469 else {
470 status = apt_do_log(file,line,priority,format,arg_ptr);
471 }
472 }
473 return status;
474 }
475
apt_thread_id_get(void)476 static APR_INLINE unsigned long apt_thread_id_get(void)
477 {
478 #ifdef WIN32
479 return (unsigned long) GetCurrentThreadId();
480 #else
481 return (unsigned long) apr_os_thread_current();
482 #endif
483 }
484
apt_do_log(const char * file,int line,apt_log_priority_e priority,const char * format,va_list arg_ptr)485 static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr)
486 {
487 char log_entry[MAX_LOG_ENTRY_SIZE];
488 apr_size_t max_size = MAX_LOG_ENTRY_SIZE - 2;
489 apr_size_t offset = 0;
490 apr_time_exp_t result;
491 apr_time_t now = apr_time_now();
492 apr_time_exp_lt(&result,now);
493
494 if(apt_logger->header & APT_LOG_HEADER_DATE) {
495 offset += apr_snprintf(log_entry+offset,max_size-offset,"%4d-%02d-%02d ",
496 result.tm_year+1900,
497 result.tm_mon+1,
498 result.tm_mday);
499 }
500 if(apt_logger->header & APT_LOG_HEADER_TIME) {
501 offset += apr_snprintf(log_entry+offset,max_size-offset,"%02d:%02d:%02d:%06d ",
502 result.tm_hour,
503 result.tm_min,
504 result.tm_sec,
505 result.tm_usec);
506 }
507 if(apt_logger->header & APT_LOG_HEADER_MARK) {
508 offset += apr_snprintf(log_entry+offset,max_size-offset,"%s:%03d ",file,line);
509 }
510 if(apt_logger->header & APT_LOG_HEADER_THREAD) {
511 offset += apr_snprintf(log_entry+offset,max_size-offset,"%05lu ",apt_thread_id_get());
512 }
513 if(apt_logger->header & APT_LOG_HEADER_PRIORITY) {
514 memcpy(log_entry+offset,priority_snames[priority],MAX_PRIORITY_NAME_LENGTH);
515 offset += MAX_PRIORITY_NAME_LENGTH;
516 }
517
518 offset += apr_vsnprintf(log_entry+offset,max_size-offset,format,arg_ptr);
519 log_entry[offset++] = '\n';
520 log_entry[offset] = '\0';
521 if((apt_logger->mode & APT_LOG_OUTPUT_CONSOLE) == APT_LOG_OUTPUT_CONSOLE) {
522 fwrite(log_entry,offset,1,stdout);
523 }
524
525 if((apt_logger->mode & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE && apt_logger->file_data) {
526 apt_log_file_dump(apt_logger->file_data,log_entry,offset);
527 }
528 return TRUE;
529 }
530
apt_log_file_path_make(apt_log_file_data_t * file_data)531 static const char* apt_log_file_path_make(apt_log_file_data_t *file_data)
532 {
533 char *log_file_path = NULL;
534 const char *log_file_name = apr_psprintf(file_data->pool,"%s-%.2"APR_SIZE_T_FMT".log",
535 file_data->log_file_name,
536 file_data->cur_file_index);
537 apr_filepath_merge(&log_file_path,
538 file_data->log_dir_path,
539 log_file_name,
540 APR_FILEPATH_NATIVE,
541 file_data->pool);
542 return log_file_path;
543 }
544
apt_log_file_get_size(apt_log_file_data_t * file_data)545 static apr_size_t apt_log_file_get_size(apt_log_file_data_t *file_data)
546 {
547 FILE* fp;
548 const char *log_file_path;
549 apr_size_t ret;
550
551 log_file_path = apt_log_file_path_make(file_data);
552 fp = fopen(log_file_path,"rb");
553
554 if(!fp) return 0;
555
556 fseek(fp,0,SEEK_END);
557 ret = ftell(fp);
558
559 fclose(fp);
560
561 return ret;
562 }
563
apt_log_file_exist(apt_log_file_data_t * file_data)564 static apr_byte_t apt_log_file_exist(apt_log_file_data_t *file_data)
565 {
566 FILE* fp;
567 const char *log_file_path;
568
569 log_file_path = apt_log_file_path_make(file_data);
570 fp = fopen(log_file_path,"rb");
571
572 if(!fp) return 0;
573
574 fclose(fp);
575
576 return 1;
577 }
578
apt_log_file_dump(apt_log_file_data_t * file_data,const char * log_entry,apr_size_t size)579 static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size)
580 {
581 apr_thread_mutex_lock(file_data->mutex);
582
583 file_data->cur_size += size;
584 if(file_data->cur_size > file_data->max_size) {
585 const char *log_file_path;
586 /* close current log file */
587 fclose(file_data->file);
588 /* roll over the next log file */
589 file_data->cur_file_index++;
590 file_data->cur_file_index %= file_data->max_file_count;
591 /* open log file */
592 log_file_path = apt_log_file_path_make(file_data);
593 file_data->file = fopen(log_file_path,"wb");
594 if(!file_data->file) {
595 return FALSE;
596 }
597
598 file_data->cur_size = size;
599 }
600 /* write to log file */
601 fwrite(log_entry,1,size,file_data->file);
602 fflush(file_data->file);
603
604 apr_thread_mutex_unlock(file_data->mutex);
605 return TRUE;
606 }
607
apt_log_doc_parse(const char * file_path,apr_pool_t * pool)608 static apr_xml_doc* apt_log_doc_parse(const char *file_path, apr_pool_t *pool)
609 {
610 apr_xml_parser *parser = NULL;
611 apr_xml_doc *xml_doc = NULL;
612 apr_file_t *fd = NULL;
613 apr_status_t rv;
614
615 rv = apr_file_open(&fd,file_path,APR_READ|APR_BINARY,0,pool);
616 if(rv != APR_SUCCESS) {
617 return NULL;
618 }
619
620 rv = apr_xml_parse_file(pool,&parser,&xml_doc,fd,2000);
621 if(rv != APR_SUCCESS) {
622 xml_doc = NULL;
623 }
624
625 apr_file_close(fd);
626 return xml_doc;
627 }
628