1、日志系统用来干什么的?
在大型软件系统中,为了检测运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件,在日志文件中存放程序流程中的一些重要信息,包括:变量名称及其值、消息结构定义、函数返回值及其执行情况、脚本执行及调用情况等。通过阅读日志文件,我们能很快地跟踪程序流程,并发现程序地问题。因此,熟练掌握日志系统地编写方法并快速地阅读日志文件,是对一个软件开发工程师地基本要求。
2、打印等级
函数需要有打印等级,目前分为ERROR、WARNING、INFO、DEBUG几种,等级越高,数字越小。在使用时需要根据实际情况,出错的情况使用ERROR,不出错可以使用INFO。为代码简洁起见,使用gcc扩展语法。
代码如下:
static const char *s_loginfo[] = {
"ERROR","WARN","INFO","DEBUG"
};
这样通过s_loginfo数组即可以获取等级对应的字符串。注意,这种语法只能在c语言中使用,c++代码编译出错。
3、可变参数宏__VA_ARGS__
__VA_ARGS__是一个可变参数的宏(gcc支持),实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点…),这样预定义宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。加##用来支持0个可变参数的情况。
可变参数宏就像下面这个样子:
#define debug(...) printf(__VA_ARGS__)
缺省号代表一个可以变化的参数表,使用保留名__VA_ARGS__把参数传递给宏,当宏的调用展开时,实际的参数就传递给了printf(),例如:
debug("Y = %d\n", y);
而处理器会把宏的调用替换成:
printf("Y = %d\n",y);
因为debug()是一个可变参数宏,你能在每一次调用中传递不同数目的参数:
debug("test"); //一个参数
带##的可变参数宏的例子
#define PRINT(s, ...) printf(s, ##__VA_ARGS__)
如果我们调用宏
PRINT("hello world\n");//这里没有可变参数,所以要用##__VA_ARGS__
这个时候处理器会把宏的调用替换成:
printf("hello world\n");
如果调用以下宏:
PRINT("hello, %s\n", "i love you");
这个时候处理器会把宏的调用替换成:
printf("hello, %s\n","i love you");
4、va_arg,va_list,va_start,va_end的理解
可变参数用到以下宏函数:
void func(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
va_arg(ap, int);
va_end(va);
}
这里ap其实就是一个指针,指向了参数的地址。
va_start()所做的就是让ap指向函数的最后一个确定的参数(声明程序中是fmt)的下一个参数的地址。
va_arg()所做的就是根据ap指向的地址,和第二个参数所确定的类型,将这个参数中的数据提取出来,作为返回值,同时让ap指向下一个参数。
va_end()所做的就是让ap这个指针指向0。
关于这三个参数实现的宏可以参看下面的实现:
// 使ap指向第一个可变参数的地址
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
// 使ap指向下一个可变参数,同时将目前ap所指向的参数提取出来并返回
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
// 销毁ap
#define va_end(ap) ( ap = (va_list)0 )
5、Linux下使用c编写的简单日志系统
log.c的代码:
/********************************************************************************
* Copyright: (C) 2020 makun<1394987689@qq.com>
* All rights reserved.
*
* Filename: log.c
* Description: This head file
*
* Version: 1.0.0(08/08/20)
* Author: makun <1394987689@qq.com>
* ChangeLog: 1, Release initial version on "08/08/20 12:45:11"
*
********************************************************************************/
#include"log.h"
FILE * g_file = NULL;
//gcc扩展语法,通过s_loginfo数组可以获取等级对应的字符串
static const char* s_loginfo[] =
{
"ERROR","INFO"
};
/* 创建文件记录error和warn信息*/
FILE* log_open()
{
g_file = fopen(LOGFILENAME,"a");
if(NULL == g_file)
return NULL;
}
int log_write(char* file, const char *func, int line, enum
en_log_level level, const char *fmt, ...)
{
char buf[LOG_BUFFSIZE];
char log_buf[LOG_BUFFSIZE];
va_list arg_list;
int millisec;
off_t filesize;
memset(buf,0,sizeof(buf));
va_start(arg_list,fmt);
vsnprintf(buf,sizeof(buf),fmt,arg_list);
va_end(arg_list);
if(level > INFO)
{
return -1;
}
snprintf(log_buf,sizeof(buf),"%s,File: %s, Func: %s, Line :%d,information is: %s",s_loginfo[level],file, func,
line, buf);
log_print(log_buf);
}
int log_print(char *log_msg)
{
if (NULL == log_msg)
{
log_error("Error input arguemwnts");
return -1;
}
char log_buf[LOG_BUFFSIZE];
snprintf(log_buf,LOG_BUFFSIZE, "mqtt log message:\n %s \n",log_msg);
fwrite(log_buf,strlen(log_buf),1,g_file);
log_roll_back(LOGFILENAME);
return 0;
}
int log_roll_back()
{
FILE *fp;
if( (fp = fopen(LOGFILENAME,"r")) == NULL)
{
return -1;
}
fseek(fp,0L,SEEK_END);
long size = ftell(fp);
fclose(fp);
if(size >= MAX_ROLL_LOG_SIZE)
{
char name_str[32]={0};
strcpy(name_str,LOGFILENAME);
strcat(name_str,(char *)".bak");
remove(name_str);
if( !rename(LOGFILENAME,name_str));
{
return -1;
}
}
return 0;
}
/* 程序结束后调用,关闭日志文件*/
int log_close()
{
fclose(g_file);
}
log.h
#ifndef _LOG_H_
#define _LOG_H_
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<time.h>
#include <stdarg.h>
enum en_log_level
{
ERROR,
INFO
};
#define LOG_BUFFSIZE 256
#define MAX_ROLL_LOG_SIZE (10*1024)
#define LOGFILENAME "mqtt_message.log"
#define log_info(fmt,...) log_write(__FILE__,__func__,__LINE__,INFO,fmt,##__VA_ARGS__)
#define log_error(fmt,...)log_write(__FILE__,__func__,__LINE__,ERROR,fmt,##__VA_ARGS__)
extern FILE *g_file;
extern FILE* log_open();
extern int log_close();
extern int log_print(char *log_msg);
extern int log_roll_back();
extern int log_write(char* file, const char *func, int line, enum en_log_level, const char *fmt, ...);
#endif /* ----- #ifndef _LOG_H_ ----- */