背景:
c++的大型开源日志系统有glog,log4cpp等,但是这些日志系统太庞大,在工程中应用还需专门学习。一些小型项目用不着那样的日志系统。本篇博客将从0开始,一步一步教你打造一款自己的c++日志系统。我们设计的初衷是超轻量化,所以严格控制代码量。
第一个日志系统
我们先写一个简单的日志系统,以同步方式记录日志,并且采用c++的流方式进行日志记录。在开始之前你需要了解c++的单例模式。
我们先写一个应用的demo,以此来设计。
/*
*基本使用
*/
#include"sync_log.h"
int main()
{
LOGINIT("app_log/log","demo1.log");
for(int i=0;i<1e5;++i)
{
ERROR<<"error"<<endl;
INFO<<"info"<<endl;
DEBUG<<"debug"<<endl;
}
}
头文件如下,这里我们写出了日志系统的框架和主要实现。
/*
* 同步日志系统
* */
#include<iostream>
#include<fstream>
#include<sstream>
#include<unistd.h>
#include<sys/time.h>
#include<sys/stat.h>
#define LOGINIT(dir,file_name) \
do\
{\
Log::inst()->init_path(dir,file_name);\
}while(0)
#define DEBUG Log::inst()->_file<<"[DEBUG]"<<__DATE__<<"|"<<__TIME__<<"|"<<__FILE__<<"|"<<__func__<<"|"<<__LINE__<<": "
#define ERROR Log::inst()->_file<<"[ERROR]"<<__DATE__<<"|"<<__TIME__<<"|"<<__FILE__<<"|"<<__func__<<"|"<<__LINE__<<": "
#define INFO Log::inst()->_file<<"[INFO]"<<__DATE__<<"|"<<__TIME__<<"|"<<__FILE__<<"|"<<__func__<<"|"<<__LINE__<<": "
using namespace std;
class Log
{
private:
string _dir;
string _file_name;
static Log* _inst;//单例模式
Log() {}
~Log() {}
public:
ofstream _file;
static Log* inst()
{
if(_inst == NULL)
{
//cout<<"inst is NULL , create "<<endl;
_inst = new Log();
}
return _inst;
}
void init_path(const string& dir, const string& file_name);
bool makedir(const std::string& dir);
bool openfile(const string& file_name);
string get_curr_time();//当前系统时间,形式:20190804|09:28:56
};
//数字转字符串
//demo: int a = str2num<int>(string("-01.23"));
template <class T>
T str2num(const string &str)
{
stringstream ss;
T num;
ss << str;
ss >> num;
return num;
}
//demo: double num=-23.1; string str = num2str(num);
//也可以指定T的类型,str = num2str<int>(num);
template <class T>
string num2str(const T &num)
{
stringstream ss;
ss << num;
string str;
ss >> str;
return str;
}
实现:
/*
*
* */
#include "sync_log.h"
#include <sys/time.h> //struct timeval
#include <time.h> //struct tm
Log *Log::_inst = NULL;
void Log::init_path(const string &dir, const string &file_name)
{
_dir = dir;
if (file_name.size() < 4 || file_name.substr(file_name.size() - 4) != ".log")
{
_file_name = file_name + ".log";
}
else
{
_file_name = file_name;
}
_file_name = get_curr_time().substr(0, 14) + _file_name;//20180809logname.log,8
makedir(_dir);
openfile(_file_name);
}
bool Log::makedir(const std::string &dir)
{
stringstream ss;
ss << dir;
string dir_c;
string tmp;
while (getline(ss, tmp, '/')) //getline第一个参数是流。
{
dir_c = dir_c + tmp + "/";
if (access(dir_c.c_str(), F_OK | W_OK) == 0) //如果目录已经存在,可读可写,则跳过
{
//DEBUG<<"dir "<<dir_c<<" exists!"<<endl;
continue;
}
if (mkdir(dir_c.c_str(), 0777) != 0) //返回0表示成功,失败则退出程序
{
//DEBUG<<"mkdir fail "<<dir_c<<endl;
return false;
}
}
return true;
}
bool Log::openfile(const string &file_name)
{
string dir_file;
if ((!_dir.empty()) && _dir[_dir.size() - 1] != '/')
{
dir_file = _dir + "/" + file_name;
}
else
dir_file = _dir + file_name;
cout<<file_name<<endl;
_file.open(dir_file.c_str(), ios::app); //追加写入
return true;
}
string Log::get_curr_time()
{
struct timeval tv;
gettimeofday(&tv, NULL); //step1
uint64_t sys_sec = tv.tv_sec;
//DEBUG<<"sys_sec="<<sys_sec<<endl;
struct tm curr_time;
localtime_r((time_t *)&sys_sec, &curr_time); //setp2
string ret;
string year, mon, mday, hour, min, sec;
year = num2str<uint64_t>(curr_time.tm_year + 1900);
mon = num2str<uint64_t>(curr_time.tm_mon + 1);
mday = num2str<uint64_t>(curr_time.tm_mday);
hour = num2str<uint64_t>(curr_time.tm_hour);
min = num2str<uint64_t>(curr_time.tm_min);
sec = num2str<uint64_t>(curr_time.tm_sec);
if (mon.size() < 2)
mon = "0" + mon;
if (mday.size() < 2)
mday = "0" + mday;
if (hour.size() < 2)
hour = "0" + hour;
if (min.size() < 2)
min = "0" + min;
if (sec.size() < 2)
sec = "0" + sec;
ret = year + mon + mday + "|" + hour + "