【发布时间】:2020-10-23 20:54:49
【问题描述】:
如何可靠地记录日志以确保多线程应用程序中条目的原子性?此外,我希望能够使用 logrotate 实用程序轮换日志。
下一个最简单的写日志变体:
- 打开/重新打开日志文件
- 写条目
printf() - 退出时关闭日志文件
这是我的例子:
// default log level
static Cl_loglevl loglevel = LOGLEVEL_NONE;
// log file descriptor (open with Cl_openlog)
static FILE *logfd = NULL;
/**
* @brief Cl_openlog - open log file
* @param logfile - file name
* @return FILE struct or NULL if failed
*/
FILE *Cl_openlog(const char *logfile, Cl_loglevl loglvl){
if(logfd){
Cl_putlog(LOGLEVEL_ERROR, "Reopen log file\n");
fclose(logfd);
logfd = NULL;
char newname[PATH_MAX];
snprintf(newname, PATH_MAX, "%s.old", logfile);
if(rename(logfile, newname)) WARN("Can't rename old log file");
}
if(loglvl < LOGLEVEL_CNT) loglevel = loglvl;
if(!logfile) return NULL;
if(!(logfd = fopen(logfile, "w"))) WARN(_("Can't open log file"));
return logfd;
}
/**
* @brief Cl_putlog - put message to log file
* @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
* @param fmt - format and the rest part of message
* @return amount of symbols saved in file
*/
int Cl_putlog(Cl_loglevl lvl, const char *fmt, ...){
if(lvl > loglevel || !logfd) return 0;
char strtm[128];
time_t t = time(NULL);
struct tm *curtm = localtime(&t);
strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm);
int i = fprintf(logfd, "%s\t", strtm);
va_list ar;
va_start(ar, fmt);
i += vfprintf(logfd, fmt, ar);
va_end(ar);
fflush(logfd);
return i;
}
Cl_openlog 的调用允许轮换日志一次。我可以在SIG_USR1 处理程序中调用这个函数并通过logrotate 发送这个信号。但目前还不清楚如何正确写入文件以实现记录的原子性。
我不想使用像 log4c 这样的外部库来解决这种简单的问题。
【问题讨论】:
-
无论何时访问日志文件(包括轮换时),您都可以使用互斥锁。或者您应该让每个日志文件写入事件打开并关闭日志文件(仍然使用互斥锁以防止同时访问),但是如果您执行大量日志记录,也可能会对性能产生影响。无论哪种方式,您都应该只在关键任务之后登录每个线程,这样日志互斥锁就不会减慢您的实际功能。此外,您还想查看现有的库(例如 log4c.sourceforge.net)。