xglcomp

view src/logger.c @ 0:d9b3fba68705

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