monolog.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include "monolog.h"
  2. #include "dynar.h"
  3. struct Monologger {
  4. struct MonologLoggerInterface logger;
  5. void * data;
  6. int index;
  7. };
  8. struct Monolevel {
  9. char * name;
  10. int enabled;
  11. };
  12. struct Monolog {
  13. Dynar * loggers;
  14. Dynar * levels;
  15. int last_logger;
  16. };
  17. static struct Monolog monolog_struct = { NULL, NULL, 0 };
  18. static struct Monolog * monolog = &monolog_struct;
  19. void monolog_done() {
  20. int index;
  21. if (monolog->loggers) {
  22. /* clean up all loggers */
  23. for (index = 0; index < dynar_size(monolog->loggers); index ++) {
  24. monolog_remove_logger(index);
  25. }
  26. dynar_free(monolog->loggers);
  27. monolog->loggers = NULL;
  28. }
  29. if (monolog->levels) {
  30. dynar_free(monolog->levels);
  31. monolog->levels = NULL;
  32. }
  33. monolog->last_logger = 0;
  34. }
  35. /* Compares loggers with each other for sorting. This is qsort compatible. */
  36. static int monolog_compare_loggers(void * p1, void * p2) {
  37. struct Monologger * me = *((struct Monologger **) p1);
  38. struct Monologger * you = *((struct Monologger **) p2);
  39. if (!me) return -1;
  40. if (!you) return 1;
  41. if (!me->logger.log) return -1;
  42. if (!you->logger.log) return 1;
  43. /* Compare index if no nulls. */
  44. if (me->index < you->index) return -1;
  45. if (me->index > you->index) return 1;
  46. return 0;
  47. }
  48. /* Compares loglevels with each other for sorting. This is qsort compatible. */
  49. static int monolog_compare_loglevels(const void * p1, const void * p2) {
  50. const struct Monolevel * me = p1;
  51. const struct Monolevel * you = p2;
  52. if(!me) return -1;
  53. if(!you) return 1;
  54. if(!me->name) return -1;
  55. if(!you->name) return 1;
  56. /* Compare name if no nulls. */
  57. return strcmp(me->name, you->name);
  58. return 0;
  59. }
  60. #define MONOLOG_START_LEVELS 32
  61. #define MONOLOG_START_LOGGERS 32
  62. static int monolog_get_loggers_max() {
  63. return dynar_size(monolog->loggers);
  64. }
  65. static struct Monologger * monolog_get_logger(int index) {
  66. return dynar_getdata(monolog->loggers, index);
  67. }
  68. static int monolog_get_unused_logger() {
  69. int index;
  70. for (index = 0 ; index < dynar_size(monolog->loggers); index++) {
  71. struct Monologger * logger = monolog_get_logger(index);
  72. if (!logger->logger.log) {
  73. return index;
  74. }
  75. }
  76. return -1;
  77. }
  78. static struct Monologger *
  79. monolog_set_logger(int index, void *data, struct MonologLoggerInterface * ifa) {
  80. struct Monologger * logger = monolog_get_logger(index);
  81. if(!logger) return NULL;
  82. logger->index = index;
  83. logger->data = data;
  84. logger->logger = (*ifa);
  85. return logger;
  86. }
  87. static struct Monolevel * monolog_get_level(int index) {
  88. return dynar_getdata(monolog->levels, index);
  89. }
  90. static int monolog_get_unused_level() {
  91. int index;
  92. for (index = 0 ; index < dynar_size(monolog->levels); index++) {
  93. struct Monolevel * level = monolog_get_level(index);
  94. if (!level->name) {
  95. return index;
  96. }
  97. }
  98. return -1;
  99. }
  100. static struct Monolevel *
  101. monolog_set_level (int index, char * name, int enabled) {
  102. struct Monolevel * level = monolog_get_level(index);
  103. if (!level) return NULL;
  104. level->name = name;
  105. level->enabled = enabled;
  106. return level;
  107. }
  108. int monolog_init() {
  109. int index;
  110. monolog->loggers = dynar_new(MONOLOG_START_LEVELS, sizeof(struct Monologger));
  111. if(!monolog->loggers) {
  112. return FALSE;
  113. }
  114. monolog->levels = dynar_new(MONOLOG_START_LOGGERS, sizeof(struct Monolevel));
  115. if(!monolog->levels) {
  116. monolog_done();
  117. return FALSE;
  118. }
  119. for (index = 0; index < dynar_amount(monolog->loggers) ; index++ ) {
  120. struct MonologLoggerInterface ifa = { NULL, NULL };
  121. monolog_set_logger(index, NULL, &ifa);
  122. }
  123. for (index = 0; index < dynar_amount(monolog->levels) ; index++ ) {
  124. monolog_set_level(index, NULL, FALSE);
  125. }
  126. return TRUE;
  127. }
  128. /** Adds a loger. Returns negative or error zero or positive (the index of the logger)
  129. * on success. */
  130. int monolog_add_logger(void * data, struct MonologLoggerInterface * logger) {
  131. int logindex;
  132. if (monolog->last_logger >= dynar_amount(monolog->loggers)) {
  133. if (!dynar_grow(monolog->loggers, MONOLOG_START_LOGGERS)) {
  134. return -1;
  135. }
  136. }
  137. logindex = monolog_get_unused_logger();
  138. if (logindex < 0) {
  139. return -2;
  140. }
  141. monolog_set_logger(logindex, data, logger);
  142. return logindex;
  143. }
  144. int monolog_remove_logger(int index) {
  145. struct MonologLoggerInterface ifa = { NULL, NULL };
  146. struct Monologger * logger = monolog_get_logger(index);
  147. /* Call logger's destructor if needed. */
  148. if (logger->logger.free) {
  149. logger->logger.free(logger->data);
  150. }
  151. monolog_set_logger(index, NULL, &ifa);
  152. return index;
  153. }
  154. struct Monolevel * monolog_find_level_by_name(char * name) {
  155. struct Monolevel key;
  156. key.name = name;
  157. key.enabled = 123;
  158. return dynar_bsearch(monolog->levels, &key, monolog_compare_loglevels);
  159. }
  160. int monolog_setup_level(char * name, int enabled) {
  161. struct Monolevel * level;
  162. int levelindex;
  163. level = monolog_find_level_by_name(name);
  164. if (!level) {
  165. levelindex = monolog_get_unused_level();
  166. if (levelindex < 0) return -1;
  167. level = monolog_get_level(levelindex);
  168. }
  169. level->name = name;
  170. level->enabled = enabled;
  171. dynar_qsort(monolog->levels, monolog_compare_loglevels);
  172. return 1;
  173. }
  174. int monolog_enable_level(char * name) {
  175. return monolog_setup_level(name, TRUE);
  176. }
  177. int monolog_disable_level(char * name) {
  178. return monolog_setup_level(name, FALSE);
  179. }
  180. int monolog_log_va(char * file, int line, char * name, char * format, va_list args) {
  181. int index;
  182. va_list args_copy;
  183. struct Monolevel * level = monolog_find_level_by_name(name);
  184. if ((!level) || (!level->enabled)) {
  185. return -1;
  186. }
  187. for (index = 0; index < dynar_size(monolog->loggers); index++) {
  188. struct Monologger * logger = monolog_get_logger(index);
  189. if (logger && logger->logger.log) {
  190. va_copy(args_copy, args);
  191. logger->logger.log(file, line, level->name, logger->data, format, args_copy);
  192. va_end(args_copy);
  193. }
  194. }
  195. return 0;
  196. }
  197. int monolog_log(char * file, int line, char * level, char * format, ...) {
  198. int result;
  199. va_list args;
  200. va_start(args, format);
  201. result = monolog_log_va(file, line, level, format, args);
  202. va_end(args);
  203. return result;
  204. }
  205. /* Log function helper. */
  206. static int monolog_log_to_stdio(
  207. FILE * fout, char * file, int line, char * level, char * format,
  208. va_list args) {
  209. int res;
  210. if (fout) {
  211. fprintf(fout, "%s: %s: %d: ", level, file, line);
  212. res = vfprintf(fout, format, args);
  213. if (format[strlen(format) - 1] != '\n') {
  214. fputc('\n', fout);
  215. }
  216. return res;
  217. }
  218. return -1;
  219. }
  220. /* Log function for logger that logs to stdout. */
  221. int monolog_stdout_logger
  222. (char * file, int line, char * level, void * data, char * format, va_list args)
  223. {
  224. (void) data;
  225. return monolog_log_to_stdio(stdout, file, line, level, format, args);
  226. }
  227. /* Log function for logger that logs to stderr. */
  228. int monolog_stderr_logger
  229. (char * file, int line, char * level, void * data, char * format, va_list args)
  230. {
  231. int res;
  232. (void) data;
  233. return monolog_log_to_stdio(stderr, file, line, level, format, args);
  234. }
  235. /** Log function for logger that logs to a FILE. */
  236. int monolog_file_logger
  237. (char * file, int line, char * level, void * data, char * format, va_list args) {
  238. int res;
  239. FILE * fout = data;
  240. return monolog_log_to_stdio(fout, file, line, level, format, args);
  241. }
  242. /** Free function for logger that logs to a FILE. */
  243. void monolog_file_free(void * data) {
  244. FILE * fout = data;
  245. if (fout) {
  246. fclose(fout);
  247. }
  248. }