C语言中日志模块的使用

       在产品研发的过程中,经常需要借助打印信息来帮助调试和后期维护。所以拥有一个完善的日志模块是至关重要的。

如下是从经手的项目中整理出来的日志模块及使用示例,以备后续项目开发时使用。

log.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <pthread.h>
#include "log.h"

/*存储日志的文件名*/

static unsigned char g_ucLogFileName[MAX_LOG_FILE_NUM][STR_COMM_SIZE] = {{0}};

/*指明是g_ucLogFileName中的哪个文件*/
static unsigned char g_ucLogFileNo = 0;

/*输出日志位置标记,0-输出到终端,1-输出到日志文件*/
unsigned long g_ulPrintLogPlaceFlag = 0;
/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
unsigned long g_ulPrintDebugLogFlag = 0;

/*日志文件大小*/
static unsigned long g_ulLogFileSize = 0;

/*日志文件句柄*/
static FILE* pFile = NULL;

/*日志存储互斥锁*/
static pthread_mutex_t g_stSaveLogMutexLock;

/*日志模块初始化标记*/
static unsigned long g_ulLogInitFlag = 0;


void LOG_SetPrintLogPlaceFlag(unsigned long flag)
{
	g_ulPrintLogPlaceFlag = flag;
}

void LOG_SetPrintDebugLogFlag(unsigned long flag)
{
	g_ulPrintDebugLogFlag = flag;
}


/*****************************************************************
** 函数名: get_file_size
** 输 入: char *path
** 输 出:
** 功能描述:获取指令文件大小
** 返回值: long
****************************************************************/
static long get_file_size(const char *path)
{
	long filesize = -1;
	struct stat statbuff;
	
	if(stat(path, &statbuff) < 0){
	    return filesize;
	}
	else{
	    filesize = statbuff.st_size;
	}
	return filesize;
}

/*****************************************************************
** 函数名: unsigned long LOG_PrintLogTime
** 输 入:  unsigned long ulBufLen 存储时间的空间长度
** 输 出:unsigned char *ucTime  存储时间
** 功能描述:日志输出
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_PrintLogTime(unsigned char *ucTime, unsigned long ulBufLen)
{
    struct tm* pstTmSec;
    struct timeval stTmMsec;
	
	if(NULL == ucTime)
	{
		return -1;
	}
	gettimeofday(&stTmMsec, NULL);
	pstTmSec = localtime(&stTmMsec.tv_sec);
	snprintf((char *)ucTime, ulBufLen - 1, "%04d-%02d-%02d %02d:%02d:%02d %03ldms",
            pstTmSec->tm_year + 1900, pstTmSec->tm_mon + 1, pstTmSec->tm_mday, pstTmSec->tm_hour, 
            pstTmSec->tm_min, pstTmSec->tm_sec, stTmMsec.tv_usec / 1000);
	
	return 0;
}

/*****************************************************************
** 函数名: 	unsigned char *LOG_LogTypeToStr
** 输 入:  	unsigned char ucType  日志类型
			unsigned long ulBufLen 存储日志类型字符串空间的长度
** 输 出:unsigned char *pucTypeString 根据日志类型将其转换成相应的字符串
** 功能描述:根据日志类型转换成相应的字符串
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_LogTypeToStr(unsigned char ucType, unsigned char *pucTypeString, unsigned long ulBufLen)
{
	if(NULL == pucTypeString)
	{
		return -1;
	}
	/*防止发生越界*/
	ulBufLen -= 1;

	switch(ucType)
	{
		case LOG_DEBUG:
		{
			strncpy((char *)pucTypeString, "DEBUG", ulBufLen);
			break;
		}
		case LOG_ERROR:
		{
			strncpy((char *)pucTypeString, "ERROR", ulBufLen);
			break;
		}
		case LOG_WARNING:
		{
			strncpy((char *)pucTypeString, "WARNING", ulBufLen);
			break;
		}
		case LOG_ACTION:
		{
			strncpy((char *)pucTypeString, "ACTION", ulBufLen);
			break;
		}
		case LOG_SYSTEM:
		{
			strncpy((char *)pucTypeString, "SYSTEM", ulBufLen);
			break;
		}
		default:
		{
			strncpy((char *)pucTypeString, "UNKNOWN", ulBufLen);
			break;
		}
	}
	return 0;
}

/*****************************************************************
** 函数名: unsigned long LOG_OpenLogFile
** 输 入:  void
** 输 出:void
** 功能描述:打开日志文件
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_OpenLogFile(void)
{
	char *path = (char*)g_ucLogFileName[g_ucLogFileNo];
	char *flag = NULL;
	int len = 0;
	
	/*判断文件是否已经打开*/
	if(NULL != pFile)
	{
		LOG_PRINT("[ACTION] file opened!");
		return 0;
	}
	/*判断文件名是否有定义*/
	if(NULL == path)
	{
		LOG_PRINT("[ERROR] file name is NULL.");
		return -1;
	}
	
	/*判断文件是否存在*/
	if (!access(path, 0))
	{
		/*获取文件大小*/
		if (0 > (len = get_file_size(path)))
		{
			LOG_PRINT("[ERROR] get file size failed!");
			return -1;
		}
	}
	flag = (len > 0 && len < g_ulLogFileSize) ? "a" : "w";
	
	/*打开文件*/
	pFile = fopen(path, flag);
	if(NULL == pFile)
	{
		LOG_PRINT("[ERROR] open file failed!");
		return -1;
	}
	LOG_PRINT("[DEBUG] open file name = %s", path);
	return 0;
}

/*****************************************************************
** 函数名: LOG_PrintLog
** 输 入:  unsigned char *ucLogInfo  需要打印或者存储的日志信息
			unsigned char ucType 日志类型
** 输 出:void
** 功能描述:日志输出
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo)
{
	unsigned long ulResult = 0;
	unsigned long ulFileLen = 0;
	unsigned char ucTime[STR_COMM_SIZE] = {0};
	unsigned char ucLogTypeStr[STR_COMM_SIZE] = {0};
	unsigned char ucLogInfo[STR_MAX_SIZE] = {0};
	
	if(NULL == pucLogInfo)
	{
		return -1;
	}
	
	/*将日志类型转换成字符串*/
	ulResult = LOG_LogTypeToStr(ucType, ucLogTypeStr, sizeof(ucLogTypeStr));
	/*获取生成日志的时间*/
	ulResult += LOG_PrintLogTime(ucTime, sizeof(ucTime));
	if(0 != ulResult)
	{
		return -1;
	}
	snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, "[%s] [%s] %s", ucTime, ucLogTypeStr, pucLogInfo);
	/*判断是否打印调试日志*/
	if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag)
	{
		printf("%s", ucLogInfo);
		return 0;
	}
	/*加锁保护文件操作*/
	pthread_mutex_lock(&g_stSaveLogMutexLock);
	/*打开日志文件*/
	(void)LOG_OpenLogFile();
	if(NULL != pFile)
	{
		fputs((char *)ucLogInfo, pFile);
		ulFileLen = ftell(pFile);
		LOG_PRINT("file len = %ld", ulFileLen);
		if(ulFileLen >= g_ulLogFileSize)
		{
			fclose(pFile);
			pFile = NULL;
			g_ucLogFileNo = (g_ucLogFileNo + 1) % MAX_LOG_FILE_NUM;
		}
	}
	pthread_mutex_unlock(&g_stSaveLogMutexLock);
	return 0;
}

/*****************************************************************
** 函数名: LOG_Init
** 输 入:  const unsigned char* ucLogFileName  用来保存日志的文件名
			unsigned long ulFileSize 存储日志的文件大小
** 输 出:void
** 功能描述:日志打印初始化
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize)
{
	unsigned int i = 0;
	/*判断参数的合法性*/
	if((NULL == ucLogFileName) || !(ulFileSize > 0))
	{
		return -1;
	}
	/*判断是否将日志输出到日志文件*/
	if((PRINT_LOG_TO_FILE != g_ulPrintLogPlaceFlag) || (0 != g_ulLogInitFlag))
	{
		printf("g_ulPrintLogPlaceFlag = %ld g_ulLogInitFlag = %ld\n",g_ulPrintLogPlaceFlag,g_ulLogInitFlag);
		LOG_PRINT("print log to termination!!");
		return 0;
	}
	
	/*记录日志模块已经被初始化(防止改模块被重复初始化)*/
	g_ulLogInitFlag = 1;
	
	/*生成存储日志的文件名*/
	for(i = 0; i < NUMBER(g_ucLogFileName); i++)
	{
		snprintf((char *)g_ucLogFileName[i], sizeof(g_ucLogFileName[i]) - 1, "%s_%02d", ucLogFileName, i);
		LOG_PRINT("Log File: %s", g_ucLogFileName[i]);
		printf("Log File: %s\n", g_ucLogFileName[i]);
	}
	/*设置日志文件大小*/
	g_ulLogFileSize = ulFileSize;
	pthread_mutex_init(&g_stSaveLogMutexLock, NULL);

	return 0;
}

/*****************************************************************
** 函数名: LOG_Destroy
** 输 入:  void
** 输 出:void
** 功能描述:日志打印资源释放
** 返回值:unsigned long
****************************************************************/
void LOG_Destroy(void)
{
	if(pFile != NULL)
	{
		fclose(pFile);
		pFile = NULL;
	}
	pthread_mutex_destroy(&g_stSaveLogMutexLock);
	return;
}

log.h

#ifndef _LOG_H_
#define _LOG_H_
#include <string.h>

/*通用字符串存储大小定义*/
#define STR_COMM_SIZE 128
#define STR_MAX_SIZE 1024

#define MAX_LOG_FILE_NUM		  (3)

#define NUMBER(type) sizeof(type)/sizeof(type[0])

#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)

/*日志类型*/
enum
{
    LOG_DEBUG = 0,/*调试日志*/
    LOG_ERROR,/*错误日志*/
    LOG_WARNING,/*告警日志*/
    LOG_ACTION,/*运行日志*/
    LOG_SYSTEM,/*系统日志*/
    BUTTOM
};

/*将日志输出到终端*/
#define PRINT_LOG_TO_TERM (0)
/*将日志输出到文件中*/
#define PRINT_LOG_TO_FILE (1)

/*调试日志宏定义*/
#define DEBUG_PRINT 0
#define LOG_PRINT(fmt, ...) do{\
	if(DEBUG_PRINT)\
	{\
		printf(fmt"  [line:%d] [%s]\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\
	}\
}while(0);

/*错误日志打印(在日志打印模块还未启动时使用)*/
#define LOG_ERR(fmt, ...) do{\
	printf("[ERROR]  "fmt"  [line:%d] [%s]\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\
}while(0);



/*存储日志标记. 0-不存储日志, 1-存储日志*/
extern unsigned long g_ulPrintLogPlaceFlag;

/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
extern unsigned long g_ulPrintDebugLogFlag;

unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo);


/*日志打印宏定义*/
#define LOG_INFO(type, fmt, ...) do{\
	if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag) \
	{ \
		if((0 == g_ulPrintDebugLogFlag) && (LOG_DEBUG == type)) \
		{\
			break;\
		}\
    	unsigned char ucLogInfo[STR_MAX_SIZE] = {0}; \
		snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \
		LOG_PrintLog(type, ucLogInfo); \
	} \
	else \
	{ \
		unsigned char ucLogInfo[STR_MAX_SIZE] = {0}; \
		snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \
		LOG_PrintLog(type, ucLogInfo); \
	} \
}while(0)


/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
extern void LOG_SetPrintDebugLogFlag(unsigned long flag);
/*存储日志标记. 0-不存储日志, 1-存储日志*/
extern void LOG_SetPrintLogPlaceFlag(unsigned long flag);

extern unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize);
extern void LOG_Destroy(void);

#endif //_LOG_H_




main.c

#include <stdio.h>
#include "log.h"

int test_func1()
{
	LOG_INFO(LOG_DEBUG, "%s", "hello world!!"); 
	return 0;
}

int main(int argc, char *argv[])
{
	LOG_SetPrintDebugLogFlag(1);//打印调试信息
	//LOG_SetPrintLogPlaceFlag(1);//保存打印信息到文件
	LOG_Init("info", 8000);
	
	LOG_INFO(LOG_DEBUG, "%s", "Init log!!");
	
	test_func1();
	
	LOG_INFO(LOG_DEBUG, "%s", "Destroy log!!");
	LOG_Destroy();
}

执行结果:

       由执行结果可以看出一条打印信息包含了时间、日志类型、内容、文件名、行号、函数名等信息,这样就能够方便快速的定位问题,其使用方法也与printf函数类似,简单方便。

       其次该日志模块还支持将日志存至文件,可以自行定义文件的大小及个数,日志循环覆盖。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值