xglcomp

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