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 initwt_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
