xglcomp
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/logger.c Thu Jan 21 08:45:31 2016 +0200 1.3 @@ -0,0 +1,351 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <errno.h> 1.8 +#include <stdarg.h> 1.9 +#include "logger.h" 1.10 + 1.11 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 1.12 +#include <unistd.h> 1.13 +#include <fcntl.h> 1.14 +#include <pthread.h> 1.15 +#include <sys/select.h> 1.16 +#endif 1.17 + 1.18 +enum targ_type { TARG_STREAM, TARG_FILE, TARG_FUNC }; 1.19 + 1.20 +struct log_target { 1.21 + unsigned int msg_type; 1.22 + enum targ_type targ_type; 1.23 + FILE *fp; 1.24 + void (*func)(const char*, void*); 1.25 + void *func_cls; 1.26 + 1.27 + struct log_target *next; 1.28 +}; 1.29 + 1.30 +static struct log_target *targ_list = (void*)1; /* uninitialized */ 1.31 + 1.32 +static void init_once(void) 1.33 +{ 1.34 + if(targ_list == (void*)1) { 1.35 + /* perform one-time init */ 1.36 + targ_list = 0; 1.37 + log_add_stream(LOG_INFO | LOG_DEBUG, stdout); 1.38 + log_add_stream(LOG_WARNING | LOG_ERROR, stderr); 1.39 + } 1.40 +} 1.41 + 1.42 +static void log_string(unsigned int type, const char *str); 1.43 +static int typecolor(int type); 1.44 + 1.45 +void log_clear_targets(void) 1.46 +{ 1.47 + init_once(); 1.48 + 1.49 + while(targ_list) { 1.50 + struct log_target *tmp = targ_list; 1.51 + targ_list = targ_list->next; 1.52 + 1.53 + if(tmp->targ_type == TARG_FILE) { 1.54 + fclose(tmp->fp); 1.55 + } 1.56 + free(tmp); 1.57 + } 1.58 + targ_list = 0; 1.59 +} 1.60 + 1.61 +int log_add_stream(unsigned int type_mask, FILE *fp) 1.62 +{ 1.63 + struct log_target *targ; 1.64 + 1.65 + init_once(); 1.66 + 1.67 + if(!(targ = malloc(sizeof *targ))) { 1.68 + perror("failed to allocate memory for log target"); 1.69 + return -1; 1.70 + } 1.71 + targ->msg_type = type_mask; 1.72 + targ->targ_type = TARG_STREAM; 1.73 + targ->fp = fp; 1.74 + targ->next = targ_list; 1.75 + targ_list = targ; 1.76 + return 0; 1.77 +} 1.78 + 1.79 +int log_add_file(unsigned int type_mask, const char *fname) 1.80 +{ 1.81 + FILE *fp; 1.82 + struct log_target *targ; 1.83 + 1.84 + init_once(); 1.85 + 1.86 + if(!(fp = fopen(fname, "w"))) { 1.87 + fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno)); 1.88 + return -1; 1.89 + } 1.90 + if(type_mask & (LOG_ERROR | LOG_DEBUG)) { 1.91 + /* make error and debug logs unbuffered */ 1.92 + setvbuf(fp, 0, _IONBF, 0); 1.93 + } 1.94 + if(!(targ = malloc(sizeof *targ))) { 1.95 + perror("failed to allocate memory for log target"); 1.96 + fclose(fp); 1.97 + return -1; 1.98 + } 1.99 + targ->msg_type = type_mask; 1.100 + targ->targ_type = TARG_FILE; 1.101 + targ->fp = fp; 1.102 + targ->next = targ_list; 1.103 + targ_list = targ; 1.104 + return 0; 1.105 +} 1.106 + 1.107 +int log_add_func(unsigned int type_mask, void (*func)(const char*, void*), void *cls) 1.108 +{ 1.109 + struct log_target *targ; 1.110 + 1.111 + init_once(); 1.112 + 1.113 + if(!(targ = malloc(sizeof *targ))) { 1.114 + perror("failed to allocate memory for log target"); 1.115 + return -1; 1.116 + } 1.117 + targ->msg_type = type_mask; 1.118 + targ->targ_type = TARG_FUNC; 1.119 + targ->func = func; 1.120 + targ->func_cls = cls; 1.121 + targ->next = targ_list; 1.122 + targ_list = targ; 1.123 + return 0; 1.124 +} 1.125 + 1.126 + 1.127 +void log_va_msg(unsigned int type, const char *fmt, va_list ap) 1.128 +{ 1.129 + char fixedbuf[256]; 1.130 + char *buf = fixedbuf; 1.131 + int len, sz = sizeof fixedbuf; 1.132 + 1.133 + init_once(); 1.134 + 1.135 + if(!targ_list || !*fmt) return; /* don't waste our time */ 1.136 + 1.137 + /* try with the fixed size buffer first which should be sufficient most of the 1.138 + * time. if this fails, allocate a buffer of the correct size. 1.139 + */ 1.140 + while((len = vsnprintf(buf, sz, fmt, ap)) >= sz || len < 0) { 1.141 + sz = len >= 0 ? len + 1 : sz * 2; 1.142 + if(buf != fixedbuf) 1.143 + free(buf); 1.144 + if(!(buf = malloc(sz))) { 1.145 + return; 1.146 + } 1.147 + } 1.148 + 1.149 + if(buf != fixedbuf) 1.150 + free(buf); 1.151 + 1.152 + log_string(type, buf); 1.153 +} 1.154 + 1.155 +/* helpers */ 1.156 +#define LOG_VA_MSG(t) \ 1.157 + do { \ 1.158 + va_list ap; \ 1.159 + va_start(ap, fmt); \ 1.160 + log_va_msg(t, fmt, ap); \ 1.161 + va_end(ap); \ 1.162 + } while(0) 1.163 + 1.164 +void log_msg(unsigned int type, const char *fmt, ...) 1.165 +{ 1.166 + LOG_VA_MSG(type); 1.167 +} 1.168 + 1.169 +void log_info(const char *fmt, ...) 1.170 +{ 1.171 + LOG_VA_MSG(LOG_INFO); 1.172 +} 1.173 + 1.174 +void log_warning(const char *fmt, ...) 1.175 +{ 1.176 + LOG_VA_MSG(LOG_WARNING); 1.177 +} 1.178 + 1.179 +void log_error(const char *fmt, ...) 1.180 +{ 1.181 + LOG_VA_MSG(LOG_ERROR); 1.182 +} 1.183 + 1.184 +void log_debug(const char *fmt, ...) 1.185 +{ 1.186 + LOG_VA_MSG(LOG_DEBUG); 1.187 +} 1.188 + 1.189 +void log_va_info(const char *fmt, va_list ap) 1.190 +{ 1.191 + log_va_msg(LOG_INFO, fmt, ap); 1.192 +} 1.193 + 1.194 +void log_va_warning(const char *fmt, va_list ap) 1.195 +{ 1.196 + log_va_msg(LOG_WARNING, fmt, ap); 1.197 +} 1.198 + 1.199 +void log_va_error(const char *fmt, va_list ap) 1.200 +{ 1.201 + log_va_msg(LOG_ERROR, fmt, ap); 1.202 +} 1.203 + 1.204 +void log_va_debug(const char *fmt, va_list ap) 1.205 +{ 1.206 + log_va_msg(LOG_DEBUG, fmt, ap); 1.207 +} 1.208 + 1.209 +static void log_string(unsigned int type, const char *str) 1.210 +{ 1.211 + struct log_target *targ; 1.212 + 1.213 + targ = targ_list; 1.214 + while(targ) { 1.215 + if(targ->msg_type & type) { 1.216 + if(targ->targ_type == TARG_STREAM || targ->targ_type == TARG_FILE) { 1.217 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 1.218 + if(isatty(fileno(targ->fp)) && type != LOG_INFO) { 1.219 + int c = typecolor(type); 1.220 + fprintf(targ->fp, "\033[%dm%s\033[0m", c, str); 1.221 + } else 1.222 +#endif 1.223 + { 1.224 + fputs(str, targ->fp); 1.225 + } 1.226 + 1.227 + } else if(targ->targ_type == TARG_FUNC) { 1.228 + targ->func(str, targ->func_cls); 1.229 + } 1.230 + } 1.231 + targ = targ->next; 1.232 + } 1.233 +} 1.234 + 1.235 +enum { 1.236 + BLACK = 0, 1.237 + RED, 1.238 + GREEN, 1.239 + YELLOW, 1.240 + BLUE, 1.241 + MAGENTA, 1.242 + CYAN, 1.243 + WHITE 1.244 +}; 1.245 + 1.246 +#define ANSI_FGCOLOR(x) (30 + (x)) 1.247 +#define ANSI_BGCOLOR(x) (40 + (x)) 1.248 + 1.249 +static int typecolor(int type) 1.250 +{ 1.251 + switch(type) { 1.252 + case LOG_ERROR: 1.253 + return ANSI_FGCOLOR(RED); 1.254 + case LOG_WARNING: 1.255 + return ANSI_FGCOLOR(YELLOW); 1.256 + case LOG_DEBUG: 1.257 + return ANSI_FGCOLOR(MAGENTA); 1.258 + default: 1.259 + break; 1.260 + } 1.261 + return 37; 1.262 +} 1.263 + 1.264 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 1.265 +static int out_pipe[2], err_pipe[2]; 1.266 +static int thr_running; 1.267 + 1.268 +static void *thr_func(void *arg); 1.269 + 1.270 +static int std_intercept(void) 1.271 +{ 1.272 + pthread_t thr; 1.273 + 1.274 + if(thr_running) return 0; 1.275 + 1.276 + pipe(out_pipe); 1.277 + pipe(err_pipe); 1.278 + 1.279 + fcntl(out_pipe[0], F_SETFL, fcntl(out_pipe[0], F_GETFL) | O_NONBLOCK); 1.280 + fcntl(err_pipe[0], F_SETFL, fcntl(err_pipe[0], F_GETFL) | O_NONBLOCK); 1.281 + 1.282 + if(pthread_create(&thr, 0, thr_func, 0) == -1) { 1.283 + log_error("failed to start std intercept thread\n"); 1.284 + return -1; 1.285 + } 1.286 + thr_running = 1; 1.287 + pthread_detach(thr); 1.288 + return 0; 1.289 +} 1.290 + 1.291 +static void *thr_func(void *arg) 1.292 +{ 1.293 + static char buf[512]; 1.294 + static int bufsz = sizeof buf; 1.295 + int max_fd = out_pipe[0] > err_pipe[0] ? out_pipe[0] : err_pipe[0]; 1.296 + 1.297 + for(;;) { 1.298 + int sz, res; 1.299 + fd_set rdset; 1.300 + 1.301 + FD_ZERO(&rdset); 1.302 + FD_SET(out_pipe[0], &rdset); 1.303 + FD_SET(err_pipe[0], &rdset); 1.304 + 1.305 + while((res = select(max_fd + 1, &rdset, 0, 0, 0)) == -1 && errno == EINTR); 1.306 + if(res == -1) { 1.307 + break; /* assume EPIPE or non-recoverable error */ 1.308 + } 1.309 + if(res == 0) continue; 1.310 + 1.311 + if(FD_ISSET(out_pipe[0], &rdset)) { 1.312 + while((sz = read(out_pipe[0], buf, bufsz - 1)) > 0) { 1.313 + buf[bufsz - 1] = 0; 1.314 + log_info("%s", buf); 1.315 + } 1.316 + if(sz == -1 && errno == EPIPE) break; 1.317 + } 1.318 + if(FD_ISSET(err_pipe[0], &rdset)) { 1.319 + while((sz = read(err_pipe[0], buf, bufsz - 1)) > 0) { 1.320 + buf[bufsz - 1] = 0; 1.321 + log_error("%s", buf); 1.322 + } 1.323 + if(sz == -1 && errno == EPIPE) break; 1.324 + } 1.325 + } 1.326 + return 0; 1.327 +} 1.328 + 1.329 +void log_grab_stdout(void) 1.330 +{ 1.331 + if(std_intercept() == -1) { 1.332 + return; 1.333 + } 1.334 + dup2(out_pipe[1], 1); 1.335 +} 1.336 + 1.337 +void log_grab_stderr(void) 1.338 +{ 1.339 + if(std_intercept() == -1) { 1.340 + return; 1.341 + } 1.342 + dup2(err_pipe[1], 2); 1.343 +} 1.344 +#else 1.345 +void log_grab_stdout(void) 1.346 +{ 1.347 + log_error("log_grab_stdout only works on UNIX\n"); 1.348 +} 1.349 + 1.350 +void log_grab_stderr(void) 1.351 +{ 1.352 + log_error("log_grab_stderr only works on UNIX\n"); 1.353 +} 1.354 +#endif