monolog.c 7.1 KB

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