
我们的程序一般都会产生输出信息。但是服务器程序一般却不希望输出信息到屏幕上,因为没有人盯着你的程序执行。所以我们要把一些信息写成日志文件,正常情况下运行程序的人不用关心日志里的内容,只有在出现问题的时候才会查看日志文件里的内容以确定问题所在。
但如果我们的程序要自己生成一个文件来保存日闷则志却不是好主意,因为这一方面增加了维护程序运行的人的负担,另一方面自己维护起系统来也多有不便。
在Linux系统中有一个系统日志,通常放在/var/log目录下,比如文件名是syslog的,系统中的一些程序产生的日志信息都会存放到这个文件键蠢里。日志文件有固定的格式,比如第1列是消息产生的时间,第2列是机器名(因为日志记录程序支持远程连接),第3列是标记信息(一般就是程序名称)等。而且对应的有一些工具来对这个日志进行维护,比如通过轮回机制保证日志文件大小不会把磁盘空间占尽。所以我们把自己程序的信息也写到这个系统日志里是比较好的想法。
在GNU C语言库提供的内容中,有接口可以用来做这件事。用下面的命令查看:
nm -D /lib/libc.so.6 | grep log
可以看到一些调用:
000b9410 T closelog0008b870 T getlogin
0008b960 T getlogin_r
000d0180 T __getlogin_r_chk
000bd190 T klogctl
00027450 T __open_catalog
000b9380 T openlog
0008bae0 T setlogin
000b8b80 T setlogmask
000b9350 T syslog
000b9320 T __syslog_chk
000b92f0 T vsyslog
000b8da0 T __vsyslog_chk
这里面的三个函数openlog, syslog, closelog是一套系统日志写入接口。另外那个vsyslog和syslog功能一样,只是参数格式不同。
程序的用法示例代码如下:
#include <syslog.h>int main(int argc, char **argv)
{
openlog("MyMsgMARK", LOG_CONS | LOG_PID, 0)
syslog(LOG_DEBUG,
"This is a syslog test message generated by program '%s'\n",
argv[0])
稿罩陪 closelog()
return 0
}
编译生成可执行程序后,运行一次程序将向/var/log/syslog文件添加一行信息如下:
Feb 12 08:48:38 localhost MyMsgMARK[7085]: This is a syslog test message generated by program './a.out'Feb 12 08:48:38 localhost MyMsgMARK[7085]: This is a syslog test message generated by program './a.out'
LOG_CONSWrite directly to system console if there is an error while sending to system logger.
LOG_NDELAY
Open the connection immediately (normally, the connection is opened when the first message is logged).
LOG_NOWAIT
Don’t wait for child processes that may have been created while logging the message. (The GNU C library does not create a
child process, so this option has no effect on Linux.)
LOG_ODELAY
The converse of LOG_NDELAY opening of the connection is delayed until syslog() is called. (This is the default, and need
not be specified.)
LOG_PERROR
(Not in SUSv3.) Print to stderr as well.
LOG_PID
Include PID with each message.
第三个参数指明记录日志的程序的类型。
syslog函数及参数
syslog函数用于把日志消息发给系统程序syslogd去记录,此函数原型是:
void syslog(int priority, const char *format, ...)
第一个参数是消息的紧急级别,第二个参数是消息的格式,之后是格式对应的参数。就是printf函数一样使用。
如果我们的程序要使用系统日志功能,只需要在程序启动时使用openlog函数来连接syslogd程序,后面随时用syslog函数写日志就行了。
另外,作为syslog的替代程序的新一代工具是syslog-ng,syslog-ng具有很强的网络功能,可以方便地把多台机器上的日志保存到一台中心日志服务器上。
用C语言的时候,您是否还在使用printf函数来输出日志呢?您是否考虑过将printf函数打印的内容存到文件中去呢?您是否想拥有一个可选择的既支持输出到屏幕又支持存储到文件中的日志函数呢?很高兴的告诉您,如果您愿意的话,欢迎使用本人编写的一个一套日志函数,该套函数由五部分组成,分别是宏变量BUF_SIZE、结构体log_st、log_init函数念棚大、log_debug函数和log_checksize函数。其中宏变量BUF_SIZE用来限制每次输出的日志的最大长度;结仔竖构体用来存储用户需求,包括文件路径、文件描述符号、单个文件最大大小、输出方式标志、文件命名标志等;log_init函数用来完成用户需求录入、文件创建等功能,在mian函数的开始调用一次即可;log_debug函数的功能跟printf很类似,是在printf基础上进行的扩充,实现将日志输出到屏幕或者写入到文件,在需要打印日志的地方调用该函数;log_checksize函数用来检测日志文件大小是否超过最大大小限制,它需要您定时或者定点调用它,如果一直不调用,则日志文件将不受指定的最大大小限制。
一、定义宏变量BUF_SIZE
view plaincopy to clipboardprint?
#define BUF_SIZE 1024
二、定义log_st结构体
view plaincopy to clipboardprint?
typedef struct _log_st log_st
struct _log_st
{
char path[128]
int fd
int size
int level
int num
}
三、定义log_init函数
参数说明:path——您要存储的文件路径;size——单个文件的最大大小,如果超过该大小则新建新的文件用来存储;level——日志输出方式,建议在上层限制其值的范围为0到3,0表示日志既不输出到屏幕也不创建文件和保存到文件,1表示日志保存到文件但不输出到屏幕,2表示日志既输出到屏幕也保存到文件,3表示日志只输出到文件而不创建文件和存入文件;num——日志文件命名方式,非0表示以(int)time(NULL)作为文件名来保存文件,文件数量随着日志量的递增而递增;0表示以“.new”和“.bak”为文件名来保存文件,文件数量不超过两个,随着日志量的递增,旧的日志文件将被新的覆盖,更直观的说就是说.new”和“.bak”文件只保存最近的日志。
view plaincopy to clipboardprint?
log_st *log_init(char *path, int size, int level, int num)
{
char new_path[128] = 和袭{0}
if (NULL == path || 0 == level) return NULL
log_st *log = (log_st *)malloc(sizeof(log_st))
memset(log, 0, sizeof(log_st))
if (level != 3)
{
//the num use to control file naming
log->num = num
if(num)
snprintf(new_path, 128, "%s%d", path, (int)time(NULL))
else
snprintf(new_path, 128, "%s.new", path)
if(-1 == (log->fd = open(new_path, O_RDWR|O_APPEND|O_CREAT|O_SYNC, S_IRUSR|S_IWUSR|S_IROTH)))
{
free(log)
log = NULL
return NULL
}
}
strncpy(log->path, path, 128)
log->size = (size > 0 ? size:0)
log->level = (level > 0 ? level:0)
return log
}
四、定义log_debug函数
view plaincopy to clipboardprint?
void log_debug(log_st *log, const char *msg, ...)
{
va_list ap
time_t now
char *pos
char _n = '\n'
char message[BUF_SIZE] = {0}
int nMessageLen = 0
int sz
if(NULL == log || 0 == log->level) return
now = time(NULL)
pos = ctime(&now)
sz = strlen(pos)
pos[sz-1]=']'
snprintf(message, BUF_SIZE, "[%s ", pos)
for (pos = message *pos pos++)
sz = pos - message
va_start(ap, msg)
nMessageLen = vsnprintf(pos, BUF_SIZE - sz, msg, ap)
va_end(ap)
if (nMessageLen <= 0) return
if (3 == log->level)
{
printf("%s\n", message)
return
}
if (2 == log->level)
printf("%s\n", message)
write(log->fd, message, strlen(message))
write(log->fd, &_n, 1)
fsync(log->fd)
}
五、定义log_checksize函数
view plaincopy to clipboardprint?
void log_checksize(log_st *log)
{
struct stat stat_buf
char new_path[128] = {0}
char bak_path[128] = {0}
if(NULL == log || 3 == log->level || '\0' == log->path[0]) return
memset(&stat_buf, 0, sizeof(struct stat))
fstat(log->fd, &stat_buf)
if(stat_buf.st_size > log->size)
{
close(log->fd)
if(log->num)
snprintf(new_path, 128, "%s%d", log->path, (int)time(NULL))
else
{
snprintf(bak_path, 128, "%s.bak", log->path)
snprintf(new_path, 128, "%s.new", log->path)
remove(bak_path) //delete the file *.bak first
rename(new_path, bak_path) //change the name of the file *.new to *.bak
}
//create a new file
log->fd = open(new_path, O_RDWR|O_APPEND|O_CREAT|O_SYNC, S_IRUSR|S_IWUSR|S_IROTH)
}
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)