目录
一、前言
二、实现一个简单的日志
1.可变参数
2.日志等级
3.日志时间
4.打印每一条参数
5.合并两个缓冲区
6.封装日志函数
三、完整代码
一、前言
当我们写一个函数,例如打开文件open,当我们打开失败的时候,会调用perror看到错误信息。
接下来我们将带大家了解日志,并自主实现一个日志系统。
这个就是我们定义的日志类的对象。
二、实现一个简单的日志
我们运行代码的时候,我们希望有各种各样的运行时候的一些信息。这也就是日志
它一半有日志时间,日志的等级,日志的内容,文件的名称和行号。
对于日志等级一半有如下的
Info:常规消息
Warning:报警信息
Error:严重,可能要立即处理
Fatal:致命的
Ddbug:调试
1.可变参数
在写日志之前,我们了解一下可变参数列表
关于可变参数我们主要用下面四个中的前三个
我们先来看一段代码
va_list s我们可以定义可以访问可变参数部分的变量,是char* 类型的。
va_start使其对象指向可变参数列表部分。
va_arg使用 va_arg
宏依次获取每个可变参数。
最后输出是正确的。
2.日志等级
我们用宏定义
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
3.日志时间
我们在用一个接口的时候,万一这个接口失败,我们要看在什么时间段出现了什么错误。
通过time接口获得时间戳。
但这个时间戳我们还要自己运算才能得到对应的年月日。
这里给大家介绍一个localtime时间类型转换函数。
这里返回的是一个名字叫tm的结构体
这里注意,mon月份从0月开始,year年份从1900年开始。
运行结果如下:
这里我们是直接打印到显示器上,但有时候我们写日志要写入到文件里,所以这个时候我们应该创建一个缓冲区,把时间先暂时放到缓冲区里。
4.打印每一条参数
我们可以用vsnprintf函数
这里我们要用到参数列表,我们把每一条参数写入到右缓冲区中。
5.合并两个缓冲区
我们把日志时间和日志参数全部写到另一个缓冲区里,如果要打印在屏幕上就直接打印,写在文件里,就把该缓冲区的数据写入。我们来测试一下。
我们这个时候已经完成了一个简易的日志系统。
接下来我们把这个函数封装一下形成一个类,我们有时候想把该日志打印到显示器,或者输出到一个文件中,或者多个文件中。
6.封装日志函数
这里我们创一个类Log
我们有时候既然想打印到显示器上或者一个文件里或者多个文件里时,我们要有一个成员变量OpenMethod,这个路径成员变量为了方便我们创建多个文件使用。
我们先把这个类的构造函数写了,我们默认是打印在显示器上的
再写两个成员函数,判断把日志写在哪里
我们先测试一下能不能打印在显示器上‘
#include <iostream>
#include <stdarg.h>
#include <ctime>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SIZE 1024
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
using namespace std;
#define Screen 1
#define Onefile 2
#define Classfile 3
#define LogFile "log.txt"
class Log
{
public:
Log()
{
OpenMethod=Screen;
path="path->";
}
void ChoseMethod(int Method)
{
OpenMethod=Method;
}
string levelToString(int level)
{
switch(level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warining";
case Error:
return "Error";
case Fatal:
return "Fatal";
}
}
void printlog(int level, const std::string &logtxt)
{
switch(OpenMethod)
{
case Screen:
std::cout << logtxt << std::endl;
break;
case Onefile:
printOneFile(LogFile, logtxt);
case Classfile:
printClassFile(level, logtxt);
break;
default:
break;
}
}
void printOneFile(const std::string &logname, const std::string &logtxt )
{
string Filename=logname+path;
int fd = open(Filename.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
if(fd<0) return;
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void printClassFile(int level, const std::string &logtxt)
{
}
void logmessage(int level, const char *format, ...)
{
//时间
time_t t = time(nullptr);
struct tm* ctime = localtime(&t);
char leftbuffer[SIZE];
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
//打印参数
char rightbuffer[SIZE];
va_list s;
va_start(s,format);
vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);
va_end(s);
//合并
char logtxt[SIZE*2];
snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);
printlog(level,logtxt);
}
private:
int OpenMethod;
string path;
}
结果显示
接下来我们把写在一个文件下和多个文件下的成员函数写了。
我们来运行一下看看。
确实是写入了。
测试一下多文件方法
都打印到各个文件中。
我们在makefile里创建一个文件夹,在我们的path初始化找到这个log文件夹。
我们看看结果。
三、完整代码
#include <iostream>
#include <stdarg.h>
#include <ctime>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SIZE 1024
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
using namespace std;
#define Screen 1
#define Onefile 2
#define Classfile 3
#define LogFile "log.txt"
class Log
{
public:
Log()
{
OpenMethod=Screen;
path = "./log/";
}
void ChoseMethod(int Method)
{
OpenMethod=Method;
}
string levelToString(int level)
{
switch(level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warining";
case Error:
return "Error";
case Fatal:
return "Fatal";
}
}
void printlog(int level, const std::string &logtxt)
{
switch(OpenMethod)
{
case Screen:
std::cout << logtxt << std::endl;
break;
case Onefile:
printOneFile(LogFile, logtxt);
break;
case Classfile:
printClassFile(level, logtxt);
break;
default:
break;
}
}
void printOneFile(const std::string &logname, const std::string &logtxt )
{
string Filename = path + logname;
int fd = open(Filename.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
if(fd<0) return;
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void printClassFile(int level, const std::string &logtxt)
{
std::string filename = LogFile;
filename += ".";
filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"
printOneFile(filename, logtxt);
}
void logmessage(int level, const char *format, ...)
{
//时间
time_t t = time(nullptr);
struct tm* ctime = localtime(&t);
char leftbuffer[SIZE];
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
//打印参数
char rightbuffer[SIZE];
va_list s;
va_start(s,format);
vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);
va_end(s);
//合并
char logtxt[SIZE*2];
snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);
printlog(level,logtxt);
}
private:
int OpenMethod;
string path;
};