应用层一般log打印函数都经过封装了,这样可以设置log等级,方便调试,可以直接屏幕输出,也可以保存到文件或者发送到云端。现只看屏幕输出部分,跟进这些函数,最终就是输出到 stderr 或者 stdout 或者调用printf。
先看一个函数:
void std_err_out(void)
{
fprintf(stdout,"Hello ");
fprintf(stderr,"World!");
}
打印结果是:World!Hello 。原因是在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕,而stderr是无缓冲的,会直接输出。
第二个函数:
void simple_out(void)
{
fprintf(stderr, "%s_%d,stderr output!\n",__FUNCTION__,__LINE__);
fprintf(stdout, "%s_%d,stdout output!\n",__FUNCTION__,__LINE__);
printf("%s_%d,printf output!\n",__FUNCTION__,__LINE__);
}
打印结果是:
simple_out_27,stderr output!
simple_out_28,stdout output!
simple_out_29,printf output!
看来加了换行符后, stderr ,stdout ,printf没有任何区别。但是生成的可执行文件重定向输出到文件呢?
$ ./stdtest > tmp.txt
simple_out_11,stderr output!
那么tmp.txt里面的内容是:
simple_out_12,stdout output!
simple_out_13,printf output!
这样看差异就出来了,原因是因为:
stdout -- 标准输出设备, printf(".."))同 stdout。
stderr -- 标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。
stdout输出到磁盘文件,stderr在屏幕。
第三个函数:
#define print_case_frist(fmt,...) \
do { if (1) fprintf(stderr, "print_frist_case: %s:%s():%d " fmt, __FILE__, __func__,__LINE__, ##__VA_ARGS__); } while (0)
这个宏定义结构在代码里经常出现,关于‘##’运算符可以用于类函数宏的替换部分, __VA_ARGS__ 是一个可变参数的宏,替换省略号所代表的字符串。
这样调用 print_case_frist("|case %d\n",one);,打印结果如下:
print_frist_case: std-printf.c:main():110 |case 1
关于宏定义中do while(0) 这个结构是必须,说明如下:
do while(0) 作用:
1. 替代{},实现局部作用域..
2. 避免使用goto,用break做跳出.
例子:
#define AB1 a; b; // 下面语句b不能被执行: if (cond) AB1;
#define AB2 { a; b; } // 下面语句编译出错:if (cond) AB2; else ...;
#define AB3 a, b // 有运算符优先级问题
#define AB4 do { a; b; } while (0)
第四个函数:
#define print_case_second(fmt, args...) second_log_error("[%s:%d] "fmt, __func__, __LINE__, ##args)
void second_log_error(const char *fmt, ...)
{
va_list ap; //指向当前参数的一个指针
va_start(ap, fmt);//对va_list变量进行初始化,将ap指针指向参数列表中的第一个参数
vfprintf(stdout, fmt, ap);//将ap(通常是字符串) 按format格式写入stdout中
va_end(ap);//回收ap指针
}
这第三个函数区别是输出到 stdout,没有用fprintf,而是用vfprintf这个函数,具体函数已经注释很清楚了。
这样调用 print_case_second("|case %d\n",two);,打印结果如下:
[main:113] |case 2
第五个函数:
#define LOG_DEBUG_MAX_LEN 4096
#define LOG_DEBUG_WAY_ALL 3
#define LOG_DEBUG_LEVEL_ERROR 1
//增加log打印等级
#define print_case_third(fmt, args...)\
third_log_error(LOG_DEBUG_WAY_ALL, "%d [%s:%d] "fmt, LOG_DEBUG_LEVEL_ERROR, __func__, __LINE__, ##args)
void third_log_error(int way, const char* fmt, ...)
{
char buf[LOG_DEBUG_MAX_LEN] = {0};
int len = 0;
char *pos = NULL;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, LOG_DEBUG_MAX_LEN - 1, fmt, ap);
if (len <= 0)
{
return ;
}
if (len > LOG_DEBUG_MAX_LEN - 1 )
{
len = LOG_DEBUG_MAX_LEN - 1;
}
if ((LOG_DEBUG_LEVEL_ERROR == 1) && (way > 2))
{
printf("%s\n", buf);
}
va_end(ap);
}
这个函数可以设置log等级了,log是保存在buf中,意味着可以根据对应参数(bug_level和way)发送到需要的地方,当然也可以写入对应系统文件中。函数三和函数四的log也可以很容易保存到文件。
其实还有一种系统log是保存在/var/log/messages里,函数接口是syslog():
配置文件:/etc/syslog.conf;
log文件:/var/log/messages
syslog():向日志设备(日志工具 facility)发送日志消息
#define ALONE_LOG(fmt,args...) \
do { \
snprintf(alone_log_buf, ALONE_LOG_BUF_SIZE, fmt,##args); \
syslog( LOG_INFO, alone_log_buf ); \
} while(0)