linux自定义log日志
近期调式项目需要,要在设备上加入log功能,网上有了解到Linux下自带syslog,没有具体研究,好像需要配置。然后想自己实现一个log功能,于是开始整理思路:
- 代码中加入log点能够打印到文件;
- 文件达到一定大小自动切换到另外一个文件,两个文件交替使用;
- 保证实时性
一开始查找到一些把程序运行的打印直接打印到文件方法,如下:
1)假设我的程序是test,运行test
$ test > result.txt
这样printf的输出就存储在result.txt中了。
2)如果既想打印在终端,又想保存在文件,还可以使用tee命令
$ test | tee result.txt
3)还有一个方法就是使用fprintf函数了
以上1)、2)两种方式在普通测试的小程序上可以用,到了大的应用程序不是很方便,无法控制文件大小。方式3)使用的时候,需要重复打开关闭文件,考虑到打印的内容频率较高,遂放弃。
重新寻找方法,使用echo
首先打印到文件,其实就是自定义打印函数,把常用的printf改成自己的打印接口。
#define _PRTLOG
#ifdef _PRTLOG
#define PRI_LOG(fmt, args...) PrtLog(eLogDebug,"[TEST] [%s: %d] "fmt"", __func__, __LINE__, ##args);
#define PRI_ERR_LOG(fmt, args...) PrtLog(eLogError,"\033[1;31m[TEST_ERR] [%s: %s: %d] "fmt" \033[0m\n", __FILE__, __func__, __LINE__, ##args)
#define PRI_LOG_FO(fmt, args...) PrtLog(eLogFileOnly,"[TEST] [%s: %d] "fmt"", __func__, __LINE__, ##args);
#else
#define PRI_LOG(fmt, args...)
#define PRI_ERR_LOG(fmt, ...)
#define PRI_LOG_FO(fmt, args...)
然后就是实现PrtLog函数,下面代码中的SemTake、SemGive是linux下信号量的获取与释放,这里是重新封装了一下,也可以直接用linux的信号量接口。GetLogTime是获取系统时间,通过gettimeofday函数就可以获取到,这个在之前的文章中有提到。
/* **********************************************************************************
* Name : PrtLog
* Description :
* Input :
* priority :level of log, LOG_ERR, LOG_DEBUG etc.
* fmt :format of message to log
* ... :args follow by fmt
* Output :
* Return :
* Note : 打印日志信息到文件,单次打印最大字符串长度1024
* **********************************************************************************/
void PrtLog(int priority, char* fmt, ...)
{
int nlogsize = 0;
char abyTime[32] = {0};
char abyCmd[ONE_MSG_MAX+64] = {0};
char priVc[][9] = {"Emerg", "Alert", "Crit", "Error", "Warning", "Notice", "Info", "Debug", "FileOnly"};
SemTake(&g_LogSem);//自定义部分
memset(LogLastMsg, 0, ABOX_ONE_MSG_MAX);
GetLogTime(abyTime);//自定义部分
char* priPt = priority < 0 || priority >= sizeof(priVc)/sizeof(priVc[0]) ?
"Unknow priority!" : priVc[priority];
va_list argPt;
unsigned Ln;
va_start(argPt, fmt); //now argPt is point to log's param:...
Ln = snprintf(LogLastMsg, sizeof(LogLastMsg), "[%s][%s]: ", abyTime, priPt);
Ln += vsnprintf(LogLastMsg + Ln, sizeof(LogLastMsg) - Ln, fmt, argPt);
va_end(argPt);
//choose the log which should be show on stderr
if (priority < LOG_ERR || priority <= Log2Stderr)
{
fprintf(stderr, "%s\n", LogLastMsg);
}
//每次打印长度累加
nlogsize = strlen(LogLastMsg);
g_LogSize += nlogsize;
//此处实现log自动切换
if (0 == g_byCurLogFile)
{
//当前使用log0,大小超限切换到log1
if (g_LogSize >= ABOX_LOG_MAX)
{
//清空log1
sprintf(abyCmd, "echo -e \"log1 start\" > %s", LOG_PATH1);
system(abyCmd);
//写入log1 -e 启用解释反斜杠的转义功能 -n 不尾随换行符
sprintf(abyCmd, "echo -e -n \"%s\" >> %s", LogLastMsg, LOG_PATH1);
system(abyCmd);
g_LogSize = nlogsize;
g_byCurLogFile = 1;
}
else
{
//写入log0
sprintf(abyCmd, "echo -e -n \"%s\" >> %s", LogLastMsg, LOG_PATH0);
system(abyCmd);
}
}
else
{
//当前使用log1,大小超限切换到log0
if (g_LogSize >= LOG_MAX)
{
//清空log0
sprintf(abyCmd, "echo -e \"log0 start\" > %s", LOG_PATH0);
system(abyCmd);
//写入log1
sprintf(abyCmd, "echo -e -n \"%s\" >> %s", LogLastMsg, LOG_PATH0);
system(abyCmd);
g_LogSize = nlogsize;
g_byCurLogFile = 0;
}
else
{
//写入log1
sprintf(abyCmd, "echo -e -n \"%s\" >> %s", LogLastMsg, LOG_PATH1);
system(abyCmd);
}
}
if (eLogFileOnly != priority)//头文件定义priority枚举
{
//此处打印到终端
printf("%s",LogLastMsg);
}
SemGive(&g_LogSem);//自定义部分
if (priority <= LOG_ERR)
{
exit(-1);
}
return ;
}
以上代码通过echo打印到文件,通过一个全局变量统计当前log文件的大小,超过后自动切换到另一个文件。
一开始使用fprintf函数,在初始化时,打开文件,之后一直不再关闭,直到文件大小超限。这种方式出现一个问题,fprintf输出到文件流中,若不关闭文件,每次都要在缓存中满1024个字节后,才会打印到文件,这就使得log日志不再具有实时性。后来又改成了echo的方式,使用这种方式,需要在初始化时,创建好两个log文件。
以上代码加入文件,就可以在自己 的代码中调用
PRI_LOG
打印log到终端和文件
PRI_ERR_LOG
打印出错信息到终端和文件
PRI_LOG_FO
只打印log到文件
欢迎大家补充,使用中有什么问题可以留言一起交流。
2020年9月3日星期四
参考文章:https://blog.csdn.net/aidixi4007/article/details/101152366