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 "log.h"
24 : #include <math.h>
25 : #include <sys/time.h>
26 :
27 : #define MAX_CALLBACKS 32
28 :
29 : typedef struct {
30 : log_LogFn fn;
31 : void *udata;
32 : int level;
33 : } Callback;
34 :
35 : static struct {
36 : void *udata;
37 : log_LockFn lock;
38 : int level;
39 : bool quiet;
40 : Callback callbacks[MAX_CALLBACKS];
41 : } L;
42 :
43 :
44 : static const char *level_strings[] = {
45 : "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"
46 : };
47 :
48 :
49 : #ifdef LOG_USE_COLOR
50 : static const char *level_colors[] = {
51 : "\x1b[35m", "\x1b[31m", "\x1b[33m", "\x1b[32m", "\x1b[36m", "\x1b[94m"
52 : };
53 : #endif
54 :
55 :
56 14615 : static void stdout_callback(log_Event *ev) {
57 14615 : char buf[20];
58 14615 : struct timeval t;
59 14615 : int msec;
60 14615 : buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
61 14615 : gettimeofday(&t, NULL);
62 14615 : msec = lrint(t.tv_usec/1000.0);
63 14615 : char buffer[24];
64 14615 : snprintf(buffer, sizeof(buffer), "%s.%03d", buf, msec);
65 : #ifdef LOG_USE_COLOR
66 : fprintf(
67 : ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s %s:%d:\x1b[0m ",
68 : buffer, level_colors[ev->level], level_strings[ev->level], "netio",
69 : ev->file, ev->line);
70 : #else
71 14615 : fprintf(
72 14615 : ev->udata, "%s %-5s%s %s:%d: ",
73 14615 : buffer, level_strings[ev->level], "netio", ev->file, ev->line);
74 : #endif
75 14615 : vfprintf(ev->udata, ev->fmt, ev->ap);
76 14615 : fprintf(ev->udata, "\n");
77 14615 : fflush(ev->udata);
78 14615 : }
79 :
80 :
81 0 : static void file_callback(log_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, level_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 14832 : static void lock(void) {
94 14832 : if (L.lock) { L.lock(true, L.udata); }
95 14832 : }
96 :
97 :
98 14832 : static void unlock(void) {
99 14832 : if (L.lock) { L.lock(false, L.udata); }
100 14832 : }
101 :
102 :
103 0 : const char* log_level_string(int level) {
104 0 : return level_strings[level];
105 : }
106 :
107 :
108 0 : void log_set_lock(log_LockFn fn, void *udata) {
109 0 : L.lock = fn;
110 0 : L.udata = udata;
111 0 : }
112 :
113 :
114 963 : void log_set_level(int level) {
115 963 : L.level = level;
116 963 : }
117 :
118 :
119 0 : void log_set_quiet(bool enable) {
120 0 : L.quiet = enable;
121 0 : }
122 :
123 :
124 0 : int log_add_callback(log_LogFn fn, void *udata, int level) {
125 0 : for (int i = 0; i < MAX_CALLBACKS; i++) {
126 0 : if (!L.callbacks[i].fn) {
127 0 : L.callbacks[i] = (Callback) { fn, udata, level };
128 0 : return 0;
129 : }
130 : }
131 : return -1;
132 : }
133 :
134 :
135 0 : int log_add_fp(FILE *fp, int level) {
136 0 : return log_add_callback(file_callback, fp, level);
137 : }
138 :
139 :
140 14614 : static void init_event(log_Event *ev, void *udata) {
141 14614 : if (!ev->time) {
142 14614 : time_t t = time(NULL);
143 14615 : ev->time = localtime(&t);
144 : }
145 14615 : ev->udata = udata;
146 14615 : }
147 :
148 :
149 14832 : void log_log(int level, const char *file, int line, const char *fmt, ...) {
150 14832 : log_Event ev = {
151 : .fmt = fmt,
152 : .file = file,
153 : .line = line,
154 : .level = level,
155 : };
156 :
157 14832 : lock();
158 :
159 14832 : if (!L.quiet && level <= L.level) {
160 14614 : if ( level <= LOG_WARN){
161 1080 : init_event(&ev, stderr);
162 : } else {
163 13534 : init_event(&ev, stdout);
164 : }
165 14615 : va_start(ev.ap, fmt);
166 14615 : stdout_callback(&ev);
167 14615 : va_end(ev.ap);
168 : }
169 :
170 14833 : for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
171 1 : Callback *cb = &L.callbacks[i];
172 1 : if (level <= cb->level) {
173 0 : init_event(&ev, cb->udata);
174 0 : va_start(ev.ap, fmt);
175 0 : cb->fn(&ev);
176 0 : va_end(ev.ap);
177 : }
178 : }
179 :
180 14832 : unlock();
181 14832 : }
|