tlog.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. /*
  2. * Copyright (C) 2018 Ruilin Peng (Nick) <[email protected]>
  3. */
  4. #include "tlog.h"
  5. #include <dirent.h>
  6. #include <errno.h>
  7. #include <fcntl.h>
  8. #include <limits.h>
  9. #include <pthread.h>
  10. #include <stdarg.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <sys/stat.h>
  15. #include <sys/time.h>
  16. #include <sys/types.h>
  17. #include <sys/wait.h>
  18. #include <unistd.h>
  19. #define TLOG_BUFF_SIZE (1024 * 128)
  20. #define TLOG_MAX_LINE_LEN (1024)
  21. #define TLOG_TMP_LEN 128
  22. #define TLOG_LOG_SIZE (1024 * 1024 * 50)
  23. #define TLOG_LOG_COUNT 32
  24. struct oldest_log {
  25. char name[TLOG_TMP_LEN];
  26. time_t mtime;
  27. };
  28. struct tlog {
  29. char *buff;
  30. int buffsize;
  31. int start;
  32. int end;
  33. int ext_end;
  34. int run;
  35. pthread_t tid;
  36. pthread_mutex_t lock;
  37. pthread_cond_t cond;
  38. pthread_cond_t client_cond;
  39. int waiters;
  40. int is_wait;
  41. int fd;
  42. int fd_lock;
  43. off_t filesize;
  44. char logdir[PATH_MAX];
  45. char logname[PATH_MAX];
  46. int logsize;
  47. int logcount;
  48. int block;
  49. int dropped;
  50. int zip_pid;
  51. int multi_log;
  52. int logscreen;
  53. };
  54. typedef int (*list_callback)(const char *name, struct dirent *entry, void *user);
  55. struct tlog tlog;
  56. static tlog_level tlog_set_level = TLOG_INFO;
  57. tlog_format_func tlog_format;
  58. static unsigned int tlog_localtime_lock = 0;
  59. static const char *tlog_level_str[] = {
  60. "DEBUG",
  61. "INFO",
  62. "NOTICE",
  63. "WARN",
  64. "ERROR",
  65. "FATAL",
  66. };
  67. static inline void _tlog_spin_lock(unsigned int *lock)
  68. {
  69. while (1) {
  70. int i;
  71. for (i = 0; i < 10000; i++) {
  72. if (__sync_bool_compare_and_swap(lock, 0, 1)) {
  73. return;
  74. }
  75. }
  76. sched_yield();
  77. }
  78. }
  79. static inline void _tlog_spin_unlock(unsigned int *lock)
  80. {
  81. __sync_bool_compare_and_swap(lock, 1, 0);
  82. }
  83. static int _tlog_mkdir(const char *path)
  84. {
  85. char path_c[PATH_MAX];
  86. char *path_end;
  87. char str;
  88. int len;
  89. if (access(path, F_OK) == 0) {
  90. return 0;
  91. }
  92. strncpy(path_c, path, sizeof(path_c) - 1);
  93. len = strnlen(path_c, sizeof(path_c) - 1);
  94. path_c[len] = '/';
  95. path_c[len + 1] = '\0';
  96. path_end = path_c;
  97. /* create directory recursively */
  98. while (*path_end != 0) {
  99. if (*path_end != '/') {
  100. path_end++;
  101. continue;
  102. }
  103. str = *path_end;
  104. *path_end = '\0';
  105. if (access(path_c, F_OK) == 0) {
  106. *path_end = str;
  107. path_end++;
  108. continue;
  109. }
  110. if (mkdir(path_c, 0750) != 0) {
  111. fprintf(stderr, "create directory %s failed, %s\n", path_c, strerror(errno));
  112. return -1;
  113. }
  114. *path_end = str;
  115. path_end++;
  116. }
  117. return 0;
  118. }
  119. static struct tm *_tlog_localtime(time_t *timep, struct tm *tm)
  120. {
  121. static time_t last_time = {0};
  122. static struct tm last_tm = {0};
  123. /* localtime_r has a global timezone lock, it's about 8 times slower than gmtime
  124. * this code is used to speed up localtime_r call.
  125. */
  126. _tlog_spin_lock(&tlog_localtime_lock);
  127. if (*timep == last_time) {
  128. *tm = last_tm;
  129. } else {
  130. _tlog_spin_unlock(&tlog_localtime_lock);
  131. tm = localtime_r(timep, tm);
  132. _tlog_spin_lock(&tlog_localtime_lock);
  133. if (tm) {
  134. last_time = *timep;
  135. last_tm = *tm;
  136. }
  137. }
  138. _tlog_spin_unlock(&tlog_localtime_lock);
  139. return tm;
  140. }
  141. static int _tlog_getmtime(struct tlog_time *log_mtime, const char *file)
  142. {
  143. struct tm tm;
  144. struct stat sb;
  145. if (stat(file, &sb) != 0) {
  146. return -1;
  147. }
  148. if (_tlog_localtime(&sb.st_mtime, &tm) == NULL) {
  149. return -1;
  150. }
  151. log_mtime->year = tm.tm_year + 1900;
  152. log_mtime->mon = tm.tm_mon + 1;
  153. log_mtime->mday = tm.tm_mday;
  154. log_mtime->hour = tm.tm_hour;
  155. log_mtime->min = tm.tm_min;
  156. log_mtime->sec = tm.tm_sec;
  157. log_mtime->usec = 0;
  158. return 0;
  159. }
  160. static int _tlog_gettime(struct tlog_time *cur_time)
  161. {
  162. struct tm tm;
  163. struct timeval tmval;
  164. if (gettimeofday(&tmval, NULL) != 0) {
  165. return -1;
  166. }
  167. if (_tlog_localtime(&tmval.tv_sec, &tm) == NULL) {
  168. return -1;
  169. }
  170. cur_time->year = tm.tm_year + 1900;
  171. cur_time->mon = tm.tm_mon + 1;
  172. cur_time->mday = tm.tm_mday;
  173. cur_time->hour = tm.tm_hour;
  174. cur_time->min = tm.tm_min;
  175. cur_time->sec = tm.tm_sec;
  176. cur_time->usec = tmval.tv_usec;
  177. return 0;
  178. }
  179. static int _tlog_format(char *buff, int maxlen, struct tlog_info *info, void *userptr, const char *format, va_list ap)
  180. {
  181. int len = 0;
  182. int total_len = 0;
  183. struct tlog_time *tm = &info->time;
  184. if (tlog.multi_log) {
  185. /* format prefix */
  186. len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5d][%4s][%17s:%-4d] ",
  187. tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(),
  188. info->level, info->file, info->line);
  189. } else {
  190. /* format prefix */
  191. len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ",
  192. tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000,
  193. info->level, info->file, info->line);
  194. }
  195. if (len < 0 || len == maxlen) {
  196. return -1;
  197. }
  198. buff += len;
  199. total_len += len;
  200. maxlen -= len;
  201. /* format log message */
  202. len = vsnprintf(buff, maxlen, format, ap);
  203. if (len < 0 || len == maxlen) {
  204. return -1;
  205. }
  206. buff += len;
  207. total_len += len;
  208. /* return total length */
  209. return total_len;
  210. }
  211. static int _tlog_log_buffer(char *buff, int maxlen, tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap)
  212. {
  213. int len;
  214. struct tlog_info info;
  215. if (tlog_format == NULL) {
  216. return -1;
  217. }
  218. if (level >= TLOG_END) {
  219. return -1;
  220. }
  221. info.file = file;
  222. info.line = line;
  223. info.func = func;
  224. info.level = tlog_level_str[level];
  225. if (_tlog_gettime(&info.time) != 0) {
  226. return -1;
  227. }
  228. len = tlog_format(buff, maxlen, &info, userptr, format, ap);
  229. if (len < 0) {
  230. return -1;
  231. }
  232. /* add new line character*/
  233. if (*(buff + len - 1) != '\n' && len + 1 < maxlen - len) {
  234. *(buff + len) = '\n';
  235. len++;
  236. }
  237. return len;
  238. }
  239. int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap)
  240. {
  241. int len;
  242. int maxlen = 0;
  243. if (tlog.buff == NULL) {
  244. return -1;
  245. }
  246. if (level < tlog_set_level) {
  247. return 0;
  248. }
  249. pthread_mutex_lock(&tlog.lock);
  250. do {
  251. if (tlog.end == tlog.start) {
  252. if (tlog.ext_end == 0) {
  253. /* if buffer is empty */
  254. maxlen = tlog.buffsize - tlog.end;
  255. }
  256. } else if (tlog.end > tlog.start) {
  257. maxlen = tlog.buffsize - tlog.end;
  258. } else {
  259. /* if reverse */
  260. maxlen = tlog.start - tlog.end;
  261. }
  262. /* if free buffer length is less than min line length */
  263. if (maxlen < TLOG_MAX_LINE_LEN) {
  264. if (tlog.end != tlog.start) {
  265. pthread_cond_signal(&tlog.cond);
  266. }
  267. /* if drop message, increase statistics and return */
  268. if (tlog.block == 0) {
  269. tlog.dropped++;
  270. pthread_mutex_unlock(&tlog.lock);
  271. return -1;
  272. }
  273. tlog.waiters++;
  274. /* block wait for free buffer */
  275. int ret = pthread_cond_wait(&tlog.client_cond, &tlog.lock);
  276. tlog.waiters--;
  277. if (ret < 0) {
  278. pthread_mutex_unlock(&tlog.lock);
  279. return -1;
  280. }
  281. }
  282. } while (maxlen < TLOG_MAX_LINE_LEN);
  283. /* write log to buffer */
  284. len = _tlog_log_buffer(tlog.buff + tlog.end, maxlen, level, file, line, func, userptr, format, ap);
  285. if (len <= 0) {
  286. pthread_mutex_unlock(&tlog.lock);
  287. return -1;
  288. }
  289. tlog.end += len;
  290. /* if remain buffer is not enough for a line, move end to start of buffer. */
  291. if (tlog.end > tlog.buffsize - TLOG_MAX_LINE_LEN) {
  292. tlog.ext_end = tlog.end;
  293. tlog.end = 0;
  294. }
  295. if (tlog.is_wait) {
  296. pthread_cond_signal(&tlog.cond);
  297. }
  298. pthread_mutex_unlock(&tlog.lock);
  299. return len;
  300. }
  301. int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr,
  302. const char *format, ...)
  303. {
  304. int len;
  305. va_list ap;
  306. va_start(ap, format);
  307. len = tlog_vext(level, file, line, func, userptr, format, ap);
  308. va_end(ap);
  309. return len;
  310. }
  311. static int _tlog_rename_logfile(const char *gzip_file)
  312. {
  313. char archive_file[PATH_MAX];
  314. struct tlog_time logtime;
  315. int i = 0;
  316. if (_tlog_getmtime(&logtime, gzip_file) != 0) {
  317. return -1;
  318. }
  319. snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d.gz",
  320. tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday,
  321. logtime.hour, logtime.min, logtime.sec);
  322. while (access(archive_file, F_OK) == 0) {
  323. i++;
  324. snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d-%d.gz",
  325. tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday,
  326. logtime.hour, logtime.min, logtime.sec, i);
  327. }
  328. if (rename(gzip_file, archive_file) != 0) {
  329. return -1;
  330. }
  331. return 0;
  332. }
  333. static int _tlog_list_dir(const char *path, list_callback callback, void *userptr)
  334. {
  335. DIR *dir = NULL;
  336. struct dirent *ent;
  337. int ret = 0;
  338. dir = opendir(path);
  339. if (dir == NULL) {
  340. fprintf(stderr, "open directory failed, %s\n", strerror(errno));
  341. goto errout;
  342. }
  343. while ((ent = readdir(dir)) != NULL) {
  344. if (strncmp(".", ent->d_name, 2) == 0 || strncmp("..", ent->d_name, 3) == 0) {
  345. continue;
  346. }
  347. ret = callback(path, ent, userptr);
  348. if (ret != 0) {
  349. goto errout;
  350. }
  351. }
  352. closedir(dir);
  353. return 0;
  354. errout:
  355. if (dir) {
  356. closedir(dir);
  357. dir = NULL;
  358. }
  359. return -1;
  360. }
  361. static int _tlog_count_log_callback(const char *path, struct dirent *entry, void *userptr)
  362. {
  363. int *lognum = (int *)userptr;
  364. if (strstr(entry->d_name, ".gz") == NULL) {
  365. return 0;
  366. }
  367. int len = strnlen(tlog.logname, sizeof(tlog.logname));
  368. if (strncmp(tlog.logname, entry->d_name, len) != 0) {
  369. return 0;
  370. }
  371. (*lognum)++;
  372. return 0;
  373. }
  374. static int _tlog_get_oldest_callback(const char *path, struct dirent *entry, void *userptr)
  375. {
  376. struct stat sb;
  377. char filename[PATH_MAX];
  378. struct oldest_log *oldestlog = userptr;
  379. /* if not a gz file, skip */
  380. if (strstr(entry->d_name, ".gz") == NULL) {
  381. return 0;
  382. }
  383. /* if not tlog gz file, skip */
  384. int len = strnlen(tlog.logname, sizeof(tlog.logname));
  385. if (strncmp(tlog.logname, entry->d_name, len) != 0) {
  386. return 0;
  387. }
  388. /* get log file mtime */
  389. snprintf(filename, sizeof(filename), "%s/%s", path, entry->d_name);
  390. if (stat(filename, &sb) != 0) {
  391. return -1;
  392. }
  393. if (oldestlog->mtime == 0 || oldestlog->mtime > sb.st_mtime) {
  394. oldestlog->mtime = sb.st_mtime;
  395. strncpy(oldestlog->name, entry->d_name, sizeof(oldestlog->name));
  396. return 0;
  397. }
  398. return 0;
  399. }
  400. static int _tlog_remove_oldestlog(void)
  401. {
  402. struct oldest_log oldestlog;
  403. oldestlog.name[0] = 0;
  404. oldestlog.mtime = 0;
  405. /* get oldest log file name */
  406. if (_tlog_list_dir(tlog.logdir, _tlog_get_oldest_callback, &oldestlog) != 0) {
  407. return -1;
  408. }
  409. char filename[PATH_MAX];
  410. snprintf(filename, sizeof(filename), "%s/%s", tlog.logdir, oldestlog.name);
  411. /* delete */
  412. unlink(filename);
  413. return 0;
  414. }
  415. static int _tlog_remove_oldlog(void)
  416. {
  417. int lognum = 0;
  418. int i = 0;
  419. /* get total log file number */
  420. if (_tlog_list_dir(tlog.logdir, _tlog_count_log_callback, &lognum) != 0) {
  421. fprintf(stderr, "get log file count failed.\n");
  422. return -1;
  423. }
  424. /* remove last N log files */
  425. for (i = 0; i < lognum - tlog.logcount; i++) {
  426. _tlog_remove_oldestlog();
  427. }
  428. return 0;
  429. }
  430. static void _tlog_log_unlock(void)
  431. {
  432. char lock_file[PATH_MAX];
  433. if (tlog.fd_lock <= 0) {
  434. return;
  435. }
  436. snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname);
  437. unlink(lock_file);
  438. close(tlog.fd_lock);
  439. tlog.fd_lock = -1;
  440. }
  441. static int _tlog_log_lock(void)
  442. {
  443. char lock_file[PATH_MAX];
  444. int fd;
  445. if (tlog.multi_log == 0) {
  446. return 0;
  447. }
  448. snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname);
  449. fd = open(lock_file, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
  450. if (fd == -1) {
  451. fprintf(stderr, "create pid file failed, %s", strerror(errno));
  452. return -1;
  453. }
  454. if (lockf(fd, F_TLOCK, 0) < 0) {
  455. goto errout;
  456. }
  457. tlog.fd_lock = fd;
  458. return 0;
  459. errout:
  460. if (fd > 0) {
  461. close(fd);
  462. }
  463. return -1;
  464. }
  465. static void _tlog_wait_pid(int wait_hang)
  466. {
  467. int status;
  468. if (tlog.zip_pid <= 0) {
  469. return;
  470. }
  471. int option = (wait_hang == 0) ? WNOHANG : 0;
  472. /* check and obtain gzip process status*/
  473. if (waitpid(tlog.zip_pid, &status, option) <= 0) {
  474. return;
  475. }
  476. /* gzip process exited */
  477. tlog.zip_pid = -1;
  478. char gzip_file[PATH_MAX];
  479. /* rename ziped file */
  480. snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname);
  481. if (_tlog_rename_logfile(gzip_file) != 0) {
  482. _tlog_log_unlock();
  483. return;
  484. }
  485. /* remove oldes file */
  486. _tlog_remove_oldlog();
  487. _tlog_log_unlock();
  488. }
  489. static int _tlog_archive_log(void)
  490. {
  491. char gzip_file[PATH_MAX];
  492. char gzip_cmd[PATH_MAX];
  493. char log_file[PATH_MAX];
  494. char pending_file[PATH_MAX];
  495. snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname);
  496. snprintf(pending_file, sizeof(pending_file), "%s/%s.pending", tlog.logdir, tlog.logname);
  497. if (_tlog_log_lock() != 0) {
  498. return -1;
  499. }
  500. /* if pending.zip exists */
  501. if (access(gzip_file, F_OK) == 0) {
  502. /* rename it to standard name */
  503. if (_tlog_rename_logfile(gzip_file) != 0) {
  504. goto errout;
  505. }
  506. }
  507. if (access(pending_file, F_OK) != 0) {
  508. /* rename current log file to pending */
  509. snprintf(log_file, sizeof(log_file), "%s/%s", tlog.logdir, tlog.logname);
  510. if (rename(log_file, pending_file) != 0) {
  511. goto errout;
  512. }
  513. }
  514. /* start gzip process to compress log file */
  515. snprintf(gzip_cmd, sizeof(gzip_cmd), "gzip -1 %s", pending_file);
  516. if (tlog.zip_pid <= 0) {
  517. int pid = vfork();
  518. if (pid == 0) {
  519. execl("/bin/sh", "sh", "-c", gzip_cmd, NULL);
  520. _exit(1);
  521. } else if (pid < 0) {
  522. goto errout;
  523. }
  524. tlog.zip_pid = pid;
  525. }
  526. return 0;
  527. errout:
  528. _tlog_log_unlock();
  529. return -1;
  530. }
  531. static int _tlog_write_log(char *buff, int bufflen)
  532. {
  533. int len;
  534. /* if log file size exceeds threshold, start to compress */
  535. if (tlog.multi_log) {
  536. tlog.filesize = lseek(tlog.fd, 0, SEEK_END);
  537. }
  538. if (tlog.filesize > tlog.logsize && tlog.zip_pid <= 0) {
  539. if (tlog.filesize < lseek(tlog.fd, 0, SEEK_END) && tlog.multi_log == 0) {
  540. const char *msg = "[Auto enable multi-process write mode, log may be lost, please enable multi-process write mode manually]\n";
  541. tlog.multi_log = 1;
  542. write(tlog.fd, msg, strlen(msg));
  543. }
  544. close(tlog.fd);
  545. tlog.fd = -1;
  546. tlog.filesize = 0;
  547. _tlog_archive_log();
  548. }
  549. if (tlog.fd <= 0) {
  550. /* open a new log file to write */
  551. char logfile[PATH_MAX];
  552. if (_tlog_mkdir(tlog.logdir) != 0) {
  553. fprintf(stderr, "create log dir %s failed.\n", tlog.logdir);
  554. return -1;
  555. }
  556. snprintf(logfile, sizeof(logfile), "%s/%s", tlog.logdir, tlog.logname);
  557. tlog.filesize = 0;
  558. tlog.fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0640);
  559. if (tlog.fd < 0) {
  560. fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno));
  561. return -1;
  562. }
  563. /* get log file size */
  564. tlog.filesize = lseek(tlog.fd, 0, SEEK_END);
  565. }
  566. /* output log to screen */
  567. if (tlog.logscreen) {
  568. write(STDOUT_FILENO, buff, bufflen);
  569. }
  570. /* write log to file */
  571. len = write(tlog.fd, buff, bufflen);
  572. if (len > 0) {
  573. tlog.filesize += len;
  574. }
  575. return len;
  576. }
  577. static void *_tlog_work(void *arg)
  578. {
  579. int ret = 0;
  580. int log_len;
  581. int log_extlen;
  582. int log_end;
  583. int log_extend;
  584. int log_dropped;
  585. struct timespec tm;
  586. time_t now = time(0);
  587. time_t last = now;
  588. while (tlog.run || tlog.end != tlog.start || tlog.ext_end > 0) {
  589. log_len = 0;
  590. log_end = 0;
  591. log_extlen = 0;
  592. log_extend = 0;
  593. /* if compressing */
  594. if (tlog.zip_pid > 0) {
  595. now = time(0);
  596. if (now != last) {
  597. /* try to archive compressed file */
  598. _tlog_wait_pid(0);
  599. last = now;
  600. }
  601. }
  602. pthread_mutex_lock(&tlog.lock);
  603. if (tlog.end == tlog.start && tlog.ext_end == 0) {
  604. /* if buffer is empty, wait */
  605. clock_gettime(CLOCK_REALTIME, &tm);
  606. tm.tv_sec += 5;
  607. tlog.is_wait = 1;
  608. ret = pthread_cond_timedwait(&tlog.cond, &tlog.lock, &tm);
  609. tlog.is_wait = 0;
  610. if (ret < 0 || ret == ETIMEDOUT) {
  611. pthread_mutex_unlock(&tlog.lock);
  612. if (ret == ETIMEDOUT) {
  613. continue;
  614. }
  615. sleep(1);
  616. continue;
  617. }
  618. }
  619. if (tlog.ext_end > 0) {
  620. log_len = tlog.ext_end - tlog.start;
  621. log_extend = tlog.ext_end;
  622. }
  623. if (tlog.end < tlog.start) {
  624. log_extlen = tlog.end;
  625. } else if (tlog.end > tlog.start) {
  626. log_len = tlog.end - tlog.start;
  627. }
  628. log_end = tlog.end;
  629. log_dropped = tlog.dropped;
  630. tlog.dropped = 0;
  631. pthread_mutex_unlock(&tlog.lock);
  632. /* write log */
  633. _tlog_write_log(tlog.buff + tlog.start, log_len);
  634. if (log_extlen > 0) {
  635. /* write extend buffer log */
  636. _tlog_write_log(tlog.buff, log_extlen);
  637. }
  638. if (log_dropped > 0) {
  639. /* if there is dropped log, record dropped log number */
  640. char dropmsg[TLOG_TMP_LEN];
  641. snprintf(dropmsg, sizeof(dropmsg), "[Totoal Dropped %d Messages]\n", log_dropped);
  642. _tlog_write_log(dropmsg, strnlen(dropmsg, sizeof(dropmsg)));
  643. }
  644. pthread_mutex_lock(&tlog.lock);
  645. /* release finished buffer */
  646. tlog.start = log_end;
  647. if (log_extend > 0) {
  648. tlog.ext_end = 0;
  649. }
  650. if (tlog.waiters > 0) {
  651. /* if there are waiters, wakeup */
  652. pthread_cond_broadcast(&tlog.client_cond);
  653. }
  654. pthread_mutex_unlock(&tlog.lock);
  655. /* sleep for a while to reduce cpu usage */
  656. usleep(20 * 1000);
  657. }
  658. return NULL;
  659. }
  660. void tlog_setlogscreen(int enable)
  661. {
  662. tlog.logscreen = (enable != 0) ? 1 : 0;
  663. }
  664. int tlog_reg_format_func(tlog_format_func callback)
  665. {
  666. tlog_format = callback;
  667. return 0;
  668. }
  669. int tlog_setlevel(tlog_level level)
  670. {
  671. if (level >= TLOG_END) {
  672. return -1;
  673. }
  674. tlog_set_level = level;
  675. return 0;
  676. }
  677. int tlog_init(const char *logdir, const char *logname, int maxlogsize, int maxlogcount, int block, int buffsize, int multiwrite)
  678. {
  679. pthread_attr_t attr;
  680. int ret;
  681. if (tlog_format != NULL) {
  682. fprintf(stderr, "tlog already initilized.\n");
  683. return -1;
  684. }
  685. if (buffsize > 0 && buffsize < TLOG_MAX_LINE_LEN * 2) {
  686. fprintf(stderr, "buffer size is invalid.\n");
  687. return -1;
  688. }
  689. tlog_format = _tlog_format;
  690. tlog.logscreen = 0;
  691. tlog.buffsize = (buffsize > 0) ? buffsize : TLOG_BUFF_SIZE;
  692. tlog.start = 0;
  693. tlog.end = 0;
  694. tlog.ext_end = 0;
  695. tlog.block = (block != 0) ? 1 : 0;
  696. tlog.waiters = 0;
  697. tlog.dropped = 0;
  698. tlog.logsize = (maxlogsize > 0) ? maxlogsize : TLOG_LOG_SIZE;
  699. tlog.logcount = (maxlogcount > 0) ? maxlogcount : TLOG_LOG_COUNT;
  700. tlog.fd = -1;
  701. tlog.filesize = 0;
  702. tlog.zip_pid = -1;
  703. tlog.logscreen = 0;
  704. tlog.multi_log = (multiwrite != 0) ? 1 : 0;
  705. tlog.is_wait = 0;
  706. pthread_attr_init(&attr);
  707. pthread_mutex_init(&tlog.lock, 0);
  708. pthread_cond_init(&tlog.cond, 0);
  709. pthread_cond_init(&tlog.client_cond, 0);
  710. tlog.buff = malloc(tlog.buffsize);
  711. if (tlog.buff == NULL) {
  712. fprintf(stderr, "malloc tlog buffer failed, %s\n", strerror(errno));
  713. goto errout;
  714. }
  715. strncpy(tlog.logdir, logdir, sizeof(tlog.logdir));
  716. strncpy(tlog.logname, logname, sizeof(tlog.logname));
  717. tlog.run = 1;
  718. ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL);
  719. if (ret != 0) {
  720. fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno));
  721. goto errout;
  722. }
  723. return 0;
  724. errout:
  725. if (tlog.buff) {
  726. free(tlog.buff);
  727. tlog.buff = NULL;
  728. }
  729. if (tlog.tid > 0) {
  730. void *retval = NULL;
  731. tlog.run = 0;
  732. pthread_join(tlog.tid, &retval);
  733. }
  734. pthread_cond_destroy(&tlog.client_cond);
  735. pthread_mutex_destroy(&tlog.lock);
  736. pthread_cond_destroy(&tlog.cond);
  737. tlog.run = 0;
  738. return -1;
  739. }
  740. void tlog_exit(void)
  741. {
  742. if (tlog.tid > 0) {
  743. void *ret = NULL;
  744. tlog.run = 0;
  745. pthread_mutex_lock(&tlog.lock);
  746. pthread_cond_signal(&tlog.cond);
  747. pthread_mutex_unlock(&tlog.lock);
  748. pthread_join(tlog.tid, &ret);
  749. }
  750. if (tlog.zip_pid > 0) {
  751. _tlog_wait_pid(1);
  752. }
  753. if (tlog.buff) {
  754. free(tlog.buff);
  755. tlog.buff = NULL;
  756. }
  757. if (tlog.fd > 0) {
  758. close(tlog.fd);
  759. tlog.fd = -1;
  760. }
  761. _tlog_log_unlock();
  762. pthread_cond_destroy(&tlog.client_cond);
  763. pthread_mutex_destroy(&tlog.lock);
  764. pthread_cond_destroy(&tlog.cond);
  765. }