1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup clog
19 */
20
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 /* Disable for small single threaded programs
28 * to avoid having to link with pthreads. */
29 #ifdef WITH_CLOG_PTHREADS
30 # include "atomic_ops.h"
31 # include <pthread.h>
32 #endif
33
34 /* For 'isatty' to check for color. */
35 #if defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
36 # include <sys/time.h>
37 # include <unistd.h>
38 #endif
39
40 #if defined(_MSC_VER)
41 # include <io.h>
42 # include <windows.h>
43 #endif
44
45 /* For printing timestamp. */
46 #define __STDC_FORMAT_MACROS
47 #include <inttypes.h>
48
49 /* Only other dependency (could use regular malloc too). */
50 #include "MEM_guardedalloc.h"
51
52 /* own include. */
53 #include "CLG_log.h"
54
55 /* Local utility defines */
56 #define STREQ(a, b) (strcmp(a, b) == 0)
57 #define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
58
59 #ifdef _WIN32
60 # define PATHSEP_CHAR '\\'
61 #else
62 # define PATHSEP_CHAR '/'
63 #endif
64
65 /* -------------------------------------------------------------------- */
66 /** \name Internal Types
67 * \{ */
68
69 typedef struct CLG_IDFilter {
70 struct CLG_IDFilter *next;
71 /** Over alloc. */
72 char match[0];
73 } CLG_IDFilter;
74
75 typedef struct CLogContext {
76 /** Single linked list of types. */
77 CLG_LogType *types;
78 #ifdef WITH_CLOG_PTHREADS
79 pthread_mutex_t types_lock;
80 #endif
81
82 /* exclude, include filters. */
83 CLG_IDFilter *filters[2];
84 bool use_color;
85 bool use_basename;
86 bool use_timestamp;
87
88 /** Borrowed, not owned. */
89 int output;
90 FILE *output_file;
91
92 /** For timer (use_timestamp). */
93 uint64_t timestamp_tick_start;
94
95 /** For new types. */
96 struct {
97 int level;
98 } default_type;
99
100 struct {
101 void (*error_fn)(void *file_handle);
102 void (*fatal_fn)(void *file_handle);
103 void (*backtrace_fn)(void *file_handle);
104 } callbacks;
105 } CLogContext;
106
107 /** \} */
108
109 /* -------------------------------------------------------------------- */
110 /** \name Mini Buffer Functionality
111 *
112 * Use so we can do a single call to write.
113 * \{ */
114
115 #define CLOG_BUF_LEN_INIT 512
116
117 typedef struct CLogStringBuf {
118 char *data;
119 uint len;
120 uint len_alloc;
121 bool is_alloc;
122 } CLogStringBuf;
123
clg_str_init(CLogStringBuf * cstr,char * buf_stack,uint buf_stack_len)124 static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
125 {
126 cstr->data = buf_stack;
127 cstr->len_alloc = buf_stack_len;
128 cstr->len = 0;
129 cstr->is_alloc = false;
130 }
131
clg_str_free(CLogStringBuf * cstr)132 static void clg_str_free(CLogStringBuf *cstr)
133 {
134 if (cstr->is_alloc) {
135 MEM_freeN(cstr->data);
136 }
137 }
138
clg_str_reserve(CLogStringBuf * cstr,const uint len)139 static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
140 {
141 if (len > cstr->len_alloc) {
142 cstr->len_alloc *= 2;
143 if (len > cstr->len_alloc) {
144 cstr->len_alloc = len;
145 }
146
147 if (cstr->is_alloc) {
148 cstr->data = MEM_reallocN(cstr->data, cstr->len_alloc);
149 }
150 else {
151 /* Copy the static buffer. */
152 char *data = MEM_mallocN(cstr->len_alloc, __func__);
153 memcpy(data, cstr->data, cstr->len);
154 cstr->data = data;
155 cstr->is_alloc = true;
156 }
157 }
158 }
159
clg_str_append_with_len(CLogStringBuf * cstr,const char * str,const uint len)160 static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
161 {
162 uint len_next = cstr->len + len;
163 clg_str_reserve(cstr, len_next);
164 char *str_dst = cstr->data + cstr->len;
165 memcpy(str_dst, str, len);
166 #if 0 /* no need. */
167 str_dst[len] = '\0';
168 #endif
169 cstr->len = len_next;
170 }
171
clg_str_append(CLogStringBuf * cstr,const char * str)172 static void clg_str_append(CLogStringBuf *cstr, const char *str)
173 {
174 clg_str_append_with_len(cstr, str, strlen(str));
175 }
176
177 ATTR_PRINTF_FORMAT(2, 0)
clg_str_vappendf(CLogStringBuf * cstr,const char * fmt,va_list args)178 static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
179 {
180 /* Use limit because windows may use '-1' for a formatting error. */
181 const uint len_max = 65535;
182 while (true) {
183 uint len_avail = cstr->len_alloc - cstr->len;
184
185 va_list args_cpy;
186 va_copy(args_cpy, args);
187 int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
188 va_end(args_cpy);
189
190 if (retval < 0) {
191 /* Some encoding error happened, not much we can do here, besides skipping/canceling this
192 * message. */
193 break;
194 }
195 else if ((uint)retval <= len_avail) {
196 /* Copy was successful. */
197 cstr->len += (uint)retval;
198 break;
199 }
200 else {
201 /* vsnprintf was not successful, due to lack of allocated space, retval contains expected
202 * length of the formatted string, use it to allocate required amount of memory. */
203 uint len_alloc = cstr->len + (uint)retval;
204 if (len_alloc >= len_max) {
205 /* Safe upper-limit, just in case... */
206 break;
207 }
208 clg_str_reserve(cstr, len_alloc);
209 len_avail = cstr->len_alloc - cstr->len;
210 }
211 }
212 }
213
214 /** \} */
215
216 /* -------------------------------------------------------------------- */
217 /** \name Internal Utilities
218 * \{ */
219
220 enum eCLogColor {
221 COLOR_DEFAULT,
222 COLOR_RED,
223 COLOR_GREEN,
224 COLOR_YELLOW,
225
226 COLOR_RESET,
227 };
228 #define COLOR_LEN (COLOR_RESET + 1)
229
230 static const char *clg_color_table[COLOR_LEN] = {NULL};
231
clg_color_table_init(bool use_color)232 static void clg_color_table_init(bool use_color)
233 {
234 for (int i = 0; i < COLOR_LEN; i++) {
235 clg_color_table[i] = "";
236 }
237 if (use_color) {
238 clg_color_table[COLOR_DEFAULT] = "\033[1;37m";
239 clg_color_table[COLOR_RED] = "\033[1;31m";
240 clg_color_table[COLOR_GREEN] = "\033[1;32m";
241 clg_color_table[COLOR_YELLOW] = "\033[1;33m";
242 clg_color_table[COLOR_RESET] = "\033[0m";
243 }
244 }
245
246 static const char *clg_severity_str[CLG_SEVERITY_LEN] = {
247 [CLG_SEVERITY_INFO] = "INFO",
248 [CLG_SEVERITY_WARN] = "WARN",
249 [CLG_SEVERITY_ERROR] = "ERROR",
250 [CLG_SEVERITY_FATAL] = "FATAL",
251 };
252
clg_severity_as_text(enum CLG_Severity severity)253 static const char *clg_severity_as_text(enum CLG_Severity severity)
254 {
255 bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
256 assert(ok);
257 if (ok) {
258 return clg_severity_str[severity];
259 }
260 else {
261 return "INVALID_SEVERITY";
262 }
263 }
264
clg_severity_to_color(enum CLG_Severity severity)265 static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
266 {
267 assert((unsigned int)severity < CLG_SEVERITY_LEN);
268 enum eCLogColor color = COLOR_DEFAULT;
269 switch (severity) {
270 case CLG_SEVERITY_INFO:
271 color = COLOR_DEFAULT;
272 break;
273 case CLG_SEVERITY_WARN:
274 color = COLOR_YELLOW;
275 break;
276 case CLG_SEVERITY_ERROR:
277 case CLG_SEVERITY_FATAL:
278 color = COLOR_RED;
279 break;
280 default:
281 /* should never get here. */
282 assert(false);
283 }
284 return color;
285 }
286
287 /** \} */
288
289 /* -------------------------------------------------------------------- */
290 /** \name Context Type Access
291 * \{ */
292
293 /**
294 * Filter the identifier based on very basic globbing.
295 * - `foo` exact match of `foo`.
296 * - `foo.bar` exact match for `foo.bar`
297 * - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
298 * - `*` matches everything.
299 */
clg_ctx_filter_check(CLogContext * ctx,const char * identifier)300 static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
301 {
302 const int identifier_len = strlen(identifier);
303 for (uint i = 0; i < 2; i++) {
304 const CLG_IDFilter *flt = ctx->filters[i];
305 while (flt != NULL) {
306 const int len = strlen(flt->match);
307 if (STREQ(flt->match, "*") || ((len == identifier_len) && (STREQ(identifier, flt->match)))) {
308 return (bool)i;
309 }
310 if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
311 if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
312 ((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1))) {
313 return (bool)i;
314 }
315 }
316 flt = flt->next;
317 }
318 }
319 return false;
320 }
321
322 /**
323 * \note This should never be called per logging call.
324 * Searching is only to get an initial handle.
325 */
clg_ctx_type_find_by_name(CLogContext * ctx,const char * identifier)326 static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
327 {
328 for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
329 if (STREQ(identifier, ty->identifier)) {
330 return ty;
331 }
332 }
333 return NULL;
334 }
335
clg_ctx_type_register(CLogContext * ctx,const char * identifier)336 static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
337 {
338 assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);
339 CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);
340 ty->next = ctx->types;
341 ctx->types = ty;
342 strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
343 ty->ctx = ctx;
344 ty->level = ctx->default_type.level;
345
346 if (clg_ctx_filter_check(ctx, ty->identifier)) {
347 ty->flag |= CLG_FLAG_USE;
348 }
349 return ty;
350 }
351
clg_ctx_error_action(CLogContext * ctx)352 static void clg_ctx_error_action(CLogContext *ctx)
353 {
354 if (ctx->callbacks.error_fn != NULL) {
355 ctx->callbacks.error_fn(ctx->output_file);
356 }
357 }
358
clg_ctx_fatal_action(CLogContext * ctx)359 static void clg_ctx_fatal_action(CLogContext *ctx)
360 {
361 if (ctx->callbacks.fatal_fn != NULL) {
362 ctx->callbacks.fatal_fn(ctx->output_file);
363 }
364 fflush(ctx->output_file);
365 abort();
366 }
367
clg_ctx_backtrace(CLogContext * ctx)368 static void clg_ctx_backtrace(CLogContext *ctx)
369 {
370 /* Note: we avoid writing to 'FILE', for back-trace we make an exception,
371 * if necessary we could have a version of the callback that writes to file
372 * descriptor all at once. */
373 ctx->callbacks.backtrace_fn(ctx->output_file);
374 fflush(ctx->output_file);
375 }
376
clg_timestamp_ticks_get(void)377 static uint64_t clg_timestamp_ticks_get(void)
378 {
379 uint64_t tick;
380 #if defined(_MSC_VER)
381 tick = GetTickCount64();
382 #else
383 struct timeval tv;
384 gettimeofday(&tv, NULL);
385 tick = tv.tv_sec * 1000 + tv.tv_usec / 1000;
386 #endif
387 return tick;
388 }
389
390 /** \} */
391
392 /* -------------------------------------------------------------------- */
393 /** \name Logging API
394 * \{ */
395
write_timestamp(CLogStringBuf * cstr,const uint64_t timestamp_tick_start)396 static void write_timestamp(CLogStringBuf *cstr, const uint64_t timestamp_tick_start)
397 {
398 char timestamp_str[64];
399 const uint64_t timestamp = clg_timestamp_ticks_get() - timestamp_tick_start;
400 const uint timestamp_len = snprintf(timestamp_str,
401 sizeof(timestamp_str),
402 "%" PRIu64 ".%03u ",
403 timestamp / 1000,
404 (uint)(timestamp % 1000));
405 clg_str_append_with_len(cstr, timestamp_str, timestamp_len);
406 }
407
write_severity(CLogStringBuf * cstr,enum CLG_Severity severity,bool use_color)408 static void write_severity(CLogStringBuf *cstr, enum CLG_Severity severity, bool use_color)
409 {
410 assert((unsigned int)severity < CLG_SEVERITY_LEN);
411 if (use_color) {
412 enum eCLogColor color = clg_severity_to_color(severity);
413 clg_str_append(cstr, clg_color_table[color]);
414 clg_str_append(cstr, clg_severity_as_text(severity));
415 clg_str_append(cstr, clg_color_table[COLOR_RESET]);
416 }
417 else {
418 clg_str_append(cstr, clg_severity_as_text(severity));
419 }
420 }
421
write_type(CLogStringBuf * cstr,CLG_LogType * lg)422 static void write_type(CLogStringBuf *cstr, CLG_LogType *lg)
423 {
424 clg_str_append(cstr, " (");
425 clg_str_append(cstr, lg->identifier);
426 clg_str_append(cstr, "): ");
427 }
428
write_file_line_fn(CLogStringBuf * cstr,const char * file_line,const char * fn,const bool use_basename)429 static void write_file_line_fn(CLogStringBuf *cstr,
430 const char *file_line,
431 const char *fn,
432 const bool use_basename)
433 {
434 uint file_line_len = strlen(file_line);
435 if (use_basename) {
436 uint file_line_offset = file_line_len;
437 while (file_line_offset-- > 0) {
438 if (file_line[file_line_offset] == PATHSEP_CHAR) {
439 file_line_offset++;
440 break;
441 }
442 }
443 file_line += file_line_offset;
444 file_line_len -= file_line_offset;
445 }
446 clg_str_append_with_len(cstr, file_line, file_line_len);
447
448 clg_str_append(cstr, " ");
449 clg_str_append(cstr, fn);
450 clg_str_append(cstr, ": ");
451 }
452
CLG_log_str(CLG_LogType * lg,enum CLG_Severity severity,const char * file_line,const char * fn,const char * message)453 void CLG_log_str(CLG_LogType *lg,
454 enum CLG_Severity severity,
455 const char *file_line,
456 const char *fn,
457 const char *message)
458 {
459 CLogStringBuf cstr;
460 char cstr_stack_buf[CLOG_BUF_LEN_INIT];
461 clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
462
463 if (lg->ctx->use_timestamp) {
464 write_timestamp(&cstr, lg->ctx->timestamp_tick_start);
465 }
466
467 write_severity(&cstr, severity, lg->ctx->use_color);
468 write_type(&cstr, lg);
469
470 {
471 write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
472 clg_str_append(&cstr, message);
473 }
474 clg_str_append(&cstr, "\n");
475
476 /* could be optional */
477 int bytes_written = write(lg->ctx->output, cstr.data, cstr.len);
478 (void)bytes_written;
479
480 clg_str_free(&cstr);
481
482 if (lg->ctx->callbacks.backtrace_fn) {
483 clg_ctx_backtrace(lg->ctx);
484 }
485
486 if (severity == CLG_SEVERITY_FATAL) {
487 clg_ctx_fatal_action(lg->ctx);
488 }
489 }
490
CLG_logf(CLG_LogType * lg,enum CLG_Severity severity,const char * file_line,const char * fn,const char * fmt,...)491 void CLG_logf(CLG_LogType *lg,
492 enum CLG_Severity severity,
493 const char *file_line,
494 const char *fn,
495 const char *fmt,
496 ...)
497 {
498 CLogStringBuf cstr;
499 char cstr_stack_buf[CLOG_BUF_LEN_INIT];
500 clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
501
502 if (lg->ctx->use_timestamp) {
503 write_timestamp(&cstr, lg->ctx->timestamp_tick_start);
504 }
505
506 write_severity(&cstr, severity, lg->ctx->use_color);
507 write_type(&cstr, lg);
508
509 {
510 write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
511
512 va_list ap;
513 va_start(ap, fmt);
514 clg_str_vappendf(&cstr, fmt, ap);
515 va_end(ap);
516 }
517 clg_str_append(&cstr, "\n");
518
519 /* could be optional */
520 int bytes_written = write(lg->ctx->output, cstr.data, cstr.len);
521 (void)bytes_written;
522
523 clg_str_free(&cstr);
524
525 if (lg->ctx->callbacks.backtrace_fn) {
526 clg_ctx_backtrace(lg->ctx);
527 }
528
529 if (severity == CLG_SEVERITY_ERROR) {
530 clg_ctx_error_action(lg->ctx);
531 }
532
533 if (severity == CLG_SEVERITY_FATAL) {
534 clg_ctx_fatal_action(lg->ctx);
535 }
536 }
537
538 /** \} */
539
540 /* -------------------------------------------------------------------- */
541 /** \name Logging Context API
542 * \{ */
543
CLG_ctx_output_set(CLogContext * ctx,void * file_handle)544 static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
545 {
546 ctx->output_file = file_handle;
547 ctx->output = fileno(ctx->output_file);
548 #if defined(__unix__) || defined(__APPLE__)
549 ctx->use_color = isatty(ctx->output);
550 #elif defined(WIN32)
551 /* Windows Terminal supports color like the Linux terminals do while the standard console does
552 * not, the way to tell the two apart is to look at the `WT_SESSION` environment variable which
553 * will only be defined for Windows Terminal. */
554
555 /* #getenv is used here rather than #BLI_getenv since it would be a bad level call
556 * and there are no benefits for using it in this context. */
557 ctx->use_color = isatty(ctx->output) && getenv("WT_SESSION");
558 #endif
559 }
560
CLG_ctx_output_use_basename_set(CLogContext * ctx,int value)561 static void CLG_ctx_output_use_basename_set(CLogContext *ctx, int value)
562 {
563 ctx->use_basename = (bool)value;
564 }
565
CLG_ctx_output_use_timestamp_set(CLogContext * ctx,int value)566 static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value)
567 {
568 ctx->use_timestamp = (bool)value;
569 if (ctx->use_timestamp) {
570 ctx->timestamp_tick_start = clg_timestamp_ticks_get();
571 }
572 }
573
574 /** Action on error severity. */
CLT_ctx_error_fn_set(CLogContext * ctx,void (* error_fn)(void * file_handle))575 static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle))
576 {
577 ctx->callbacks.error_fn = error_fn;
578 }
579
580 /** Action on fatal severity. */
CLG_ctx_fatal_fn_set(CLogContext * ctx,void (* fatal_fn)(void * file_handle))581 static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
582 {
583 ctx->callbacks.fatal_fn = fatal_fn;
584 }
585
CLG_ctx_backtrace_fn_set(CLogContext * ctx,void (* backtrace_fn)(void * file_handle))586 static void CLG_ctx_backtrace_fn_set(CLogContext *ctx, void (*backtrace_fn)(void *file_handle))
587 {
588 ctx->callbacks.backtrace_fn = backtrace_fn;
589 }
590
clg_ctx_type_filter_append(CLG_IDFilter ** flt_list,const char * type_match,int type_match_len)591 static void clg_ctx_type_filter_append(CLG_IDFilter **flt_list,
592 const char *type_match,
593 int type_match_len)
594 {
595 if (type_match_len == 0) {
596 return;
597 }
598 CLG_IDFilter *flt = MEM_callocN(sizeof(*flt) + (type_match_len + 1), __func__);
599 flt->next = *flt_list;
600 *flt_list = flt;
601 memcpy(flt->match, type_match, type_match_len);
602 /* no need to null terminate since we calloc'd */
603 }
604
CLG_ctx_type_filter_exclude(CLogContext * ctx,const char * type_match,int type_match_len)605 static void CLG_ctx_type_filter_exclude(CLogContext *ctx,
606 const char *type_match,
607 int type_match_len)
608 {
609 clg_ctx_type_filter_append(&ctx->filters[0], type_match, type_match_len);
610 }
611
CLG_ctx_type_filter_include(CLogContext * ctx,const char * type_match,int type_match_len)612 static void CLG_ctx_type_filter_include(CLogContext *ctx,
613 const char *type_match,
614 int type_match_len)
615 {
616 clg_ctx_type_filter_append(&ctx->filters[1], type_match, type_match_len);
617 }
618
CLG_ctx_level_set(CLogContext * ctx,int level)619 static void CLG_ctx_level_set(CLogContext *ctx, int level)
620 {
621 ctx->default_type.level = level;
622 for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
623 ty->level = level;
624 }
625 }
626
CLG_ctx_init(void)627 static CLogContext *CLG_ctx_init(void)
628 {
629 CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
630 #ifdef WITH_CLOG_PTHREADS
631 pthread_mutex_init(&ctx->types_lock, NULL);
632 #endif
633 ctx->default_type.level = 1;
634 CLG_ctx_output_set(ctx, stdout);
635
636 return ctx;
637 }
638
CLG_ctx_free(CLogContext * ctx)639 static void CLG_ctx_free(CLogContext *ctx)
640 {
641 while (ctx->types != NULL) {
642 CLG_LogType *item = ctx->types;
643 ctx->types = item->next;
644 MEM_freeN(item);
645 }
646
647 for (uint i = 0; i < 2; i++) {
648 while (ctx->filters[i] != NULL) {
649 CLG_IDFilter *item = ctx->filters[i];
650 ctx->filters[i] = item->next;
651 MEM_freeN(item);
652 }
653 }
654 #ifdef WITH_CLOG_PTHREADS
655 pthread_mutex_destroy(&ctx->types_lock);
656 #endif
657 MEM_freeN(ctx);
658 }
659
660 /** \} */
661
662 /* -------------------------------------------------------------------- */
663 /** \name Public Logging API
664 *
665 * Currently uses global context.
666 * \{ */
667
668 /* We could support multiple at once, for now this seems not needed. */
669 static struct CLogContext *g_ctx = NULL;
670
CLG_init(void)671 void CLG_init(void)
672 {
673 g_ctx = CLG_ctx_init();
674
675 clg_color_table_init(g_ctx->use_color);
676 }
677
CLG_exit(void)678 void CLG_exit(void)
679 {
680 CLG_ctx_free(g_ctx);
681 }
682
CLG_output_set(void * file_handle)683 void CLG_output_set(void *file_handle)
684 {
685 CLG_ctx_output_set(g_ctx, file_handle);
686 }
687
CLG_output_use_basename_set(int value)688 void CLG_output_use_basename_set(int value)
689 {
690 CLG_ctx_output_use_basename_set(g_ctx, value);
691 }
692
CLG_output_use_timestamp_set(int value)693 void CLG_output_use_timestamp_set(int value)
694 {
695 CLG_ctx_output_use_timestamp_set(g_ctx, value);
696 }
697
CLG_error_fn_set(void (* error_fn)(void * file_handle))698 void CLG_error_fn_set(void (*error_fn)(void *file_handle))
699 {
700 CLT_ctx_error_fn_set(g_ctx, error_fn);
701 }
702
CLG_fatal_fn_set(void (* fatal_fn)(void * file_handle))703 void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
704 {
705 CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
706 }
707
CLG_backtrace_fn_set(void (* fatal_fn)(void * file_handle))708 void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle))
709 {
710 CLG_ctx_backtrace_fn_set(g_ctx, fatal_fn);
711 }
712
CLG_type_filter_exclude(const char * type_match,int type_match_len)713 void CLG_type_filter_exclude(const char *type_match, int type_match_len)
714 {
715 CLG_ctx_type_filter_exclude(g_ctx, type_match, type_match_len);
716 }
717
CLG_type_filter_include(const char * type_match,int type_match_len)718 void CLG_type_filter_include(const char *type_match, int type_match_len)
719 {
720 CLG_ctx_type_filter_include(g_ctx, type_match, type_match_len);
721 }
722
CLG_level_set(int level)723 void CLG_level_set(int level)
724 {
725 CLG_ctx_level_set(g_ctx, level);
726 }
727
728 /** \} */
729
730 /* -------------------------------------------------------------------- */
731 /** \name Logging Reference API
732 *
733 * Use to avoid look-ups each time.
734 * \{ */
735
CLG_logref_init(CLG_LogRef * clg_ref)736 void CLG_logref_init(CLG_LogRef *clg_ref)
737 {
738 #ifdef WITH_CLOG_PTHREADS
739 /* Only runs once when initializing a static type in most cases. */
740 pthread_mutex_lock(&g_ctx->types_lock);
741 #endif
742 if (clg_ref->type == NULL) {
743 CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
744 if (clg_ty == NULL) {
745 clg_ty = clg_ctx_type_register(g_ctx, clg_ref->identifier);
746 }
747 #ifdef WITH_CLOG_PTHREADS
748 atomic_cas_ptr((void **)&clg_ref->type, clg_ref->type, clg_ty);
749 #else
750 clg_ref->type = clg_ty;
751 #endif
752 }
753 #ifdef WITH_CLOG_PTHREADS
754 pthread_mutex_unlock(&g_ctx->types_lock);
755 #endif
756 }
757
CLG_color_support_get(CLG_LogRef * clg_ref)758 int CLG_color_support_get(CLG_LogRef *clg_ref)
759 {
760 if (clg_ref->type == NULL) {
761 CLG_logref_init(clg_ref);
762 }
763 return clg_ref->type->ctx->use_color;
764 }
765
766 /** \} */
767