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