hlog.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #include "hlog.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdarg.h>
  5. #include "htime.h" // for get_datetime
  6. static int s_initialized = 0;
  7. static hlog_handler s_logger = DEFAULT_LOGGER;
  8. static char s_logfile[256] = DEFAULT_LOG_FILE;
  9. static int s_loglevel = DEFAULT_LOG_LEVEL;
  10. static bool s_logcolor = false;
  11. static bool s_fflush = true;
  12. static int s_remain_days = DEFAULT_LOG_REMAIN_DAYS;
  13. static char s_logbuf[LOG_BUFSIZE];
  14. // for thread-safe
  15. #include "hmutex.h"
  16. static hmutex_t s_mutex;
  17. void hlog_set_logger(hlog_handler fn) {
  18. s_logger = fn;
  19. }
  20. void hlog_set_level(int level) {
  21. s_loglevel = level;
  22. }
  23. void hlog_set_remain_days(int days) {
  24. s_remain_days = days;
  25. }
  26. int hlog_printf(int level, const char* fmt, ...) {
  27. if (level < s_loglevel)
  28. return -10;
  29. datetime_t now = datetime_now();
  30. const char* pcolor = "";
  31. const char* plevel = "";
  32. #define CASE_LOG(id, str, clr) \
  33. case id: plevel = str; pcolor = clr; break;
  34. switch (level) {
  35. FOREACH_LOG(CASE_LOG)
  36. }
  37. #undef CASE_LOG
  38. if (!s_logcolor) {
  39. pcolor = "";
  40. }
  41. if (!s_initialized) {
  42. s_initialized = 1;
  43. hmutex_init(&s_mutex);
  44. }
  45. // lock s_logbuf, s_logger
  46. hmutex_lock(&s_mutex);
  47. int len = snprintf(s_logbuf, LOG_BUFSIZE, "%s[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s]: ",
  48. pcolor,
  49. now.year, now.month, now.day, now.hour, now.min, now.sec, now.ms, plevel);
  50. va_list ap;
  51. va_start(ap, fmt);
  52. len += vsnprintf(s_logbuf + len, LOG_BUFSIZE - len, fmt, ap);
  53. va_end(ap);
  54. if (s_logcolor) {
  55. len += snprintf(s_logbuf + len, LOG_BUFSIZE - len, "%s", CL_CLR);
  56. }
  57. s_logger(level, s_logbuf, len);
  58. hmutex_unlock(&s_mutex);
  59. return len;
  60. }
  61. void hlog_enable_color(int on) {
  62. s_logcolor = on;
  63. }
  64. void stdout_logger(int loglevel, const char* buf, int len) {
  65. fprintf(stdout, "%s", buf);
  66. }
  67. void stderr_logger(int loglevel, const char* buf, int len) {
  68. fprintf(stderr, "%s", buf);
  69. }
  70. static void ts_logfile(time_t ts, char* buf, int len) {
  71. struct tm* tm = localtime(&ts);
  72. snprintf(buf, len, "%s-%04d-%02d-%02d.log",
  73. s_logfile,
  74. tm->tm_year+1900,
  75. tm->tm_mon+1,
  76. tm->tm_mday);
  77. }
  78. static FILE* shift_logfile() {
  79. static FILE* s_logfp = NULL;
  80. static char s_cur_logfile[256] = {0};
  81. static time_t s_last_logfile_ts = 0;
  82. time_t ts_now = time(NULL);
  83. int interval_days = s_last_logfile_ts == 0 ? 0 : (ts_now / SECONDS_PER_DAY - s_last_logfile_ts / SECONDS_PER_DAY);;
  84. if (s_logfp == NULL || interval_days > 0) {
  85. // close old logfile
  86. if (s_logfp) {
  87. fclose(s_logfp);
  88. s_logfp = NULL;
  89. }
  90. else {
  91. interval_days = 30;
  92. }
  93. if (interval_days >= s_remain_days) {
  94. // remove [today-interval_days, today-s_remain_days] logfile
  95. char rm_logfile[256] = {0};
  96. for (int i = interval_days; i >= s_remain_days; --i) {
  97. time_t ts_rm = ts_now - i * SECONDS_PER_DAY;
  98. ts_logfile(ts_rm, rm_logfile, sizeof(rm_logfile));
  99. remove(rm_logfile);
  100. }
  101. }
  102. else {
  103. // remove today-s_remain_days logfile
  104. char rm_logfile[256] = {0};
  105. time_t ts_rm = ts_now - s_remain_days * SECONDS_PER_DAY;
  106. ts_logfile(ts_rm, rm_logfile, sizeof(rm_logfile));
  107. remove(rm_logfile);
  108. }
  109. }
  110. // open today logfile
  111. if (s_logfp == NULL) {
  112. ts_logfile(ts_now, s_cur_logfile, sizeof(s_cur_logfile));
  113. s_logfp = fopen(s_cur_logfile, "a"); // note: append-mode for multi-processes
  114. s_last_logfile_ts = ts_now;
  115. }
  116. // rewrite if too big
  117. if (s_logfp && ftell(s_logfp) > MAX_LOG_FILESIZE) {
  118. fclose(s_logfp);
  119. s_logfp = NULL;
  120. s_logfp = fopen(s_cur_logfile, "w");
  121. }
  122. return s_logfp;
  123. }
  124. void file_logger(int loglevel, const char* buf, int len) {
  125. FILE* fp = shift_logfile();
  126. if (fp == NULL) return;
  127. fprintf(fp, "%s", buf);
  128. if (s_fflush) {
  129. fflush(fp);
  130. }
  131. }
  132. int hlog_set_file(const char* logfile) {
  133. if (logfile == NULL || strlen(logfile) == 0) return -10;
  134. strncpy(s_logfile, logfile, sizeof(s_logfile));
  135. // remove suffix .log
  136. char* suffix = strrchr(s_logfile, '.');
  137. if (suffix && strcmp(suffix, ".log") == 0) {
  138. *suffix = '\0';
  139. }
  140. return 0;
  141. }
  142. void hlog_set_fflush(int on) {
  143. s_fflush = on;
  144. }
  145. void hlog_fflush() {
  146. hmutex_lock(&s_mutex);
  147. FILE* fp = shift_logfile();
  148. if (fp) {
  149. fflush(fp);
  150. }
  151. hmutex_unlock(&s_mutex);
  152. }