log.cpp 13 KB

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