C++实现一个日志库,满足以下要求:
1. 日志信息分级:FATAL(致命错误)、ERROR(一般错误)、WARN(警告)、INFO(一般信息)、DEBUG(调试信息)
2. 记录日志信息支持记录到文件、控制台、数据库(要求能支持多种数据库,比如 MSSQL MySQL SQLite等)
3. 要求能够方便配置该功能:比如 开关日志,仅记录某级别以上的日志,日志记录到哪里的功能。
4. 适当考虑多线程并发,和毫秒级快速日志记录(需要日志记录的时间间隔小于读写文件的时间间隔)
这里,配置文件使用的XML文件,解析XML文件调用的tinyxml2库。
这里XML文件中选择显示在Text和控制台,因此最终实现结果为:
在debug目录下,会有一个Log.txt文件,打开就是我们的日志信息。
代码结构如下:
上代码:
日志库头文件
#ifndef LOGOUT_H
#define LOGOUT_H
#include <string>
#include <time.h>
#include <iostream>
#include <windows.h>
#include "logxml.h"
using namespace std;
typedef enum LogLevel{
DEBUG, ///<调试信息 级别5
INFO, ///<一般信息 级别4
WARN, ///<警告信息 级别3
ERRO, ///<一般错误 级别2
FATAL, ///<致命错误 级别1
}LogLevel;
/*宏定义函数,用于获取日志函数所在行号,也是最终提供的接口*/
#define Log_Out(str,level){\
char sline[256] = {0};\
string line;\
sprintf(sline,"[File:%s Line:%04d]",__FILE__,__LINE__);\
line = sline;\
LogOut log;\
log.logOut(line,str,level);}
class LogOut
{
public:
LogOut();
void logOut(string line,string msg,LogLevel level); //输出
protected:
string getCurrentTime();//获取当前系统时间
void printLog(string line,string msg,LogLevel level); //写日志打印到控制台
void writeLog(string line,string msg,LogLevel level); //写日志到txt文件
void saveLog(string line,string msg,LogLevel level); //写日志到数据库
private:
string m_path; //储存文件路径
LogXml *m_logXml;
};
#endif // LOGOUT_H
日志库cpp
#include <fstream>
#include "logout.h"
LogOut::LogOut()
{
m_path.clear();
m_logXml = new LogXml;
}
void LogOut::logOut(string line, string msg, LogLevel level)
{
if("true" == m_logXml->getTextSwitch())
{
writeLog(line,msg,level);
}
if("true" == m_logXml->getConSwitch())
{
printLog(line,msg,level);
}
if("true" == m_logXml->getDBSwitch())
{
saveLog(line,msg,level);
}
}
string LogOut::getCurrentTime()
{
struct tm *local; //定义tm结构指针存储时间信息
time_t now; //声明time_t类型变量
timeb tb;
ftime(&tb); //微秒
now=time(NULL);//获取当前系统的日历时间
local=localtime(&now);//localtime()函数是将日历时间转化为本地时间
char temp[50] = {0};
sprintf(temp,"[%d-%d-%d %d:%d:%d:%3d]",local->tm_year + 1900,local->tm_mon + 1,local->tm_mday,
local->tm_hour,local->tm_min,local->tm_sec,tb.millitm);
string timeNow = temp;
return timeNow;
}
void LogOut::printLog(string line, string msg, LogLevel level)
{
switch(level)
{
case DEBUG:
cout<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->DEBUG"<<endl;
break;
case INFO:
cout<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->INFO"<<endl;
break;
case WARN:
cout<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->WARN"<<endl;
break;
case ERRO:
cout<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->ERRO"<<endl;
break;
case FATAL:
cout<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->FATAL"<<endl;
break;
default:
break;
}
}
void LogOut::writeLog(string line, string msg, LogLevel level)
{
string fileName = m_logXml->getPath();
ofstream outfile(fileName.c_str(),ios::app);//将fileName转化为c型字符串作为文件名
if(!outfile)
{
cout<<"Log.txt can't open."<<endl;
return;
}
else
{
switch(level)
{
case DEBUG:
if(m_logXml->getLevel()>="5")
{
outfile<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->DEBUG"<<endl;
}
break;
case INFO:
if(m_logXml->getLevel()>="4")
{
outfile<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->INFO"<<endl;
}
break;
case WARN:
if(m_logXml->getLevel()>="3")
{
outfile<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->WARN"<<endl;
}
break;
case ERRO:
if(m_logXml->getLevel()>="2")
{
outfile<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->ERRO"<<endl;
}
break;
case FATAL:
if(m_logXml->getLevel()>="1")
{
outfile<<getCurrentTime()<<line<<m_path.c_str()<<": "<<msg.c_str()<<" -->FATAL"<<endl;
}
break;
default:
break;
}
outfile.close();
cout<<"save to text......"<<endl;
}
}
void LogOut::saveLog(string line, string msg, LogLevel level)
{
cout<<"save to db......"<<endl;
}
解析XML头文件:
#ifndef LOGXML_H
#define LOGXML_H
#include <iostream>
#include "tinyxml2.h"
using namespace std;
using namespace tinyxml2;
class LogXml
{
public:
LogXml();
string getTextSwitch(); //获取文本文件开关
string getConSwitch(); //获取控制台开关
string getDBSwitch(); //获取数据库开关
string getLevel(); //获取日志等级
string getPath(); //获取日志输出路径
private:
string m_textSwitch; //文本输出开关
string m_consoleSwitch; //控制台输出开关
string m_dbSwitch; //数据库输出开关
string m_level; //日志消息等级
string m_path; //日志输出路径
};
#endif // LOGXML_H
解析XML cpp
#include "logxml.h"
LogXml::LogXml()
{
tinyxml2::XMLDocument docXml;
docXml.LoadFile("../LogLib/config.xml");
XMLElement *elmtRoot = docXml.RootElement();
XMLElement *elmtConfig=elmtRoot->FirstChildElement("config");
XMLElement *configChild = elmtConfig->FirstChildElement("text");
m_textSwitch = configChild->GetText();
configChild = elmtConfig->FirstChildElement("console");
m_consoleSwitch = configChild->GetText();
configChild = elmtConfig->FirstChildElement("database");
m_dbSwitch = configChild->GetText();
configChild = elmtConfig->FirstChildElement("level");
m_level = configChild->GetText();
configChild = elmtConfig->FirstChildElement("path");
m_path = configChild->GetText();
}
string LogXml::getTextSwitch()
{
return m_textSwitch;
}
string LogXml::getConSwitch()
{
return m_consoleSwitch;
}
string LogXml::getDBSwitch()
{
return m_dbSwitch;
}
string LogXml::getLevel()
{
return m_level;
}
string LogXml::getPath()
{
return m_path;
}
最后附上源码传送门:https://download.csdn.net/download/fan_xingwang/10407947