Line data Source code
1 : /*
2 : * Copyright (c) 2020 rxi
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a copy
5 : * of this software and associated documentation files (the "Software"), to
6 : * deal in the Software without restriction, including without limitation the
7 : * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 : * sell copies of the Software, and to permit persons to whom the Software is
9 : * furnished to do so, subject to the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included in
12 : * all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 : * IN THE SOFTWARE.
21 : */
22 :
23 : #include "clog.h"
24 : #include <math.h>
25 : #include <sys/time.h>
26 :
27 : #define MAX_CALLBACKS 32
28 :
29 : typedef struct {
30 : clog_LogFn fn;
31 : void *udata;
32 : int level;
33 : } CCallback;
34 :
35 : static struct {
36 : void *udata;
37 : clog_LockFn lock;
38 : int level;
39 : bool quiet;
40 : CCallback callbacks[MAX_CALLBACKS];
41 : } CL;
42 :
43 :
44 : static const char *clevel_strings[] = {
45 : "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"
46 : };
47 :
48 :
49 : #ifdef CLOG_USE_COLOR
50 : static const char *clevel_colors[] = {
51 : "\x1b[35m", "\x1b[31m", "\x1b[33m", "\x1b[32m", "\x1b[36m", "\x1b[94m"
52 : };
53 : #endif
54 :
55 :
56 12302 : static void cstdout_callback(clog_Event *ev) {
57 12302 : char buf[20];
58 12302 : struct timeval t;
59 12302 : int msec;
60 12302 : buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
61 12302 : gettimeofday(&t, NULL);
62 12302 : msec = lrint(t.tv_usec/1000.0);
63 12302 : char buffer[24];
64 12302 : snprintf(buffer, sizeof(buffer), "%s.%03d", buf, msec);
65 : #ifdef CLOG_USE_COLOR
66 : fprintf(
67 : ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s %s:%d:\x1b[0m ",
68 : buffer, clevel_colors[ev->level], clevel_strings[ev->level], "client",
69 : ev->file, ev->line);
70 : #else
71 12302 : fprintf(
72 12302 : ev->udata, "%s %-5s %s %s:%d: ",
73 12302 : buffer, clevel_strings[ev->level], "client", ev->file, ev->line);
74 : #endif
75 12302 : vfprintf(ev->udata, ev->fmt, ev->ap);
76 12302 : fprintf(ev->udata, "\n");
77 12302 : fflush(ev->udata);
78 12302 : }
79 :
80 :
81 0 : static void cfile_callback(clog_Event *ev) {
82 0 : char buf[64];
83 0 : buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
84 0 : fprintf(
85 0 : ev->udata, "%s %-5s %s:%d: ",
86 0 : buf, clevel_strings[ev->level], ev->file, ev->line);
87 0 : vfprintf(ev->udata, ev->fmt, ev->ap);
88 0 : fprintf(ev->udata, "\n");
89 0 : fflush(ev->udata);
90 0 : }
91 :
92 :
93 114047 : static void clog_lock(void) {
94 114047 : if (CL.lock) { CL.lock(true, CL.udata); }
95 114047 : }
96 :
97 :
98 114047 : static void clog_unlock(void) {
99 114047 : if (CL.lock) { CL.lock(false, CL.udata); }
100 114047 : }
101 :
102 :
103 419 : const char* clog_level_string(int level) {
104 419 : return clevel_strings[level];
105 : }
106 :
107 :
108 0 : void clog_set_lock(clog_LockFn fn, void *udata) {
109 0 : CL.lock = fn;
110 0 : CL.udata = udata;
111 0 : }
112 :
113 :
114 419 : void clog_set_level(int level) {
115 419 : CL.level = CLOG_INFO;
116 419 : clog_info("Setting log_level to %s", clog_level_string(level));
117 419 : CL.level = level;
118 419 : }
119 :
120 0 : int clog_level() {
121 0 : return CL.level;
122 : }
123 :
124 0 : void clog_set_quiet(bool enable) {
125 0 : CL.quiet = enable;
126 0 : }
127 :
128 :
129 0 : int clog_add_callback(clog_LogFn fn, void *udata, int level) {
130 0 : for (int i = 0; i < MAX_CALLBACKS; i++) {
131 0 : if (!CL.callbacks[i].fn) {
132 0 : CL.callbacks[i] = (CCallback) { fn, udata, level };
133 0 : return 0;
134 : }
135 : }
136 : return -1;
137 : }
138 :
139 :
140 0 : int clog_add_fp(FILE *fp, int level) {
141 0 : return clog_add_callback(cfile_callback, fp, level);
142 : }
143 :
144 :
145 12302 : static void cinit_event(clog_Event *ev, void *udata) {
146 12302 : if (!ev->time) {
147 12302 : time_t t = time(NULL);
148 12302 : ev->time = localtime(&t);
149 : }
150 12302 : ev->udata = udata;
151 12302 : }
152 :
153 :
154 114046 : void clog_log(int level, const char *file, int line, const char *fmt, ...) {
155 114046 : clog_Event ev = {
156 : .fmt = fmt,
157 : .file = file,
158 : .line = line,
159 : .level = level,
160 : };
161 :
162 114046 : clog_lock();
163 :
164 114047 : if (!CL.quiet && level <= CL.level) {
165 12302 : if ( level <= CLOG_WARN){
166 166 : cinit_event(&ev, stderr);
167 : } else {
168 12136 : cinit_event(&ev, stdout);
169 : }
170 12302 : va_start(ev.ap, fmt);
171 12302 : cstdout_callback(&ev);
172 12302 : va_end(ev.ap);
173 : }
174 :
175 114047 : for (int i = 0; i < MAX_CALLBACKS && CL.callbacks[i].fn; i++) {
176 0 : CCallback *cb = &CL.callbacks[i];
177 0 : if (level <= cb->level) {
178 0 : cinit_event(&ev, cb->udata);
179 0 : va_start(ev.ap, fmt);
180 0 : cb->fn(&ev);
181 0 : va_end(ev.ap);
182 : }
183 : }
184 :
185 114047 : clog_unlock();
186 114047 : }
|