hlog.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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. #pragma warning (disable: 4244) // conversion loss of data
  10. #include <windows.h>
  11. #define hmutex_t CRITICAL_SECTION
  12. #define hmutex_init InitializeCriticalSection
  13. #define hmutex_destroy DeleteCriticalSection
  14. #define hmutex_lock EnterCriticalSection
  15. #define hmutex_unlock LeaveCriticalSection
  16. #else
  17. #include <sys/time.h> // for gettimeofday
  18. #include <pthread.h>
  19. #define hmutex_t pthread_mutex_t
  20. #define hmutex_init(mutex) pthread_mutex_init(mutex, NULL)
  21. #define hmutex_destroy pthread_mutex_destroy
  22. #define hmutex_lock pthread_mutex_lock
  23. #define hmutex_unlock pthread_mutex_unlock
  24. #endif
  25. //#include "htime.h"
  26. #define SECONDS_PER_HOUR 3600
  27. #define SECONDS_PER_DAY 86400 // 24*3600
  28. #define SECONDS_PER_WEEK 604800 // 7*24*3600;
  29. static int s_gmtoff = 28800; // 8*3600
  30. struct logger_s {
  31. logger_handler handler;
  32. unsigned int bufsize;
  33. char* buf;
  34. int level;
  35. int enable_color;
  36. char format[64];
  37. // for file logger
  38. char filepath[256];
  39. unsigned long long max_filesize;
  40. int remain_days;
  41. int enable_fsync;
  42. FILE* fp_;
  43. char cur_logfile[256];
  44. time_t last_logfile_ts;
  45. int can_write_cnt;
  46. hmutex_t mutex_; // thread-safe
  47. };
  48. static void logger_init(logger_t* logger) {
  49. logger->handler = NULL;
  50. logger->bufsize = DEFAULT_LOG_MAX_BUFSIZE;
  51. logger->buf = (char*)malloc(logger->bufsize);
  52. logger->level = DEFAULT_LOG_LEVEL;
  53. logger->enable_color = 0;
  54. // NOTE: format is faster 6% than snprintf
  55. // logger->format[0] = '\0';
  56. strncpy(logger->format, DEFAULT_LOG_FORMAT, sizeof(logger->format) - 1);
  57. logger->fp_ = NULL;
  58. logger->max_filesize = DEFAULT_LOG_MAX_FILESIZE;
  59. logger->remain_days = DEFAULT_LOG_REMAIN_DAYS;
  60. logger->enable_fsync = 1;
  61. logger_set_file(logger, DEFAULT_LOG_FILE);
  62. logger->last_logfile_ts = 0;
  63. logger->can_write_cnt = -1;
  64. hmutex_init(&logger->mutex_);
  65. }
  66. logger_t* logger_create() {
  67. // init gmtoff here
  68. time_t ts = time(NULL);
  69. struct tm* local_tm = localtime(&ts);
  70. int local_hour = local_tm->tm_hour;
  71. struct tm* gmt_tm = gmtime(&ts);
  72. int gmt_hour = gmt_tm->tm_hour;
  73. s_gmtoff = (local_hour - gmt_hour) * SECONDS_PER_HOUR;
  74. logger_t* logger = (logger_t*)malloc(sizeof(logger_t));
  75. logger_init(logger);
  76. return logger;
  77. }
  78. void logger_destroy(logger_t* logger) {
  79. if (logger) {
  80. if (logger->buf) {
  81. free(logger->buf);
  82. logger->buf = NULL;
  83. }
  84. if (logger->fp_) {
  85. fclose(logger->fp_);
  86. logger->fp_ = NULL;
  87. }
  88. hmutex_destroy(&logger->mutex_);
  89. free(logger);
  90. }
  91. }
  92. void logger_set_handler(logger_t* logger, logger_handler fn) {
  93. logger->handler = fn;
  94. }
  95. void logger_set_level(logger_t* logger, int level) {
  96. logger->level = level;
  97. }
  98. void logger_set_level_by_str(logger_t* logger, const char* szLoglevel) {
  99. int loglevel = DEFAULT_LOG_LEVEL;
  100. if (strcmp(szLoglevel, "VERBOSE") == 0) {
  101. loglevel = LOG_LEVEL_VERBOSE;
  102. } else if (strcmp(szLoglevel, "DEBUG") == 0) {
  103. loglevel = LOG_LEVEL_DEBUG;
  104. } else if (strcmp(szLoglevel, "INFO") == 0) {
  105. loglevel = LOG_LEVEL_INFO;
  106. } else if (strcmp(szLoglevel, "WARN") == 0) {
  107. loglevel = LOG_LEVEL_WARN;
  108. } else if (strcmp(szLoglevel, "ERROR") == 0) {
  109. loglevel = LOG_LEVEL_ERROR;
  110. } else if (strcmp(szLoglevel, "FATAL") == 0) {
  111. loglevel = LOG_LEVEL_FATAL;
  112. } else if (strcmp(szLoglevel, "SILENT") == 0) {
  113. loglevel = LOG_LEVEL_SILENT;
  114. } else {
  115. loglevel = DEFAULT_LOG_LEVEL;
  116. }
  117. logger->level = loglevel;
  118. }
  119. void logger_set_format(logger_t* logger, const char* format) {
  120. if (format) {
  121. strncpy(logger->format, format, sizeof(logger->format) - 1);
  122. } else {
  123. logger->format[0] = '\0';
  124. }
  125. }
  126. void logger_set_remain_days(logger_t* logger, int days) {
  127. logger->remain_days = days;
  128. }
  129. void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize) {
  130. logger->bufsize = bufsize;
  131. logger->buf = (char*)realloc(logger->buf, bufsize);
  132. }
  133. void logger_enable_color(logger_t* logger, int on) {
  134. logger->enable_color = on;
  135. }
  136. void logger_set_file(logger_t* logger, const char* filepath) {
  137. strncpy(logger->filepath, filepath, sizeof(logger->filepath) - 1);
  138. // remove suffix .log
  139. char* suffix = strrchr(logger->filepath, '.');
  140. if (suffix && strcmp(suffix, ".log") == 0) {
  141. *suffix = '\0';
  142. }
  143. }
  144. void logger_set_max_filesize(logger_t* logger, unsigned long long filesize) {
  145. logger->max_filesize = filesize;
  146. }
  147. void logger_set_max_filesize_by_str(logger_t* logger, const char* str) {
  148. int num = atoi(str);
  149. if (num <= 0) return;
  150. // 16 16M 16MB
  151. const char* e = str;
  152. while (*e != '\0') ++e;
  153. --e;
  154. char unit;
  155. if (*e >= '0' && *e <= '9') unit = 'M';
  156. else if (*e == 'B') unit = *(e-1);
  157. else unit = *e;
  158. unsigned long long filesize = num;
  159. switch (unit) {
  160. case 'K': filesize <<= 10; break;
  161. case 'M': filesize <<= 20; break;
  162. case 'G': filesize <<= 30; break;
  163. default: filesize <<= 20; break;
  164. }
  165. logger->max_filesize = filesize;
  166. }
  167. void logger_enable_fsync(logger_t* logger, int on) {
  168. logger->enable_fsync = on;
  169. }
  170. void logger_fsync(logger_t* logger) {
  171. hmutex_lock(&logger->mutex_);
  172. if (logger->fp_) {
  173. fflush(logger->fp_);
  174. }
  175. hmutex_unlock(&logger->mutex_);
  176. }
  177. const char* logger_get_cur_file(logger_t* logger) {
  178. return logger->cur_logfile;
  179. }
  180. static void logfile_name(const char* filepath, time_t ts, char* buf, int len) {
  181. struct tm* tm = localtime(&ts);
  182. snprintf(buf, len, "%s.%04d%02d%02d.log",
  183. filepath,
  184. tm->tm_year+1900,
  185. tm->tm_mon+1,
  186. tm->tm_mday);
  187. }
  188. static FILE* logfile_shift(logger_t* logger) {
  189. time_t ts_now = time(NULL);
  190. 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;
  191. if (logger->fp_ == NULL || interval_days > 0) {
  192. // close old logfile
  193. if (logger->fp_) {
  194. fclose(logger->fp_);
  195. logger->fp_ = NULL;
  196. }
  197. else {
  198. interval_days = 30;
  199. }
  200. if (logger->remain_days >= 0) {
  201. char rm_logfile[256] = {0};
  202. if (interval_days >= logger->remain_days) {
  203. // remove [today-interval_days, today-remain_days] logfile
  204. for (int i = interval_days; i >= logger->remain_days; --i) {
  205. time_t ts_rm = ts_now - i * SECONDS_PER_DAY;
  206. logfile_name(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
  207. remove(rm_logfile);
  208. }
  209. }
  210. else {
  211. // remove today-remain_days logfile
  212. time_t ts_rm = ts_now - logger->remain_days * SECONDS_PER_DAY;
  213. logfile_name(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
  214. remove(rm_logfile);
  215. }
  216. }
  217. }
  218. // open today logfile
  219. if (logger->fp_ == NULL) {
  220. logfile_name(logger->filepath, ts_now, logger->cur_logfile, sizeof(logger->cur_logfile));
  221. logger->fp_ = fopen(logger->cur_logfile, "a");
  222. logger->last_logfile_ts = ts_now;
  223. }
  224. // NOTE: estimate can_write_cnt to avoid frequent fseek/ftell
  225. if (logger->fp_ && --logger->can_write_cnt < 0) {
  226. fseek(logger->fp_, 0, SEEK_END);
  227. long filesize = ftell(logger->fp_);
  228. if (filesize > logger->max_filesize) {
  229. fclose(logger->fp_);
  230. logger->fp_ = NULL;
  231. // ftruncate
  232. logger->fp_ = fopen(logger->cur_logfile, "w");
  233. // reopen with O_APPEND for multi-processes
  234. if (logger->fp_) {
  235. fclose(logger->fp_);
  236. logger->fp_ = fopen(logger->cur_logfile, "a");
  237. }
  238. }
  239. else {
  240. logger->can_write_cnt = (logger->max_filesize - filesize) / logger->bufsize;
  241. }
  242. }
  243. return logger->fp_;
  244. }
  245. static void logfile_write(logger_t* logger, const char* buf, int len) {
  246. FILE* fp = logfile_shift(logger);
  247. if (fp) {
  248. fwrite(buf, 1, len, fp);
  249. if (logger->enable_fsync) {
  250. fflush(fp);
  251. }
  252. }
  253. }
  254. static int i2a(int i, char* buf, int len) {
  255. for (int l = len - 1; l >= 0; --l) {
  256. if (i == 0) {
  257. buf[l] = '0';
  258. } else {
  259. buf[l] = i % 10 + '0';
  260. i /= 10;
  261. }
  262. }
  263. return len;
  264. }
  265. int logger_print(logger_t* logger, int level, const char* fmt, ...) {
  266. if (level < logger->level)
  267. return -10;
  268. int year,month,day,hour,min,sec,us;
  269. #ifdef _WIN32
  270. SYSTEMTIME tm;
  271. GetLocalTime(&tm);
  272. year = tm.wYear;
  273. month = tm.wMonth;
  274. day = tm.wDay;
  275. hour = tm.wHour;
  276. min = tm.wMinute;
  277. sec = tm.wSecond;
  278. us = tm.wMilliseconds * 1000;
  279. #else
  280. struct timeval tv;
  281. struct tm* tm = NULL;
  282. gettimeofday(&tv, NULL);
  283. time_t tt = tv.tv_sec;
  284. tm = localtime(&tt);
  285. year = tm->tm_year + 1900;
  286. month = tm->tm_mon + 1;
  287. day = tm->tm_mday;
  288. hour = tm->tm_hour;
  289. min = tm->tm_min;
  290. sec = tm->tm_sec;
  291. us = tv.tv_usec;
  292. #endif
  293. const char* pcolor = "";
  294. const char* plevel = "";
  295. #define XXX(id, str, clr) \
  296. case id: plevel = str; pcolor = clr; break;
  297. switch (level) {
  298. LOG_LEVEL_MAP(XXX)
  299. }
  300. #undef XXX
  301. // lock logger->buf
  302. hmutex_lock(&logger->mutex_);
  303. char* buf = logger->buf;
  304. int bufsize = logger->bufsize;
  305. int len = 0;
  306. if (logger->enable_color) {
  307. len = snprintf(buf, bufsize, "%s", pcolor);
  308. }
  309. const char* p = logger->format;
  310. if (*p) {
  311. while (*p) {
  312. if (*p == '%') {
  313. switch(*++p) {
  314. case 'y':
  315. len += i2a(year, buf + len, 4);
  316. break;
  317. case 'm':
  318. len += i2a(month, buf + len, 2);
  319. break;
  320. case 'd':
  321. len += i2a(day, buf + len, 2);
  322. break;
  323. case 'H':
  324. len += i2a(hour, buf + len, 2);
  325. break;
  326. case 'M':
  327. len += i2a(min, buf + len, 2);
  328. break;
  329. case 'S':
  330. len += i2a(sec, buf + len, 2);
  331. break;
  332. case 'z':
  333. len += i2a(us/1000, buf + len, 3);
  334. break;
  335. case 'Z':
  336. len += i2a(us, buf + len, 6);
  337. break;
  338. case 'l':
  339. buf[len++] = *plevel;
  340. break;
  341. case 'L':
  342. for (int i = 0; i < 5; ++i) {
  343. buf[len++] = plevel[i];
  344. }
  345. break;
  346. case 's':
  347. {
  348. va_list ap;
  349. va_start(ap, fmt);
  350. len += vsnprintf(buf + len, bufsize - len, fmt, ap);
  351. va_end(ap);
  352. }
  353. break;
  354. case '%':
  355. buf[len++] = '%';
  356. break;
  357. default: break;
  358. }
  359. } else {
  360. buf[len++] = *p;
  361. }
  362. ++p;
  363. }
  364. } else {
  365. len += snprintf(buf + len, bufsize - len, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s ",
  366. year, month, day, hour, min, sec, us/1000,
  367. plevel);
  368. va_list ap;
  369. va_start(ap, fmt);
  370. len += vsnprintf(buf + len, bufsize - len, fmt, ap);
  371. va_end(ap);
  372. }
  373. if (logger->enable_color) {
  374. len += snprintf(buf + len, bufsize - len, "%s", CLR_CLR);
  375. }
  376. if (logger->handler) {
  377. logger->handler(level, buf, len);
  378. }
  379. else {
  380. logfile_write(logger, buf, len);
  381. }
  382. hmutex_unlock(&logger->mutex_);
  383. return len;
  384. }
  385. static logger_t* s_logger = NULL;
  386. logger_t* hv_default_logger() {
  387. if (s_logger == NULL) {
  388. s_logger = logger_create();
  389. atexit(hv_destroy_default_logger);
  390. }
  391. return s_logger;
  392. }
  393. void hv_destroy_default_logger() {
  394. if (s_logger) {
  395. logger_fsync(s_logger);
  396. logger_destroy(s_logger);
  397. s_logger = NULL;
  398. }
  399. }
  400. void stdout_logger(int loglevel, const char* buf, int len) {
  401. fprintf(stdout, "%.*s", len, buf);
  402. }
  403. void stderr_logger(int loglevel, const char* buf, int len) {
  404. fprintf(stderr, "%.*s", len, buf);
  405. }
  406. void file_logger(int loglevel, const char* buf, int len) {
  407. logfile_write(hv_default_logger(), buf, len);
  408. }