xglcomp

view src/logger.c @ 6:3f908f812ec7

added docs
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 29 Jan 2016 10:23:08 +0200
parents d9b3fba68705
children
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <stdarg.h>
6 #ifdef _MSC_VER
7 #include <malloc.h>
8 #else
9 #include <alloca.h>
10 #endif
11 #include "logger.h"
13 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <pthread.h>
17 #include <sys/select.h>
18 #endif
20 enum targ_type { TARG_STREAM, TARG_FILE, TARG_FUNC };
22 struct log_target {
23 unsigned int msg_type;
24 enum targ_type targ_type;
25 FILE *fp;
26 void (*func)(const char*, void*);
27 void *func_cls;
29 struct log_target *next;
30 };
32 static struct log_target *targ_list = (void*)1; /* uninitialized */
34 static void init_once(void)
35 {
36 if(targ_list == (void*)1) {
37 /* perform one-time init */
38 targ_list = 0;
39 log_add_stream(LOG_INFO | LOG_DEBUG, stdout);
40 log_add_stream(LOG_WARNING | LOG_ERROR, stderr);
41 }
42 }
44 static void log_string(unsigned int type, const char *str);
45 static int typecolor(int type);
47 void log_clear_targets(void)
48 {
49 init_once();
51 while(targ_list) {
52 struct log_target *tmp = targ_list;
53 targ_list = targ_list->next;
55 if(tmp->targ_type == TARG_FILE) {
56 fclose(tmp->fp);
57 }
58 free(tmp);
59 }
60 targ_list = 0;
61 }
63 int log_add_stream(unsigned int type_mask, FILE *fp)
64 {
65 struct log_target *targ;
67 init_once();
69 if(!(targ = malloc(sizeof *targ))) {
70 perror("failed to allocate memory for log target");
71 return -1;
72 }
73 targ->msg_type = type_mask;
74 targ->targ_type = TARG_STREAM;
75 targ->fp = fp;
76 targ->next = targ_list;
77 targ_list = targ;
78 return 0;
79 }
81 int log_add_file(unsigned int type_mask, const char *fname)
82 {
83 FILE *fp;
84 struct log_target *targ;
86 init_once();
88 if(!(fp = fopen(fname, "w"))) {
89 fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno));
90 return -1;
91 }
92 if(type_mask & (LOG_ERROR | LOG_DEBUG)) {
93 /* make error and debug logs unbuffered */
94 setvbuf(fp, 0, _IONBF, 0);
95 }
96 if(!(targ = malloc(sizeof *targ))) {
97 perror("failed to allocate memory for log target");
98 fclose(fp);
99 return -1;
100 }
101 targ->msg_type = type_mask;
102 targ->targ_type = TARG_FILE;
103 targ->fp = fp;
104 targ->next = targ_list;
105 targ_list = targ;
106 return 0;
107 }
109 int log_add_func(unsigned int type_mask, void (*func)(const char*, void*), void *cls)
110 {
111 struct log_target *targ;
113 init_once();
115 if(!(targ = malloc(sizeof *targ))) {
116 perror("failed to allocate memory for log target");
117 return -1;
118 }
119 targ->msg_type = type_mask;
120 targ->targ_type = TARG_FUNC;
121 targ->func = func;
122 targ->func_cls = cls;
123 targ->next = targ_list;
124 targ_list = targ;
125 return 0;
126 }
129 void log_va_msg(unsigned int type, const char *fmt, va_list ap)
130 {
131 char fixedbuf[256];
132 char *buf = fixedbuf;
133 int len, sz = sizeof fixedbuf;
135 init_once();
137 if(!targ_list || !*fmt) return; /* don't waste our time */
139 /* try with the fixed size buffer first which should be sufficient most of the
140 * time. if this fails, allocate a buffer of the correct size.
141 */
142 while((len = vsnprintf(buf, sz, fmt, ap)) >= sz || len < 0) {
143 sz = len >= 0 ? len + 1 : sz * 2;
144 if(buf != fixedbuf)
145 free(buf);
146 if(!(buf = malloc(sz))) {
147 return;
148 }
149 }
151 if(buf != fixedbuf)
152 free(buf);
154 log_string(type, buf);
155 }
157 /* helpers */
158 #define LOG_VA_MSG(t) \
159 do { \
160 va_list ap; \
161 va_start(ap, fmt); \
162 log_va_msg(t, fmt, ap); \
163 va_end(ap); \
164 } while(0)
166 void log_msg(unsigned int type, const char *fmt, ...)
167 {
168 LOG_VA_MSG(type);
169 }
171 void log_info(const char *fmt, ...)
172 {
173 LOG_VA_MSG(LOG_INFO);
174 }
176 void log_warning(const char *fmt, ...)
177 {
178 LOG_VA_MSG(LOG_WARNING);
179 }
181 void log_error(const char *fmt, ...)
182 {
183 LOG_VA_MSG(LOG_ERROR);
184 }
186 void log_debug(const char *fmt, ...)
187 {
188 LOG_VA_MSG(LOG_DEBUG);
189 }
191 void log_va_info(const char *fmt, va_list ap)
192 {
193 log_va_msg(LOG_INFO, fmt, ap);
194 }
196 void log_va_warning(const char *fmt, va_list ap)
197 {
198 log_va_msg(LOG_WARNING, fmt, ap);
199 }
201 void log_va_error(const char *fmt, va_list ap)
202 {
203 log_va_msg(LOG_ERROR, fmt, ap);
204 }
206 void log_va_debug(const char *fmt, va_list ap)
207 {
208 log_va_msg(LOG_DEBUG, fmt, ap);
209 }
211 static void log_string(unsigned int type, const char *str)
212 {
213 struct log_target *targ;
215 targ = targ_list;
216 while(targ) {
217 if(targ->msg_type & type) {
218 if(targ->targ_type == TARG_STREAM || targ->targ_type == TARG_FILE) {
219 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
220 if(isatty(fileno(targ->fp)) && type != LOG_INFO) {
221 int c = typecolor(type);
223 int len = strlen(str);
224 if(str[len - 1] == '\n') {
225 char *s = alloca(len);
226 memcpy(s, str, len);
227 s[len - 1] = 0;
228 fprintf(targ->fp, "\033[%dm%s\033[0m\n", c, s);
229 } else {
230 fprintf(targ->fp, "\033[%dm%s\033[0m", c, str);
231 }
232 } else
233 #endif
234 {
235 fputs(str, targ->fp);
236 }
238 } else if(targ->targ_type == TARG_FUNC) {
239 targ->func(str, targ->func_cls);
240 }
241 }
242 targ = targ->next;
243 }
244 }
246 enum {
247 BLACK = 0,
248 RED,
249 GREEN,
250 YELLOW,
251 BLUE,
252 MAGENTA,
253 CYAN,
254 WHITE
255 };
257 #define ANSI_FGCOLOR(x) (30 + (x))
258 #define ANSI_BGCOLOR(x) (40 + (x))
260 static int typecolor(int type)
261 {
262 switch(type) {
263 case LOG_ERROR:
264 return ANSI_FGCOLOR(RED);
265 case LOG_WARNING:
266 return ANSI_FGCOLOR(YELLOW);
267 case LOG_DEBUG:
268 return ANSI_FGCOLOR(MAGENTA);
269 default:
270 break;
271 }
272 return 37;
273 }
275 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
276 static int out_pipe[2], err_pipe[2];
277 static int thr_running;
279 static void *thr_func(void *arg);
281 static int std_intercept(void)
282 {
283 pthread_t thr;
285 if(thr_running) return 0;
287 pipe(out_pipe);
288 pipe(err_pipe);
290 fcntl(out_pipe[0], F_SETFL, fcntl(out_pipe[0], F_GETFL) | O_NONBLOCK);
291 fcntl(err_pipe[0], F_SETFL, fcntl(err_pipe[0], F_GETFL) | O_NONBLOCK);
293 if(pthread_create(&thr, 0, thr_func, 0) == -1) {
294 log_error("failed to start std intercept thread\n");
295 return -1;
296 }
297 thr_running = 1;
298 pthread_detach(thr);
299 return 0;
300 }
302 static void *thr_func(void *arg)
303 {
304 static char buf[512];
305 static int bufsz = sizeof buf;
306 int max_fd = out_pipe[0] > err_pipe[0] ? out_pipe[0] : err_pipe[0];
308 for(;;) {
309 int sz, res;
310 fd_set rdset;
312 FD_ZERO(&rdset);
313 FD_SET(out_pipe[0], &rdset);
314 FD_SET(err_pipe[0], &rdset);
316 while((res = select(max_fd + 1, &rdset, 0, 0, 0)) == -1 && errno == EINTR);
317 if(res == -1) {
318 break; /* assume EPIPE or non-recoverable error */
319 }
320 if(res == 0) continue;
322 if(FD_ISSET(out_pipe[0], &rdset)) {
323 while((sz = read(out_pipe[0], buf, bufsz - 1)) > 0) {
324 buf[bufsz - 1] = 0;
325 log_info("%s", buf);
326 }
327 if(sz == -1 && errno == EPIPE) break;
328 }
329 if(FD_ISSET(err_pipe[0], &rdset)) {
330 while((sz = read(err_pipe[0], buf, bufsz - 1)) > 0) {
331 buf[bufsz - 1] = 0;
332 log_error("%s", buf);
333 }
334 if(sz == -1 && errno == EPIPE) break;
335 }
336 }
337 return 0;
338 }
340 void log_grab_stdout(void)
341 {
342 if(std_intercept() == -1) {
343 return;
344 }
345 dup2(out_pipe[1], 1);
346 }
348 void log_grab_stderr(void)
349 {
350 if(std_intercept() == -1) {
351 return;
352 }
353 dup2(err_pipe[1], 2);
354 }
355 #else
356 void log_grab_stdout(void)
357 {
358 log_error("log_grab_stdout only works on UNIX\n");
359 }
361 void log_grab_stderr(void)
362 {
363 log_error("log_grab_stderr only works on UNIX\n");
364 }
365 #endif