hlog.c 15 KB

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