同步异步日志系统-日志落地模块的实现
功能:将格式化完成后的日志消息字符串,输出到指定的位置
扩展:支持同时将日志落地到不同的位置
位置分类:
1.标准输出
2.指定文件(时候进行日志分析)
3.滚动文件(文件按照时间/大小进行滚动切换)
扩展:支持落地方向的扩展
用户可以自己编写一个新的落地模块,将日志进行其他方向的落地。
实现思想
1.抽象出一个落地基类
2.之后根据落地方向从基类派生出不同落地方向的子类
3.使用工厂模式进行创建与表示分离
标准输出
class StdoutSink :public LogSink{
public:
void log(const char* data,size_t len)override{
std::cout.write(data,len);
}
};
输入到指定文件
class FileSink :public LogSink{
public:
//传入文件路径,并且打开文件
FileSink(const std::string& pathname)
:_pathname(pathname){
//创建日志文件所在的目录
util::File::createDirectory(util::File::path(pathname));
//创建并打开日志文件
_ofs.open(_pathname,std::ios::binary|std::ios::app);
assert(_ofs.is_open());//判断文件是否打开
}
//将日志消息输入到文件里面
void log(const char* data,size_t len)override{
_ofs.write(data,len);
assert(_ofs.good());
}
private:
std::string _pathname;
std::ofstream _ofs;
};
以大小进行滚动
class RollBySizeSink :public LogSink{
public:
//传入文件路径,并且打开文件
RollBySizeSink(const std::string& basename,size_t max_size)
:_basename(basename),_max_fsize(max_size),_cur_fsize(0){
std::string pathname = createNewFile();
//创建日志文件所在的目录
util::File::createDirectory(util::File::path(pathname));
//创建并打开日志文件
_ofs.open(pathname,std::ios::binary|std::ios::app);
assert(_ofs.is_open());//判断文件是否打开
}
//写入前判断文件大小,超过了最大大小就要切换文件
void log(const char* data,size_t len)override{
if(_cur_fsize>= _max_fsize){
std::string pathname = createNewFile();
_ofs.close();//关闭原来已经打开的文件。
_ofs.open(pathname,std::ios::binary|std::ios::app);
assert(_ofs.is_open());
_cur_fsize = 0;
}
_ofs.write(data,len);
assert(_ofs.good());
_cur_fsize += len;
}
private:
std::string createNewFile(){
//获取系统时间,以时间来构造文件扩展名
time_t t = util::Date::now();
struct tm lt;
localtime_r(&t,<);//将时间戳转换为有年月日的结构
std::stringstream filename;
filename << _basename;
filename << lt.tm_year+1900;
filename << lt.tm_mon+1;
filename << lt.tm_mday;
filename << lt.tm_hour;
filename << lt.tm_min;
filename << lt.tm_sec;
filename << "-";
filename <<_name_count++;
filename << ".log";
return filename.str();
}//进行大小判断,超过指定大小就要切换新文件
private:
//基础文件名+扩展文件名(时间生成)组成一个实际的当前输出文件名
size_t _name_count=0;
std::string _basename;
std::ofstream _ofs;
size_t _max_fsize;//记录最大大小,当前文件超过了这个大小就要切换文件
size_t _cur_fsize;//记录当前文件已经写入的大小
};
以时间进行滚动
enum class TimeGap{
GAP_SECOND,
GAP_MIUTE,
GAP_HOUR,
GAP_DAY,
};
class RollByTimeSink :public bitlog::LogSink{
public:
//传入文件路径,并且打开文件
RollByTimeSink(const std::string& basename,TimeGap gap_type)
:_basename(basename){
switch(gap_type){
case TimeGap::GAP_SECOND:_gap_size = 1;break;
case TimeGap::GAP_MIUTE:_gap_size = 60;break;
case TimeGap::GAP_HOUR:_gap_size = 3600;break;
case TimeGap::GAP_DAY:_gap_size = 3600*24;break;
}
_cur_gap = _gap_size == 1 ? bitlog::util::Date::now() : bitlog::util::Date::now() % _gap_size;
std::string filename = createNewFile();
//创建日志文件所在的目录
bitlog::util::File::createDirectory(bitlog::util::File::path(filename));
//创建并打开日志文件
_ofs.open(filename,std::ios::binary|std::ios::app);
assert(_ofs.is_open());//判断文件是否打开
}
//写入前判断文件大小,超过了最大大小就要切换文件
void log(const char* data,size_t len)override{
time_t cur =bitlog::util::Date::now();
if((cur%_gap_size)!=_cur_gap){
_ofs.close();
std::string filename = createNewFile();
_ofs.open(filename,std::ios::binary|std::ios::app);
assert(_ofs.is_open());
}
_ofs.write(data,len);
assert(_ofs.good());
}
private:
std::string createNewFile(){
//获取系统时间,以时间来构造文件扩展名
time_t t = bitlog::util::Date::now();
struct tm lt;
localtime_r(&t,<);//将时间戳转换为有年月日的结构
std::stringstream filename;
filename << _basename;
filename << lt.tm_year+1900;
filename << lt.tm_mon+1;
filename << lt.tm_mday;
filename << lt.tm_hour;
filename << lt.tm_min;
filename << lt.tm_sec;
filename << ".log";
return filename.str();
}//进行大小判断,超过指定大小就要切换新文件
private:
std::string _basename;
std::ofstream _ofs;
size_t _cur_gap;//当前是第几个时间段
size_t _gap_size;//时间段的大小
};
使用简易工厂模式来创建
class SinkFactory{
public:
template<typename SinkType,typename ...Args>
static LogSink::ptr create(Args &&...args){
return std::make_shared<SinkType>(std::forward<Args>(args)...);
}
};