linux c c++ 通过printf收集系统日志
背景:
在开发大型项目的过程中,总是避免不了收集系统日志的需要,要不然自己编写的项目出现问题了,一问三不知,或者没有踪迹可查,那将会是一个非常可怕的事情,因此,小菜鸡分享一下以前参与到的某个大项目的日志收集模块做法,关于本文的编码已经进行了信息脱敏处理,因此,可以放心使用~
实现思路
利用C中的宏函数重新定义printf的功能;
重定向printf的输出方向;
设置日志输出等级;
详细实现
#ifndef WT_BACKGROUND_APPLICATION_LOG_H
#define WT_BACKGROUND_APPLICATION_LOG_H
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#define __DEBUG //日志模块总开关,注释掉将关闭日志输出
#ifdef __DEBUG
#define DEBUG(format, ...) printf (format, ##__VA_ARGS__)
#else
#define DEBUG(format, ...)
#endif
//定义日志级别
enum LOG_LEVEL {
LOG_LEVEL_OFF=0,
LOG_LEVEL_FATAL,
LOG_LEVEL_ERR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_ALL,
};
extern enum LOG_LEVEL debug_level;//模块日志输出级别
static void wt_get_log_time(){
time_t timer;//time_t就是long int 类型
struct tm *tblock;
timer = time(NULL);
tblock = localtime(&timer);
printf("\n%s", asctime(tblock));
}
#define log_fatal(level,format, ...) \
do { \
if(level>=LOG_LEVEL_FATAL){\
wt_get_log_time();\
DEBUG("->FATAL @ FUNC:%s FILE:%s LINE:%d " format "\n",\
__func__, __FILE__, __LINE__, ##__VA_ARGS__ );\
}\
} while (0)
#define log_err(level,format, ...) \
do { \
if(level>=LOG_LEVEL_ERR){\
wt_get_log_time();\
DEBUG("->ERR @ FUNC:%s FILE:%s LINE:%d " format "\n",\
__func__, __FILE__, __LINE__, ##__VA_ARGS__ );\
}\
} while (0)
#define log_warn(level,format, ...) \
do { \
if(level>=LOG_LEVEL_WARN){\
wt_get_log_time();\
DEBUG("->WARN @ FUNC:%s " format "\n",__func__, ##__VA_ARGS__ );\
}\
} while (0)
#define log_info(level,format, ...) \
do { \
if(level>=LOG_LEVEL_INFO){\
wt_get_log_time();\
DEBUG("->INFO " format"\n",##__VA_ARGS__ );\
}\
} while (0)
#define log_debug(level,format, ...) \
do { \
if(level>=LOG_LEVEL_ALL){\
wt_get_log_time();\
DEBUG("->DEBUG " format"\n",##__VA_ARGS__ );\
}\
} while (0)
extern void wt_set_background_application_log_level(int log_level);
extern void wt_log_test();
#endif // WT_BACKGROUND_APPLICATION_LOG_H
使用提示:将上方代码做成头文件即可!
static int save_standard_output_descriptor_symbol;
static int log_file_descriptor_symbol;
enum LOG_LEVEL debug_level;//模块日志输出级别
void wt_log_ouput_init(){
fflush(stdout); //刷新流 stream 的输出缓冲区
setvbuf(stdout,NULL,_IONBF,0); //定义流 stream 不使用缓冲
save_standard_output_descriptor_symbol = dup(STDOUT_FILENO); //保存标准输出文件描述符
if(!IsFileExist(NSWT_LOG_DIRECTORY)){
mkdir(NSWT_LOG_DIRECTORY,S_IRWXU | S_IRWXG | S_IRWXO);
}
log_file_descriptor_symbol = open(NSWT_LOG_FILE,(O_RDWR | O_CREAT | O_TRUNC), 0664);
if(log_file_descriptor_symbol > 0){
dup2(log_file_descriptor_symbol,STDOUT_FILENO); //用fd替换标准输出
log_debug(debug_level,"\n wt_log_ouput_init initialization completed! \n");
}
}
void wt_log_output_restore(){
close(log_file_descriptor_symbol);
dup2(save_standard_output_descriptor_symbol,STDOUT_FILENO); //用完之后恢复标准输出到屏幕
}
void wt_log_test(){
log_debug(debug_level,"The moniter debug level is set successfully!");
log_info(debug_level,"The moniter debug level is set successfully!");
log_warn(debug_level,"The moniter debug level is set successfully!");
log_err(debug_level,"The moniter debug level is set successfully!");
}
void wt_set_background_application_log_level(int log_level){
switch (log_level) {
case 0:
debug_level = LOG_LEVEL_OFF;
break;
case 1:
debug_level = LOG_LEVEL_FATAL;
break;
case 2:
debug_level = LOG_LEVEL_ERR;
break;
case 3:
debug_level = LOG_LEVEL_WARN;
break;
case 4:
debug_level = LOG_LEVEL_INFO;
break;
case 5:
debug_level = LOG_LEVEL_ALL;
break;
default:
debug_level = LOG_LEVEL_OFF;
break;
}
}
int main()
{
//sleep(15); //wait for the system init
wt_log_ouput_init();
wt_set_background_application_log_level(LOG_LEVEL_ALL);
wt_log_test();
log_debug(debug_level,"exit \n");
wt_log_output_restore();
return 0;
}
使用提示:将上方代码做成源文件即可!
最后
虽然,在Linux中还可以通过很多种方式实现日志收集,例如syslog等操作,但在,这里小菜鸡只是利用了printf实现了日志收集的功能,主要考虑的是项目的可移植性问题,通过C语言原生的库函数实现,意味着只要移植的平台可以支持C语言,即可兼容!同时,还可以避免使用第三方工具出现的漏洞问题,例如,前段时间出现的Log4j漏洞问题,本文虽然简陋,但却是一个非常经典的参考~
Sentence one day
The Master said ,"When we see men of worth,we should think of equaling them;when we see men of a contrary character,we should turn inwards and examine ourselves."
点击名片 关注小菜鸡学Linux