1
0

hlog.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #include "hlog.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdarg.h>
  6. #include <time.h>
  7. //#include "hmutex.h"
  8. #ifdef _WIN32
  9. #include <windows.h>
  10. #define hmutex_t CRITICAL_SECTION
  11. #define hmutex_init InitializeCriticalSection
  12. #define hmutex_destroy DeleteCriticalSection
  13. #define hmutex_lock EnterCriticalSection
  14. #define hmutex_unlock LeaveCriticalSection
  15. #else
  16. #include <sys/time.h> // for gettimeofday
  17. #include <pthread.h>
  18. #define hmutex_t pthread_mutex_t
  19. #define hmutex_init(mutex) pthread_mutex_init(mutex, NULL)
  20. #define hmutex_destroy pthread_mutex_destroy
  21. #define hmutex_lock pthread_mutex_lock
  22. #define hmutex_unlock pthread_mutex_unlock
  23. #endif
  24. //#include "htime.h"
  25. #define SECONDS_PER_HOUR 3600
  26. #define SECONDS_PER_DAY 86400 // 24*3600
  27. #define SECONDS_PER_WEEK 604800 // 7*24*3600;
  28. static int s_gmtoff = 28800; // 8*3600
  29. struct logger_s {
  30. logger_handler handler;
  31. unsigned int bufsize;
  32. char* buf;
  33. int level;
  34. int enable_color;
  35. // for file logger
  36. char filepath[256];
  37. unsigned long long max_filesize;
  38. int remain_days;
  39. int enable_fsync;
  40. FILE* fp_;
  41. char cur_logfile[256];
  42. time_t last_logfile_ts;
  43. int can_write_cnt;
  44. hmutex_t mutex_; // thread-safe
  45. };
  46. static void logger_init(logger_t* logger) {
  47. logger->handler = NULL;
  48. logger->bufsize = DEFAULT_LOG_MAX_BUFSIZE;
  49. logger->buf = (char*)malloc(logger->bufsize);
  50. logger->level = DEFAULT_LOG_LEVEL;
  51. logger->enable_color = 0;
  52. logger->fp_ = NULL;
  53. logger->max_filesize = DEFAULT_LOG_MAX_FILESIZE;
  54. logger->remain_days = DEFAULT_LOG_REMAIN_DAYS;
  55. logger->enable_fsync = 1;
  56. logger_set_file(logger, DEFAULT_LOG_FILE);
  57. logger->last_logfile_ts = 0;
  58. logger->can_write_cnt = -1;
  59. hmutex_init(&logger->mutex_);
  60. }
  61. logger_t* logger_create() {
  62. // init gmtoff here
  63. time_t ts = time(NULL);
  64. struct tm* local_tm = localtime(&ts);
  65. int local_hour = local_tm->tm_hour;
  66. struct tm* gmt_tm = gmtime(&ts);
  67. int gmt_hour = gmt_tm->tm_hour;
  68. s_gmtoff = (local_hour - gmt_hour) * SECONDS_PER_HOUR;
  69. logger_t* logger = (logger_t*)malloc(sizeof(logger_t));
  70. logger_init(logger);
  71. return logger;
  72. }
  73. void logger_destroy(logger_t* logger) {
  74. if (logger) {
  75. if (logger->buf) {
  76. free(logger->buf);
  77. }
  78. hmutex_destroy(&logger->mutex_);
  79. free(logger);
  80. }
  81. }
  82. void logger_set_handler(logger_t* logger, logger_handler fn) {
  83. logger->handler = fn;
  84. }
  85. void logger_set_level(logger_t* logger, int level) {
  86. logger->level = level;
  87. }
  88. void logger_set_remain_days(logger_t* logger, int days) {
  89. logger->remain_days = days;
  90. }
  91. void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize) {
  92. logger->bufsize = bufsize;
  93. logger->buf = (char*)realloc(logger->buf, bufsize);
  94. }
  95. void logger_enable_color(logger_t* logger, int on) {
  96. logger->enable_color = on;
  97. }
  98. void logger_set_file(logger_t* logger, const char* filepath) {
  99. strncpy(logger->filepath, filepath, sizeof(logger->filepath));
  100. // remove suffix .log
  101. char* suffix = strrchr(logger->filepath, '.');
  102. if (suffix && strcmp(suffix, ".log") == 0) {
  103. *suffix = '\0';
  104. }
  105. }
  106. void logger_set_max_filesize(logger_t* logger, unsigned long long filesize) {
  107. logger->max_filesize = filesize;
  108. }
  109. void logger_enable_fsync(logger_t* logger, int on) {
  110. logger->enable_fsync = on;
  111. }
  112. void logger_fsync(logger_t* logger) {
  113. hmutex_lock(&logger->mutex_);
  114. if (logger->fp_) {
  115. fflush(logger->fp_);
  116. }
  117. hmutex_unlock(&logger->mutex_);
  118. }
  119. static void ts_logfile(const char* filepath, time_t ts, char* buf, int len) {
  120. struct tm* tm = localtime(&ts);
  121. snprintf(buf, len, "%s-%04d-%02d-%02d.log",
  122. filepath,
  123. tm->tm_year+1900,
  124. tm->tm_mon+1,
  125. tm->tm_mday);
  126. }
  127. static FILE* shift_logfile(logger_t* logger) {
  128. time_t ts_now = time(NULL);
  129. int interval_days = logger->last_logfile_ts == 0 ? 0 : (ts_now+s_gmtoff) / SECONDS_PER_DAY - (logger->last_logfile_ts+s_gmtoff) / SECONDS_PER_DAY;
  130. if (logger->fp_ == NULL || interval_days > 0) {
  131. // close old logfile
  132. if (logger->fp_) {
  133. fclose(logger->fp_);
  134. logger->fp_ = NULL;
  135. }
  136. else {
  137. interval_days = 30;
  138. }
  139. if (logger->remain_days >= 0) {
  140. char rm_logfile[256] = {0};
  141. if (interval_days >= logger->remain_days) {
  142. // remove [today-interval_days, today-remain_days] logfile
  143. for (int i = interval_days; i >= logger->remain_days; --i) {
  144. time_t ts_rm = ts_now - i * SECONDS_PER_DAY;
  145. ts_logfile(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
  146. remove(rm_logfile);
  147. }
  148. }
  149. else {
  150. // remove today-remain_days logfile
  151. time_t ts_rm = ts_now - logger->remain_days * SECONDS_PER_DAY;
  152. ts_logfile(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
  153. remove(rm_logfile);
  154. }
  155. }
  156. }
  157. // open today logfile
  158. if (logger->fp_ == NULL) {
  159. ts_logfile(logger->filepath, ts_now, logger->cur_logfile, sizeof(logger->cur_logfile));
  160. logger->fp_ = fopen(logger->cur_logfile, "a");
  161. logger->last_logfile_ts = ts_now;
  162. }
  163. // NOTE: estimate can_write_cnt to avoid frequent fseek/ftell
  164. if (logger->fp_ && --logger->can_write_cnt < 0) {
  165. fseek(logger->fp_, 0, SEEK_END);
  166. long filesize = ftell(logger->fp_);
  167. if (filesize > logger->max_filesize) {
  168. fclose(logger->fp_);
  169. logger->fp_ = NULL;
  170. // ftruncate
  171. logger->fp_ = fopen(logger->cur_logfile, "w");
  172. // reopen with O_APPEND for multi-processes
  173. if (logger->fp_) {
  174. fclose(logger->fp_);
  175. logger->fp_ = fopen(logger->cur_logfile, "a");
  176. }
  177. }
  178. else {
  179. logger->can_write_cnt = (logger->max_filesize - filesize) / logger->bufsize;
  180. }
  181. }
  182. return logger->fp_;
  183. }
  184. int logger_print(logger_t* logger, int level, const char* fmt, ...) {
  185. if (level < logger->level)
  186. return -10;
  187. int year,month,day,hour,min,sec,ms;
  188. #ifdef _WIN32
  189. SYSTEMTIME tm;
  190. GetLocalTime(&tm);
  191. year = tm.wYear;
  192. month = tm.wMonth;
  193. day = tm.wDay;
  194. hour = tm.wHour;
  195. min = tm.wMinute;
  196. sec = tm.wSecond;
  197. ms = tm.wMilliseconds;
  198. #else
  199. struct timeval tv;
  200. struct tm* tm = NULL;
  201. gettimeofday(&tv, NULL);
  202. time_t tt = tv.tv_sec;
  203. tm = localtime(&tt);
  204. year = tm->tm_year + 1900;
  205. month = tm->tm_mon + 1;
  206. day = tm->tm_mday;
  207. hour = tm->tm_hour;
  208. min = tm->tm_min;
  209. sec = tm->tm_sec;
  210. ms = tv.tv_usec/1000;
  211. #endif
  212. const char* pcolor = "";
  213. const char* plevel = "";
  214. #define XXX(id, str, clr) \
  215. case id: plevel = str; pcolor = clr; break;
  216. switch (level) {
  217. LOG_LEVEL_MAP(XXX)
  218. }
  219. #undef XXX
  220. if (!logger->enable_color) {
  221. pcolor = "";
  222. }
  223. // lock logger->buf
  224. hmutex_lock(&logger->mutex_);
  225. char* buf = logger->buf;
  226. int bufsize = logger->bufsize;
  227. int len = snprintf(buf, bufsize, "%s[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s] ",
  228. pcolor,
  229. year, month, day, hour, min, sec, ms,
  230. plevel);
  231. va_list ap;
  232. va_start(ap, fmt);
  233. len += vsnprintf(buf + len, bufsize - len, fmt, ap);
  234. va_end(ap);
  235. if (logger->enable_color) {
  236. len += snprintf(buf + len, bufsize - len, "%s", CLR_CLR);
  237. }
  238. if (logger->handler) {
  239. logger->handler(level, buf, len);
  240. }
  241. else {
  242. FILE* fp = shift_logfile(logger);
  243. if (fp) {
  244. fwrite(buf, 1, len, fp);
  245. if (logger->enable_fsync) {
  246. fflush(fp);
  247. }
  248. }
  249. }
  250. hmutex_unlock(&logger->mutex_);
  251. return len;
  252. }
  253. logger_t* default_logger() {
  254. static logger_t* s_logger = NULL;
  255. if (s_logger == NULL) {
  256. s_logger = logger_create();
  257. }
  258. return s_logger;
  259. }
  260. void stdout_logger(int loglevel, const char* buf, int len) {
  261. fprintf(stdout, "%.*s", len, buf);
  262. }
  263. void stderr_logger(int loglevel, const char* buf, int len) {
  264. fprintf(stderr, "%.*s", len, buf);
  265. }
  266. void file_logger(int loglevel, const char* buf, int len) {
  267. logger_t* logger = default_logger();
  268. FILE* fp = shift_logfile(logger);
  269. if (fp) {
  270. fwrite(buf, 1, len, fp);
  271. if (logger->enable_fsync) {
  272. fflush(fp);
  273. }
  274. }
  275. }