/********************************************************************** * 版权所有 (C)2015, 王茂春。 * * 文件名称:WriteLog.c * 文件标识:无 * 内容摘要:演示日志信息的打印方法 * 其它说明:无 第一,本文中对日志信息的写入采用的是直接在日志文件后面追加的方式,因此每次测试之前,要在“log”目录下删除上一次产生的“WriteLog.log”文件, 否则新的日志信息会写入旧的日志文件中。 第二,由于写日志函数WriteLogFile的入参较多,每次调用的时候编写代码较为繁琐,因此使用一个宏WRITELOGFILE来代替,且只需要带上日志等级和日志消息两个参数即可, 其它的如代码文件名、函数名和代码行数直接使用系统自定义的宏即可。 * 完成日期:20160522 * **********************************************************************/ #include <stdio.h> #include <string.h> #include <time.h> #include <sys/time.h> // 函数宏定义 #define WriteLogFile(level, msg) writeLog((char *)__FILE__, (char *)__FUNCTION__, __LINE__, level, (char *)msg) // 全局变量 typedef char INT8; INT8 LogFileName[100] = "WriteLog.log"; // 带路径的日志文件名 // 函数声明 void writeLog(char *pFileName,char *pFunctionName,int iCodeLine,int iLogLevel,char *pContent); void GetTime(char *pszTimeStr); /********************************************************************** * 功能描述: 获取时间串 * 输入参数: pszTimeStr-时间串 * 输出参数: pszTimeStr-时间串 * 返 回 值: 无 * 其它说明: 时间串样式: YYYY.MM.DD HH:MIN:SS.Usec * 修改日期 版本号 修改人 修改内容 * ------------------------------------------------------------------- * 2016年5月22日 V1.0 王茂春 创建 ********************************************************************/ void GetTime(char *pszTimeStr) { struct tm tSysTime = {0}; struct timeval tTimeVal = {0}; time_t tCurrentTime = {0}; INT8 szUsec[20] = {0}; // 微秒 INT8 szMsec[20] = {0}; // 毫秒 if (pszTimeStr == NULL) { return; } tCurrentTime = time(NULL); localtime_r(&tCurrentTime, &tSysTime); // localtime_r是线程安全的 gettimeofday(&tTimeVal, NULL); sprintf(szUsec, "%06ld", tTimeVal.tv_usec); // 获取微秒 strncpy(szMsec, szUsec, 3); // 微秒的前3位为毫秒(1毫秒=1000微秒) sprintf(pszTimeStr, "[%04d.%02d.%02d %02d:%02d:%02d.%3.3s]", tSysTime.tm_year+1900, tSysTime.tm_mon+1, tSysTime.tm_mday, tSysTime.tm_hour, tSysTime.tm_min, tSysTime.tm_sec, szMsec); } /********************************************************************** * 功能描述: 将内容写到日志文件中 * 输入参数: pFileName-代码文件名 pFunctionName-代码所在函数名 iCodeLine-代码行 iLogLevel-日志等级(0,1,2,3) pContent-每条日志的具体内容 * 输出参数: 无 * 返 回 值: 无 * 其它说明: 无 * 修改日期 版本号 修改人 修改内容 * ------------------------------------------------------------------- * 20160522 V1.0 王茂春 创建 ********************************************************************/ void writeLog(char *pFileName,char *pFunctionName,int iCodeLine,int iLogLevel,char *pContent) { char szTimeStr[128] = {0}; char szLogContent[128] = {0}; FILE *fp = NULL; //判断文件名,函数名,日志文件名是否为空 if (pFileName == NULL || pFunctionName == NULL || LogFileName == NULL ) { printf("eer"); return; } fp = fopen(LogFileName, "at+"); // 打开文件, 每次写入的时候在后面追加 if (fp == NULL) { return; } // 先打印版本相关信息 //snprintf(szLogContent, sizeof(szLogContent)-1, "/******Version [1.0], Build time[%s %s].****/ ", __DATE__, __TIME__); //fputs(szLogContent, fp); // 写入日志时间 GetTime(szTimeStr); fputs(szTimeStr, fp); //在日志信息中显示"文件名/函数名/代码行数"信息 //0 === 严重错误 if (0 == iLogLevel) { snprintf(szLogContent, sizeof(szLogContent)-1, "[%s][%s][%04d][%s]%s ", pFileName, pFunctionName, iCodeLine, "LOG_FATAL", pContent); } //1 === 一般错误 if(1 == iLogLevel) { snprintf(szLogContent, sizeof(szLogContent)-1, "[%s][%s][%04d][%s]%s ", pFileName, pFunctionName, iCodeLine, "LOG_ERROR", pContent); } //2 === 警告 if(2 == iLogLevel) { snprintf(szLogContent, sizeof(szLogContent)-1, "[%s][%s][%04d][%s]%s ", pFileName, pFunctionName, iCodeLine, "LOG_WARN", pContent); } //3 === 一般信息 if(3 == iLogLevel) { snprintf(szLogContent, sizeof(szLogContent)-1, "[%s][%s][%04d][%s]%s ", pFileName, pFunctionName, iCodeLine, "LOG_INFO", pContent); } fputs(szLogContent, fp); fflush(fp); // 刷新文件 fclose(fp); // 关闭文件 fp = NULL; // 将文件指针置为空 return; } void test() { WriteLogFile(2,"hhh----fun"); WriteLogFile(3,"hhh----fun"); } int main() { WriteLogFile(0,"hhh----main"); WriteLogFile(1,"hhh----main"); test(); }
本文借鉴 周兆雄 博客,写一个自己的简单的日志文件。
优点:
1. 仅仅提供三个入口:日志文件名、错误等级、错误信息,其他(所属文件名,函数名,所在行,错误等级字符均隐藏)
2. 使用了宏定义函数,简化函数操作
3.文件操作使用snprintf比sprintf更优秀。
缺点:
1.文件采用无限追加模式,每次运行前需要删除日志
2.原博客也是对“const char *"转换为”char ")经常错误出现,这里的解决办法使用强制转换,比如:
writeLog((char *)__FILE__, (char *)__FUNCTION__, __LINE__, level, (char *)msg)
3.本文为了简便起见,对错误等级使用if判断,而没有使用switch语句,笨方法。