log.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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 *data);
  67. static void *ansyWritrLog_(void *_);
  68. /*
  69. * 函数名: initLogSystem
  70. * 目标: 初始化日志系统
  71. * 返回值:
  72. * 1 表示初始化成功
  73. * 2 表示已经初始化
  74. * 0 表示初始化失败
  75. *
  76. * 该程序线程不安全
  77. */
  78. int initLogSystem(FilePath path, bool asyn){
  79. if (strlen(path) >= 218) // 路径过长
  80. return 0;
  81. int re = 1;
  82. pthread_mutex_lock(MUTEX);
  83. if (log_factory.init) {
  84. pthread_mutex_unlock(MUTEX);
  85. return 2;
  86. }
  87. char log_path[218] = {0};
  88. char csv_path[218] = {0};
  89. log_factory.pid = getpid(); // 获取进程ID
  90. char *ti = getTime(NULL, "%Y-%m-%d%z");
  91. snprintf(log_path, 218, "%s-%s.log", path, ti);
  92. snprintf(csv_path, 218, "%s-%s.csv", path, ti);
  93. free(ti);
  94. uintmax_t log_size = getFileSize(log_path);
  95. uintmax_t csv_size = getFileSize(csv_path);
  96. bool csv_head_write = (checkFile(csv_path) == 0); // 文件不存在时才写入头部
  97. log_factory.log = fopen(log_path, "a");
  98. if (log_factory.log == NULL) {
  99. printf("log_path = %s\n", log_path);
  100. pthread_mutex_unlock(MUTEX);
  101. return 0;
  102. }
  103. log_factory.csv = fopen(csv_path, "a");
  104. if (log_factory.csv == NULL) {
  105. pthread_mutex_unlock(MUTEX);
  106. return 0;
  107. }
  108. #define CSV_FORMAT "%s,%s,%d,%d,%s,%ld,%s,%d,%s,%s\n"
  109. #define CSV_TITLE "Level,Logger,PID,TID,Data,Timestamp,File,Line,Function,Log\n"
  110. if (csv_head_write) {
  111. fprintf(log_factory.csv, CSV_TITLE); // 设置 cvs 标题
  112. fflush(log_factory.csv);
  113. }
  114. #undef CSV_TITLE
  115. log_factory.init = true;
  116. log_factory.asyn = asyn;
  117. if (log_factory.asyn) {
  118. pthread_cond_init(&log_factory.cond, NULL);
  119. pthread_create(&log_factory.pt, NULL, ansyWritrLog_, NULL);
  120. }
  121. initLogger(&(log_factory.sys_log), "SYSTEM", log_info); // 设置为 debug, 记录 success 信息
  122. pthread_mutex_unlock(MUTEX);
  123. writeInfoLog(NULL, "Log system init success");
  124. writeInfoLog(NULL, "Log .log size %lld", log_size);
  125. writeInfoLog(NULL, "Log .csv size %lld", csv_size);
  126. aFunAtExit(destructLogSystemAtExit, NULL);
  127. return re;
  128. }
  129. static void destructLogSystemAtExit(void *data) {
  130. destructLogSystem();
  131. }
  132. int destructLogSystem(void) {
  133. int re = 1;
  134. pthread_mutex_lock(MUTEX);
  135. if (!log_factory.init) {
  136. re = 2;
  137. goto RETURN;
  138. }
  139. pthread_mutex_unlock(MUTEX);
  140. writeInfoLog(NULL, "Log system destruct by exit."); // 需要用锁
  141. pthread_mutex_lock(MUTEX);
  142. log_factory.init = false;
  143. if (log_factory.asyn) {
  144. pthread_mutex_unlock(MUTEX);
  145. pthread_cond_signal(&log_factory.cond);
  146. pthread_join(log_factory.pt, NULL);
  147. pthread_mutex_lock(MUTEX);
  148. pthread_cond_destroy(&log_factory.cond);
  149. if (log_factory.log_buf != NULL)
  150. printf_stderr(0, "Logsystem destruct error.");
  151. }
  152. fclose(log_factory.log);
  153. fclose(log_factory.csv);
  154. log_factory.log = NULL;
  155. log_factory.csv = NULL;
  156. log_factory.init = false;
  157. RETURN:
  158. pthread_mutex_unlock(MUTEX);
  159. return re;
  160. }
  161. void initLogger(Logger *logger, char *id, LogLevel level) {
  162. memset(logger, 0, sizeof(Logger));
  163. logger->id = id;
  164. logger->level = level;
  165. }
  166. /* LogLevel和字符串的转换 */
  167. static const char *LogLevelName[] = {
  168. "TK", // track 0
  169. "DE", // debug 1
  170. "IN", // info 2
  171. "WA", // warning 3
  172. "ER", // error 4
  173. "SE", // send_error 5
  174. "FE", // fatal_error 6
  175. };
  176. static const char *LogLevelNameLong[] = {
  177. /* 内容输出到终端时使用*/
  178. "Track", // track 0
  179. "Debug", // debug 1
  180. "Info", // info 2
  181. "Warning", // warning 3
  182. "Error", // error 4
  183. "Fatal Error", // send_error 5
  184. "*FATAL ERROR*", // fatal_error 6
  185. };
  186. static void writeLogToFactory_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  187. #define FORMAT "%s/[%s] %d %d {%s %ld} (%s:%d at %s) : '%s' \n"
  188. /* 写入文件日志 */
  189. if (log_factory.log != NULL) {
  190. fprintf(log_factory.log, FORMAT, LogLevelName[level], id, log_factory.pid, tid, ti, t, file, line, func, info);
  191. fflush(log_factory.log);
  192. }
  193. if (log_factory.csv != NULL) {
  194. fprintf(log_factory.csv, CSV_FORMAT, LogLevelName[level], id, log_factory.pid, tid, ti, t, file, line, func, info);
  195. fflush(log_factory.csv);
  196. }
  197. #undef FORMAT
  198. #undef CSV_FORMAT
  199. }
  200. static void writeLogToConsole_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  201. #define FORMAT_SHORT "\r* %s(%s:%d) : %s \n" // 显示到终端, 添加\r回车符确保顶行显示
  202. #define STD_BUF_SIZE (STR_LEN(info) + 1024)
  203. if (level < log_warning) {
  204. printf_stdout(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, info);
  205. fflush(stdout);
  206. } else {
  207. printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, info);
  208. fflush(stderr);
  209. }
  210. #undef FORMAT_SHORT
  211. #undef STD_BUF_SIZE
  212. }
  213. static void writeLogToAsyn_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
  214. #define D(i) (*(log_factory.plog_buf))->i
  215. *(log_factory.plog_buf) = calloc(1, sizeof(LogNode));
  216. D(level) = level;
  217. D(id) = id;
  218. D(tid) = tid;
  219. D(date) = strCopy(ti);
  220. D(time) = t;
  221. D(file) = file;
  222. D(line) = line;
  223. D(func) = func;
  224. D(info) = strCopy(info);
  225. log_factory.plog_buf = &(D(next));
  226. pthread_cond_signal(&log_factory.cond);
  227. #undef D
  228. }
  229. static void *ansyWritrLog_(void *_) {
  230. pthread_mutex_lock(MUTEX);
  231. while (1) {
  232. while (log_factory.init && log_factory.log_buf == NULL)
  233. pthread_cond_wait(&log_factory.cond, MUTEX);
  234. if (!log_factory.init && log_factory.log_buf == NULL)
  235. break;
  236. #define D(i) log_factory.log_buf->i
  237. writeLogToFactory_(D(level), D(id), D(tid), D(date), D(time), D(file), D(line), D(func), D(info));
  238. #undef D
  239. LogNode *tmp = log_factory.log_buf->next;
  240. free(log_factory.log_buf->date);
  241. free(log_factory.log_buf->info);
  242. free(log_factory.log_buf);
  243. log_factory.log_buf = tmp;
  244. if (tmp == NULL)
  245. log_factory.plog_buf = &log_factory.log_buf;
  246. }
  247. pthread_mutex_unlock(MUTEX);
  248. return NULL;
  249. }
  250. static int writeLog_(Logger *logger, bool pc, LogLevel level, char *file, int line, char *func, char *format, va_list ap){
  251. if (logger->level > level)
  252. return 2;
  253. pthread_mutex_lock(MUTEX);
  254. if (!log_factory.init || log_factory.log == NULL) {
  255. pthread_mutex_unlock(MUTEX);
  256. return 1;
  257. }
  258. CLEAR_FERROR(log_factory.log);
  259. // 输出 head 信息
  260. time_t t = 0;
  261. char *ti = getTime(&t, "%Y-%m-%d %H:%M:%S");
  262. pid_t tid = gettid();
  263. char tmp[2048] = {0};
  264. vsnprintf(tmp, 1024, format, ap); // ap只使用一次
  265. va_end(ap);
  266. if (!log_factory.asyn)
  267. writeLogToAsyn_(level, logger->id, tid, ti, t, file, line, func, tmp);
  268. else
  269. writeLogToFactory_(level, logger->id, tid, ti, t, file, line, func, tmp);
  270. if (pc)
  271. writeLogToConsole_(level, logger->id, tid, ti, t, file, line, func, tmp);
  272. pthread_mutex_unlock(MUTEX);
  273. free(ti);
  274. return 0;
  275. }
  276. #define CHECK_LOGGER() do {if (logger == NULL) {logger = &(log_factory.sys_log);} \
  277. if (logger == NULL || logger->id == NULL) return -1;} while(0)
  278. int writeTrackLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  279. #if aFunWriteTrack
  280. CHECK_LOGGER();
  281. va_list ap;
  282. va_start(ap, format);
  283. return writeLog_(logger, aFunConsoleTrack, log_track, file, line, func, format, ap);
  284. #endif
  285. }
  286. int writeDebugLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  287. #if aFunWriteDebug
  288. CHECK_LOGGER();
  289. va_list ap;
  290. va_start(ap, format);
  291. return writeLog_(logger, aFunConsoleDebug, log_debug, file, line, func, format, ap);
  292. #endif
  293. }
  294. int writeInfoLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  295. #if aFunWriteInfo
  296. CHECK_LOGGER();
  297. va_list ap;
  298. va_start(ap, format);
  299. return writeLog_(logger, aFunConsoleInfo, log_info, file, line, func, format, ap);
  300. #endif
  301. }
  302. int writeWarningLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  303. #if !aFunIgnoreWarning
  304. CHECK_LOGGER();
  305. va_list ap;
  306. va_start(ap, format);
  307. return writeLog_(logger, aFunConsoleWarning, log_warning, file, line, func, format, ap);
  308. #endif
  309. }
  310. int writeErrorLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  311. #if !aFunIgnoreError
  312. CHECK_LOGGER();
  313. va_list ap;
  314. va_start(ap, format);
  315. return writeLog_(logger, aFunConsoleError, log_error, file, line, func, format, ap);
  316. #endif
  317. }
  318. int writeSendErrorLog_(Logger *logger, char *file, int line, char *func, char *format, ...) {
  319. #ifndef aFunOFFAllLog
  320. CHECK_LOGGER();
  321. #if !aFunIgnoreSendError
  322. va_list ap;
  323. va_start(ap, format);
  324. jmp_buf *buf = logger->buf;
  325. writeLog_(logger, aFunConsoleSendError, log_send_error, file, line, func, format, ap);
  326. #endif
  327. destructLogSystem();
  328. if (buf != NULL) {
  329. initLogger(logger, NULL, 0); // 清零
  330. longjmp(*buf, 1);
  331. } else
  332. aFunExit(aFunExitFail);
  333. #endif
  334. }
  335. int writeFatalErrorLog_(Logger *logger, char *file, int line, char *func, int exit_code, char *format, ...) {
  336. #ifndef aFunOFFAllLog
  337. CHECK_LOGGER();
  338. #if !aFunIgnoreFatal
  339. va_list ap;
  340. va_start(ap, format);
  341. writeLog_(logger, aFunConsoleFatalError, log_fatal_error, file, line, func, format, ap);
  342. #endif
  343. destructLogSystem();
  344. if (exit_code == EXIT_SUCCESS)
  345. abort();
  346. else
  347. aFunExit(exit_code);
  348. #endif
  349. }