log.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. * 文件名: log.c
  3. * 目标: 日志系统对aFun的API
  4. * 注意: 因为tool模块需要使用log系统, 因此log系统尽量少点依赖tool模块, 避免造成死循环
  5. * 仅依赖:
  6. * time_s.h 中 getTime -> strCopy
  7. * mem.h 中 free
  8. * file.h 中 getFileSize
  9. * stdio_.h
  10. * str.h
  11. * exit_.h
  12. * pthread.h
  13. */
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdarg.h>
  17. #include <string.h>
  18. #include "macro.h"
  19. #include "mem.h"
  20. #include "log.h"
  21. #include "time_s.h"
  22. #include "file.h"
  23. #include "stdio_.h"
  24. #include "str.h"
  25. #include "exit_.h"
  26. #include "pthread.h"
  27. #ifdef aFunWIN32
  28. #include <windows.h>
  29. #define getpid() (long)GetCurrentProcessId()
  30. #define gettid() (long)GetCurrentThreadId()
  31. // cygwin没有syscall.h, 因此需要依赖 windows 的 api
  32. #else
  33. #include <unistd.h>
  34. #include "sys/syscall.h"
  35. #define gettid() (long)syscall(SYS_gettid)
  36. #define getpid() (long)getpid()
  37. #endif
  38. #undef calloc
  39. typedef struct LogNode LogNode;
  40. struct LogNode { // 日志信息记录节点
  41. LogLevel level;
  42. char *id;
  43. pid_t tid;
  44. char *date; // 需要释放
  45. time_t time;
  46. char *file;
  47. int line;
  48. char *func;
  49. char *info; // 需要释放
  50. LogNode *next;
  51. };
  52. static struct LogFactory {
  53. bool init; // 是否已经初始化
  54. pid_t pid;
  55. FILE *log; // 记录文件输出的位置
  56. FILE *csv;
  57. Logger sys_log;
  58. bool asyn; // 异步
  59. pthread_t pt;
  60. pthread_cond_t cond; // 有日志
  61. LogNode *log_buf;
  62. LogNode **plog_buf; // 指向 log_buf的末端
  63. } log_factory = {.init=false};
  64. static pthread_mutex_t log_factory_mutex = PTHREAD_MUTEX_INITIALIZER;
  65. #define MUTEX (&log_factory_mutex)
  66. static void destructLogSystemAtExit(void *_);
  67. static void *ansyWritrLog_(void *_);
  68. /**
  69. * 输出日志系统的相关信息, Debug使用
  70. */
  71. void printLogSystemInfo(void) {
  72. printf("Log system on %p\n", &log_factory);
  73. }
  74. /**
  75. * 函数名: initLogSystem
  76. * 目标: 初始化日志系统
  77. * 返回值:
  78. * 1 表示初始化成功
  79. * 2 表示已经初始化
  80. * 0 表示初始化失败
  81. *
  82. * 该程序线程不安全
  83. * @param path 日志保存的地址
  84. * @param asyn 是否启用异步
  85. * @return
  86. */
  87. int initLogSystem(FilePath path, bool asyn){
  88. if (strlen(path) >= 218) // 路径过长
  89. return 0;
  90. int re = 1;
  91. pthread_mutex_lock(MUTEX);
  92. if (log_factory.init) {
  93. pthread_mutex_unlock(MUTEX);
  94. return 2;
  95. }
  96. char log_path[218] = {0};
  97. char csv_path[218] = {0};
  98. log_factory.pid = getpid(); // 获取进程ID
  99. char *ti = getTime(NULL, "%Y-%m-%d%z");
  100. snprintf(log_path, 218, "%s-%s.log", path, ti);
  101. snprintf(csv_path, 218, "%s-%s.csv", path, ti);
  102. free(ti);
  103. uintmax_t log_size = getFileSize(log_path);
  104. uintmax_t csv_size = getFileSize(csv_path);
  105. bool csv_head_write = (checkFile(csv_path) == 0); // 文件不存在时才写入头部
  106. log_factory.log = fileOpen(log_path, "a");
  107. if (log_factory.log == NULL) {
  108. perror("ERROR: ");
  109. printf("log_path = %s\n", log_path);
  110. pthread_mutex_unlock(MUTEX);
  111. return 0;
  112. }
  113. log_factory.csv = fileOpen(csv_path, "a");
  114. if (log_factory.csv == NULL) {
  115. pthread_mutex_unlock(MUTEX);
  116. return 0;
  117. }
  118. #define CSV_FORMAT "%s,%s,%d,%d,%s,%ld,%s,%d,%s,%s\n"
  119. #define CSV_TITLE "Level,Logger,PID,TID,Data,Timestamp,File,Line,Function,Log\n"
  120. if (csv_head_write) {
  121. fprintf(log_factory.csv, CSV_TITLE); // 设置 cvs 标题
  122. fflush(log_factory.csv);
  123. }
  124. #undef CSV_TITLE
  125. log_factory.init = true;
  126. log_factory.asyn = asyn;
  127. if (log_factory.asyn) {
  128. log_factory.plog_buf = &log_factory.log_buf;
  129. pthread_cond_init(&log_factory.cond, NULL);
  130. pthread_create(&log_factory.pt, NULL, ansyWritrLog_, NULL);
  131. }
  132. initLogger(&(log_factory.sys_log), "SYSTEM", log_info); // 设置为 debug, 记录 success 信息
  133. pthread_mutex_unlock(MUTEX);
  134. writeInfoLog(NULL, "Log system init success");
  135. writeInfoLog(NULL, "Log .log size %lld", log_size);
  136. writeInfoLog(NULL, "Log .csv size %lld", csv_size);
  137. aFunAtExit(destructLogSystemAtExit, NULL);
  138. return re;
  139. }
  140. static void destructLogSystemAtExit(void *_) {
  141. destructLogSystem();
  142. }
  143. int destructLogSystem(void) {
  144. int re = 1;
  145. pthread_mutex_lock(MUTEX);
  146. if (!log_factory.init) {
  147. re = 2;
  148. goto RETURN;
  149. }
  150. pthread_mutex_unlock(MUTEX);
  151. writeInfoLog(NULL, "Log system destruct by exit."); // 需要用锁
  152. pthread_mutex_lock(MUTEX);
  153. log_factory.init = false;
  154. if (log_factory.asyn) {
  155. pthread_mutex_unlock(MUTEX);
  156. pthread_cond_signal(&log_factory.cond);
  157. pthread_join(log_factory.pt, NULL);
  158. pthread_mutex_lock(MUTEX);
  159. pthread_cond_destroy(&log_factory.cond);
  160. if (log_factory.log_buf != NULL)
  161. printf_stderr(0, "Logsystem destruct error.");
  162. }
  163. fileClose(log_factory.log);
  164. fileClose(log_factory.csv);
  165. log_factory.log = NULL;
  166. log_factory.csv = NULL;
  167. RETURN:
  168. pthread_mutex_unlock(MUTEX);
  169. return re;
  170. }
  171. void initLogger(Logger *logger, char *id, LogLevel level) {
  172. memset(logger, 0, sizeof(Logger));
  173. logger->id = id;
  174. logger->level = level;
  175. }
  176. /* LogLevel和字符串的转换 */
  177. static const char *LogLevelName[] = {
  178. "TK", // track 0
  179. "DE", // debug 1
  180. "IN", // info 2
  181. "WA", // warning 3
  182. "ER", // error 4
  183. "SE", // send_error 5
  184. "FE", // fatal_error 6
  185. };
  186. static const char *LogLevelNameLong[] = {
  187. /* 内容输出到终端时使用*/
  188. "Track", // track 0
  189. "Debug", // debug 1
  190. "Info", // info 2
  191. "Warning", // warning 3
  192. "Error", // error 4
  193. "Fatal Error", // send_error 5
  194. "*FATAL ERROR*", // fatal_error 6
  195. };
  196. /**
  197. * 日志写入到文件
  198. * @param level 日志等级
  199. * @param id 日志器ID
  200. * @param tid 线程号
  201. * @param ti 时间
  202. * @param t 时间戳
  203. * @param file 文件名
  204. * @param line 行号
  205. * @param func 函数名
  206. * @param info 日志内容
  207. */
  208. static void writeLogToFactory_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  209. #define FORMAT "%s/[%s] %d %d {%s %ld} (%s:%d at %s) : '%s' \n"
  210. /* 写入文件日志 */
  211. if (log_factory.log != NULL) {
  212. fprintf(log_factory.log, FORMAT, LogLevelName[level], id, log_factory.pid, tid, ti, t, file, line, func, info);
  213. fflush(log_factory.log);
  214. }
  215. if (log_factory.csv != NULL) {
  216. fprintf(log_factory.csv, CSV_FORMAT, LogLevelName[level], id, log_factory.pid, tid, ti, t, file, line, func, info);
  217. fflush(log_factory.csv);
  218. }
  219. #undef FORMAT
  220. #undef CSV_FORMAT
  221. }
  222. /**
  223. * 日志写入到控制台
  224. * @param level 日志等级
  225. * @param id 日志器ID
  226. * @param tid 线程号
  227. * @param ti 时间
  228. * @param t 时间戳
  229. * @param file 文件名
  230. * @param line 行号
  231. * @param func 函数名
  232. * @param info 日志内容
  233. */
  234. static void writeLogToConsole_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  235. #define FORMAT_SHORT "\r* %s[%d](%s:%d) : %s \n" // 显示到终端, 添加\r回车符确保顶行显示
  236. #define STD_BUF_SIZE (STR_LEN(info) + 1024)
  237. if (level < log_warning) {
  238. printf_stdout(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], tid, file, line, info);
  239. fflush(stdout);
  240. } else {
  241. printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], tid, file, line, info);
  242. fflush(stderr);
  243. }
  244. #undef FORMAT_SHORT
  245. #undef STD_BUF_SIZE
  246. }
  247. /**
  248. * 日志异步写入
  249. * @param level 日志等级
  250. * @param id 日志器ID
  251. * @param tid 线程号
  252. * @param ti 时间
  253. * @param t 时间戳
  254. * @param file 文件名
  255. * @param line 行号
  256. * @param func 函数名
  257. * @param info 日志内容
  258. */
  259. static void writeLogToAsyn_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  260. #define D(i) (*(log_factory.plog_buf))->i
  261. *(log_factory.plog_buf) = calloc(1, sizeof(LogNode));
  262. D(level) = level;
  263. D(id) = id;
  264. D(tid) = tid;
  265. D(date) = strCopy(ti);
  266. D(time) = t;
  267. D(file) = file;
  268. D(line) = line;
  269. D(func) = func;
  270. D(info) = strCopy(info);
  271. log_factory.plog_buf = &(D(next));
  272. #undef D
  273. }
  274. /**
  275. * 异步写入日志程序
  276. * @param _ 无意义
  277. * @return
  278. */
  279. static void *ansyWritrLog_(void *_) {
  280. pthread_mutex_lock(MUTEX);
  281. while (1) {
  282. while (log_factory.init && log_factory.log_buf == NULL)
  283. pthread_cond_wait(&log_factory.cond, MUTEX);
  284. if (!log_factory.init && log_factory.log_buf == NULL)
  285. break;
  286. #define D(i) log_factory.log_buf->i
  287. writeLogToFactory_(D(level), D(id), D(tid), D(date), D(time), D(file), D(line), D(func), D(info));
  288. #undef D
  289. LogNode *tmp = log_factory.log_buf->next;
  290. free(log_factory.log_buf->date);
  291. free(log_factory.log_buf->info);
  292. free(log_factory.log_buf);
  293. log_factory.log_buf = tmp;
  294. if (tmp == NULL)
  295. log_factory.plog_buf = &log_factory.log_buf;
  296. }
  297. pthread_mutex_unlock(MUTEX);
  298. return NULL;
  299. }
  300. /**
  301. * 日志器 写入日志
  302. * @param logger 日志器
  303. * @param pc 是否写入到控制台
  304. * @param level 日志等级
  305. * @param file 文件名
  306. * @param line 行号
  307. * @param func 函数名
  308. * @param format 日志内容(格式字符串)
  309. * @param ap 格式字符串内容
  310. * @return
  311. */
  312. static int writeLog_(Logger *logger, bool pc, LogLevel level, char *file, int line, char *func, char *format, va_list ap){
  313. if (logger->level > level)
  314. return 2;
  315. pthread_mutex_lock(MUTEX);
  316. if (!log_factory.init || log_factory.log == NULL) {
  317. pthread_mutex_unlock(MUTEX);
  318. return 1;
  319. }
  320. CLEAR_FERROR(log_factory.log);
  321. // 输出 head 信息
  322. time_t t = 0;
  323. char *ti = getTime(&t, "%Y-%m-%d %H:%M:%S");
  324. pid_t tid = gettid();
  325. char tmp[2048] = {0};
  326. vsnprintf(tmp, 1024, format, ap); // ap只使用一次
  327. va_end(ap);
  328. if (log_factory.asyn)
  329. writeLogToAsyn_(level, logger->id, tid, ti, t, file, line, func, tmp);
  330. else
  331. writeLogToFactory_(level, logger->id, tid, ti, t, file, line, func, tmp);
  332. if (pc)
  333. writeLogToConsole_(level, logger->id, tid, ti, t, file, line, func, tmp);
  334. pthread_mutex_unlock(MUTEX);
  335. if (log_factory.asyn)
  336. pthread_cond_signal(&log_factory.cond);
  337. free(ti);
  338. return 0;
  339. }
  340. #define CHECK_LOGGER() do {if (logger == NULL) {logger = &(log_factory.sys_log);} \
  341. if (logger == NULL || logger->id == NULL) return -1;} while(0)
  342. int writeTrackLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  343. #if aFunWriteTrack
  344. CHECK_LOGGER();
  345. va_list ap;
  346. va_start(ap, format);
  347. return writeLog_(logger, aFunConsoleTrack, log_track, file, line, func, format, ap);
  348. #endif
  349. }
  350. int writeDebugLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  351. #if aFunWriteDebug
  352. CHECK_LOGGER();
  353. va_list ap;
  354. va_start(ap, format);
  355. return writeLog_(logger, aFunConsoleDebug, log_debug, file, line, func, format, ap);
  356. #endif
  357. }
  358. int writeInfoLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  359. #if aFunWriteInfo
  360. CHECK_LOGGER();
  361. va_list ap;
  362. va_start(ap, format);
  363. return writeLog_(logger, aFunConsoleInfo, log_info, file, line, func, format, ap);
  364. #endif
  365. }
  366. int writeWarningLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  367. #if !aFunIgnoreWarning
  368. CHECK_LOGGER();
  369. va_list ap;
  370. va_start(ap, format);
  371. return writeLog_(logger, aFunConsoleWarning, log_warning, file, line, func, format, ap);
  372. #endif
  373. }
  374. int writeErrorLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  375. #if !aFunIgnoreError
  376. CHECK_LOGGER();
  377. va_list ap;
  378. va_start(ap, format);
  379. return writeLog_(logger, aFunConsoleError, log_error, file, line, func, format, ap);
  380. #endif
  381. }
  382. int writeSendErrorLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  383. #ifndef aFunOFFAllLog
  384. CHECK_LOGGER();
  385. #if !aFunIgnoreSendError
  386. va_list ap;
  387. va_start(ap, format);
  388. jmp_buf *buf = logger->buf;
  389. writeLog_(logger, aFunConsoleSendError, log_send_error, file, line, func, format, ap);
  390. #endif
  391. destructLogSystem();
  392. if (buf != NULL) {
  393. initLogger(logger, NULL, 0); // 清零
  394. longjmp(*buf, 1);
  395. } else
  396. aFunExit(aFunExitFail);
  397. #endif
  398. }
  399. int writeFatalErrorLog_(Logger *logger, char *file, int line, char *func, int exit_code, char *format, ...) {
  400. #ifndef aFunOFFAllLog
  401. CHECK_LOGGER();
  402. #if !aFunIgnoreFatal
  403. va_list ap;
  404. va_start(ap, format);
  405. writeLog_(logger, aFunConsoleFatalError, log_fatal_error, file, line, func, format, ap);
  406. #endif
  407. destructLogSystem();
  408. if (exit_code == EXIT_SUCCESS)
  409. abort();
  410. else
  411. aFunExit(exit_code);
  412. #endif
  413. }