在嵌入式Linux设备中,系统资源比较有限,在产品开发时我们会打印很多的日志信息方便工程调试和问题定位。但是在产品发布的时候,为了节省系统资源,那些调试信息或是一些不总要的信息就不需要再记入到日志当中,所以我们需要设置相应的日志等级。
Linux内核调试信息printk函数,它的输出等级在Linux内核中已经帮我们定义好。可以通过命令查看和设置系统日志等级:
/ # cat /proc/sys/kernel/printk
4 4 1 7
/ #
/ #
/ # echo 7 4 1 7 > /proc/sys/kernel/printk
/ #
/ #
/ # cat /proc/sys/kernel/printk
7 4 1 7
这四个值分别表示:控制台日志级别;默认的消息日志级别;最低的控制台日志级别,默认的控制台日志级别。这四个值是在kernel的printk.c文件中被定义,数值越小,优先级越高。如果要修改默然的开机启动日志级别,可以直接修改printk.c中的定义。
对于应用层的日志输出控制,一般的应用会有自己的日志系统。如果没有日志系统,或是使用的日志系统比较混乱,也可以自己定义一套属于自己的日志系统。
#ifdef __cplusplus
extern "C" {
#endif
/* __cplusplus */
/*
* Name: debug.h
* Purpose: general debug system
* Created By: licaibiao
* Created Date: 2016.11.10
* ChangeList:
*/
/*must use _B_ ,have a DEBUG_ in Jason*/
#ifndef _DEBUG_B_H_
#define _DEBUG_B_H_
/*
* debug control, you can switch on (delete 'x' suffix)
* to enable log output and assert mechanism
*/
#define CONFIG_ENABLE_DEBUG
/*
* debug level,
* if is DEBUG_LEVEL_DISABLE, no log is allowed output,
* if is DEBUG_LEVEL_ERR, only ERR is allowed output,
* if is DEBUG_LEVEL_INFO, ERR and INFO are allowed output,
* if is DEBUG_LEVEL_DEBUG, all log are allowed output,
*/
enum debug_level {
DEBUG_LEVEL_DISABLE = 0,
DEBUG_LEVEL_ERR,
DEBUG_LEVEL_INFO,
DEBUG_LEVEL_DEBUG
};
#ifdef CONFIG_ENABLE_DEBUG
/* it can be change to others, such as file operations */
#include
#define PRINT printf
#define debug DEBUG_LEVEL_DISABLE
#define ASSERT()
do {
PRINT("ASSERT: %s %s %d",
__FILE__, __FUNCTION__, __LINE__);
while (1);
} while (0)
#define ERR(...)
do {
if (debug >= DEBUG_LEVEL_ERR) {
PRINT(__VA_ARGS__);
}
} while (0)
#define INFO(...)
do {
if (debug >= DEBUG_LEVEL_INFO) {
PRINT(__VA_ARGS__);
}
} while (0)
#define DEBUG(...)
do {
if (debug >= DEBUG_LEVEL_DEBUG) {
PRINT(__VA_ARGS__);
}
} while (0)
#else /* CONFIG_ENABLE_DEBUG */
#define ASSERT()
#define ERR(...)
#define INFO(...)
#define DEBUG(...)
#endif /* CONFIG_ENABLE_DEBUG */
#endif /* _DEBUG_H_ */
#ifdef __cplusplus
}
#endif /* __cplusplus */
在应用程序中可以直接包含头文件调用接口:
ERR("This is a test interface
");
对于linux应用层的日志信息,如果要将日志写入到文件中,且能够打印时间戳等信息,可以使用下面的接口:
#ifndef __GPS_DEBUG_H__
#define __GPS_DEBUG_H__
#include
#include
#include
#include
#define MAX_LOGFILE_SIZE 50000
#define LOGFLAG 1
enum UserDefineLogNum
{
GENERAL_LOG_NUM=1,
DAEMON_LOG_NUM,
ROUTER_LOG_NUM,
MODEM_LOG_NUM,
PERIPHER_LOG_NUM,
GPSSERVICE_LOG_NUM,
CAPTURE_LOG_NUM,
OTHER_LOG_NUM,
};
#if LOGFLAG > 0
#define TRACE_TRACE_GENERAL Trace(GENERAL_LOG_NUM, __LINE__)
#define TRACE_DAEMON Trace(DAEMON_LOG_NUM, __LINE__)
#define TRACE_ROUTER Trace(ROUTER_LOG_NUM, __LINE__)
#define TRACE_MODEM Trace(MODEM_LOG_NUM, __LINE__)
#define TRACE_PERIPHER Trace(PERIPHER_LOG_NUM, __LINE__)
#define TRACE_GPSSERVICE Trace(GPSSERVICE_LOG_NUM, __LINE__)
#define TRACE_CAPTURE Trace(CAPTURE_LOG_NUM, __LINE__)
#define TRACE_OTHER Trace(OTHER_LOG_NUM, __LINE__)
#else
#define TRACE printf
#define TRACE_GENERAL printf
#define TRACE_DAEMON printf
#define TRACE_GPS printf
#define TRACE_CAPTURE printf
#define TRACE_PPP printf
#define TRACE_COMM printf
#define TRACE_UPDATE printf
#endif
#define SYSLOGDIR "/mnt/log/"
#define SYSLOGDISK "/mnt/"
#define MIX_AVAILABLE_DISK (1024+512)
#define TRACE_HST (printf("%s(%d)-<%s>: ",__FILE__, __LINE__, __FUNCTION__), printf)
class Trace
{
public:
Trace(UserDefineLogNum LogNum, int nLineNo ):defineLogNum(LogNum),m_nLineNo(nLineNo)
{
}
inline void operator()(const char *pszFmt, ...) const
{
va_list ptr;
va_start(ptr, pszFmt);
TraceV(m_nLineNo,pszFmt,ptr);
va_end(ptr);
}
private:
unsigned int availableDisk(const char * pcDir) const
{
if(NULL == pcDir)
return -1;
struct statfs diskInfo;
unsigned int blocksize = 0; //每个block里包含的字节数
unsigned int availableDisk = 0; //可用空间大小
int iRet = -1;
iRet = statfs(pcDir, &diskInfo);
if(0 == iRet)
{
blocksize = diskInfo.f_bsize;
availableDisk = diskInfo.f_bavail * blocksize/1024;//(K)
}
return availableDisk;
}
void TraceV(int nLine,const char *pszFmt, va_list args) const
{
time_t tNow;
time(&tNow);
struct tm *time;
time=localtime(&tNow);
char log_path[64] ={0};
char log_dir[64] ={0};
if(time->tm_year-100 < 16 && time->tm_year-100 > 30)
{
time->tm_year =116;
time->tm_mon =0;
}
if(MIX_AVAILABLE_DISK>=availableDisk(SYSLOGDISK))
{
system("rm -rf /mnt/log/*"); //统一删除不做覆盖
return;
}
sprintf(log_dir,"%sl-%02d%02d%02d",SYSLOGDIR,(time->tm_year-100),(1+time->tm_mon),(time->tm_mday));
if ( 0 != access(log_dir, F_OK) )
{
if( mkdir(log_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)==0 )
{
printf("[%s:%d] create dir %s ok!
", __func__, __LINE__,log_dir);
}
else
{
printf("[%s:%d] create dir %s error!
", __func__, __LINE__,log_dir);
perror("mkdir");
return;
}
}
sprintf(log_path,"%s/1000000%d.log",log_dir,defineLogNum);
FILE* pFile=fopen(log_path,"a+");
if (pFile==NULL)
{
vprintf(pszFmt,args);
return;
}
fprintf(pFile,"%02d-%02d %02d:%02d:%02d : ",time->tm_mon+1,time->tm_mday,time->tm_hour,time->tm_min,time->tm_sec);
vfprintf(pFile,pszFmt,args);
fclose(pFile);
return;
}
private:
UserDefineLogNum defineLogNum;
const int m_nLineNo;
};
#endif
上面代码可以实现不同进程调用同一套接口,根据时间和预定义的名字将日志文件打印到相应的文件中去。