企业做网站需要什么,网络信息工程师,网站建设项目需求说明书,网址格式同步异步日志系统 一、日志消息格式化设计1.1 格式化子项类的定义和实现1.2 格式化类的定义和实现 二、日志落地类设计2.1 日志落地模块功能实现与测试2.2 日志落地模块功能功能扩展 一、日志消息格式化设计
日志格式化模块的作用#xff1a;对日志消息进行格式化#xff0c… 同步异步日志系统 一、日志消息格式化设计1.1 格式化子项类的定义和实现1.2 格式化类的定义和实现 二、日志落地类设计2.1 日志落地模块功能实现与测试2.2 日志落地模块功能功能扩展 一、日志消息格式化设计
日志格式化模块的作用对日志消息进行格式化并且组织成指定格式的字符串。 %d ⽇期 %T 缩进 %t 线程id %p ⽇志级别 %c ⽇志器名称 %f ⽂件名 %l ⾏号 %m ⽇志消息 %n 换⾏ 如[2024-07-09 17:04][root][1234567][main.c:99][FATAL]:\t创建套接字失败…\n 格式化字符串控制了日志的输出格式 定义格式化字符是为了让日志系统进行日志格式化更加的灵活方便。
成员 1.格式化字符串(用户定义的输出格式格式) 2.格式化子项数组(对格式化字符串进行解析保存了日志消息要素的排序) 不同的格式化子项会从日志消息中取出指定的元素转化为字符串。 [%d{%H:%M:%S}][%f:%l]%m%n 格式化子项 其他信息(非格式化字符)子项[ 日期子项%H%M%S 其他信息子项] 其他信息子项[ 文件名子项main.c 其他信息子项: 行号信息子项99 其他信息子项] 消息主体子项吃饭睡觉打豆豆 换行子项\n [12:40;50][main.c:99]吃饭睡觉打豆豆\n 1.1 格式化子项类的定义和实现
格式化子项的实现思想从日志消息中取出指定的元素追加到一块内存空间中。 设计思想 1.抽象出一个格式化子项的基类 2.基于基类派生出不同的格式化子项子类 主体消息、日志等级、时间子项、文件名、行号、日志器名称、线程ID、制表符、换行、非格式化的原始字符串。 这样就可以在父类中定义父类指针的数组指向不同的格式化子项子类的对象。
FormatItem类主要负责日志消息子项的获取及格式化。其包含以下子类
MsgFormatItem 表示要从LogMsg中取出有效⽇志数据LevelFormatItem表示要从LogMsg中取出⽇志等级NameFormatItem 表示要从LogMsg中取出⽇志器名称ThreadFormatItem 表示要从LogMsg中取出线程IDTimeFormatItem表示要从LogMsg中取出时间戳并按照指定格式进行格式化CFileFormatItem 表示要从LogMsg中取出源码所在⽂件名CLineFormatItem 表示要从LogMsg中取出源码所在⾏号TabFormatItem 表示⼀个制表符缩进NLineFormatItem 表示⼀个换行OtherFormatItem 表示⾮格式化的原始字符串
首先搭架子定义抽象的格式化子项的基类 //抽象格式化子类基类class FormatItem{//c17语法与typedef作用一样using ptrstd::shared_ptrFormatItem;//纯虚函数virtual void format(std::ostream out,const LogMsg msg)0;}在基类的基础上派生出格式化子项的子类
#ifndef __M_FORMAT_H__
#define __M_FORMAT_H__
//日志消息格式化模块
#include ctime
#include level.hpp
#include message.hppnamespace logslearn{//抽象格式化子类基类class FormatItem{//c17语法与typedef作用一样using ptrstd::shared_ptrFormatItem;//纯虚函数virtual void format(std::ostream out,const LogMsg msg)0;};//派生出格式化子项的子类主体消息、日志等级、时间子项、文件名、行号、日志器名称、线程ID、制表符、换行、非格式化的原始字符串//主体消息class MsgFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outmsg._payload;}};//日志等级class LevelFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outloglevel::tostring(msg._level);}};//时间子项class TimeFormatItem:public FormatItem{public://默认构造函数,设置时间的默认格式TimeFormatItem(const std::string fmt%H:%M:%S):_time_fmt(fmt){}//虚函数进行重写void format(std::ostream out,const LogMsg msg) override{struct tm t;localtime_r(msg._ctime,t);char tmp[32]{0};strftime(tmp,31,_time_fmt.c_str(),t);outtmp;}private:std::string _time_fmt;//默认的时间格式};//文件名class FileFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outmsg._file;}};//行号class LineFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outmsg._line;}};//日志器名称class LoggerFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outmsg._logger;}};//线程IDclass ThreadFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{outmsg._tid;}};//制表符class TabFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out\t;}};//换行class NLineFormatItem:public FormatItem{public://虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out\n;}};//非格式化的原始字符串class OtherFormatItem:public FormatItem{public://设置默认构造函数OtherFormatItem(std::string str):_str(str){}//虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out_str;}private:std::string _str;};
}
#endif1.2 格式化类的定义和实现
确定框架设计格式化类设计需要的成员需要完成的功能。
/*格式化类的定义和实现%d 表示日期 包含子格式{%H%M%S} %t 表示线程id%c 表示⽇志器名称%f 表示源码⽂件名%l 表示源码⾏号%p 表示⽇志级别%T 表示制表符缩进%m 表示主体消息%n 表示换⾏
*/
class Formatter{public://构造默认函数Formatter(const std::string pattern[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n):_pattern(pattern){}//对msg进行格式化void format(std::ostream out,const LogMsg msg);std::string format(); //对格式化规则字符进行解析bool parsePatern();private://根据不同的格式化字符创建不同的格式化子项对象FormatItem::ptr createItem(const std::string key,const std::string val);private:std::string _pattern;//格式化规则字符串std::vectorlogslearn::FormatItem::ptr _items;//格式化字符串解析出的格式化子项
};对格式化的功能接口进行设计
#ifndef __M_FORMAT_H__
#define __M_FORMAT_H__
// 日志消息格式化模块#include message.hpp
#include level.hpp
#include memory
#include ctime
#include vector
#include assert.h
#include sstream
namespace logslearn
{// 抽象格式化子类基类class FormatItem{public:// c17语法与typedef作用一样using ptr std::shared_ptrFormatItem;// 纯虚函数virtual void format(std::ostream out,const LogMsg msg) 0;};// 派生出格式化子项的子类主体消息、日志等级、时间子项、文件名、行号、日志器名称、线程ID、制表符、换行、非格式化的原始字符串// 主体消息class MsgFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out msg._payload;}};// 日志等级class LevelFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out loglevel::tostring(msg._level);}};// 时间子项class TimeFormatItem : public FormatItem{public:// 默认构造函数,设置时间的默认格式TimeFormatItem(const std::string fmt %H:%M:%S) : _time_fmt(fmt) {if (_time_fmt.empty()) _time_fmt %H:%M:%S;}// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{struct tm t;localtime_r(msg._ctime, t);char tmp[32] {0};strftime(tmp, 31, _time_fmt.c_str(), t);outtmp;}private:std::string _time_fmt; // 默认的时间格式};// 文件名class FileFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out msg._file;}};// 行号class LineFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out msg._line;}};// 日志器名称class LoggerFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out msg._logger;}};// 线程IDclass ThreadFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out msg._tid;}};// 制表符class TabFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out \t;}};// 换行class NLineFormatItem : public FormatItem{public:// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out \n;}};// 非格式化的原始字符串class OtherFormatItem : public FormatItem{public:// 设置默认构造函数OtherFormatItem(std::string str) : _str(str) {}// 虚函数进行重写void format(std::ostream out,const LogMsg msg) override{out _str;}private:std::string _str;};/*格式化类的定义和实现%d 表示日期 包含子格式{%H%M%S}%t 表示线程id%c 表示⽇志器名称%f 表示源码⽂件名%l 表示源码⾏号%p 表示⽇志级别%T 表示制表符缩进%m 表示主体消息%n 表示换⾏*/class Formatter{public://基类指针用来控制继承子类的对象using ptrstd::shared_ptrFormatter;// 构造默认函数Formatter(const std::string pattern [%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n) : _pattern(pattern){// 断言是否解析格式化规则字符assert(parsePatern());}// 对msg进行格式化void format(std::ostream out, const LogMsg msg){for (auto item : _items){item-format(out, msg);}}std::string format(LogMsg msg){std::stringstream ss;format(ss, msg);return ss.str();}private:// 对格式化字符进行解析bool parsePatern(){// 1.对格式化规则字符串进行解析// 2.根据解析得到的数据初始化格式化子项数组成员// 规则字符串的处理过程是一个循环的过程原始字符串结束后遇到%则处理一个格式化字符std::vectorstd::pairstd::string, std::string fmt_order;size_t pos 0;std::string key, val;while (pos _pattern.size()){// 1.处理原始字符串--判断是否是%不是就是原始字符串if (_pattern[pos] ! %){val.push_back(_pattern[pos]);continue;}// 能走下来就代表pos位置就是%字符%%处理称为一个原始%字符if (pos 1 _pattern.size() _pattern[pos 1] %){val.push_back(%);pos 2;continue;}// 能走下去,代表%后面是格式化字符代表原始字符串处理完毕if (val.empty() false){fmt_order.push_back(std::make_pair(, val));val.clear(); // 清空}// 这时候pos指向的是%的位置是格式化字符的处理pos 1; // 这一步之后pos位置指向格式化字符的位置if (pos _pattern.size()){std::cout %之后没有对应的格式化字符!\n;return false;}key _pattern[pos];// 这时候pos指向格式化字符后的位置pos 1;if (pos _pattern.size() _pattern[pos] {){// 这时候pos指向{之后子规则的起始位置pos 1;while (pos _pattern.size() _pattern[pos] ! }){val.push_back(_pattern[pos]);}// 走到末尾跳出循环则代表没有遇到}代表格式是错误的if (pos _pattern.size()){std::cout 子规则{}匹配出错!\n;return false; // 没有找到}}pos 1; // 因为这时候pos指向的是}位置向后走一步走到了下次处理的新位置}fmt_order.push_back(std::make_pair(key, val)); // 添加处理的结果// 两次都清空开始下一次处理key.clear();val.clear();}// 2.根据解析得到的数据初始化格式化子项数组成员for (auto it : fmt_order){_items.push_back(createItem(it.first, it.second));}return true;}// 根据不同的格式化字符创建不同的格式化子项对象FormatItem::ptr createItem(const std::string key, const std::string val){if (key d)return std::make_sharedTimeFormatItem(val);if (key t)return std::make_sharedThreadFormatItem();if (key c)return std::make_sharedLoggerFormatItem();if (key f)return std::make_sharedFileFormatItem();if (key l)return std::make_sharedLineFormatItem();if (key p)return std::make_sharedLevelFormatItem();if (key T)return std::make_sharedTabFormatItem();if (key m)return std::make_sharedMsgFormatItem();if (key n)return std::make_sharedNLineFormatItem();if (key )return std::make_sharedOtherFormatItem(val);std::cout 没有对应的格式化字符串% key std::endl;abort();return FormatItem::ptr();}private:std::string _pattern; // 格式化规则字符串std::vectorlogslearn::FormatItem::ptr _items; // 格式化字符串解析出的格式化子项};
}
#endif对日志格式化模块进行测试和完善 1日志格式化的默认格式
//测试代码
#include util.hpp
#include level.hpp
#include message.hpp
#include format.hpp
int main()
{//日志格式化模块测试logslearn::LogMsg msg(logslearn::loglevel::value::INFO,53,main.c,root,格式化功能测试...);logslearn::Formatter fmt;//不给格式会生成默认格式std::string strfmt.format(msg);std::coutstr;//std::cout Main thread ID: std::this_thread::get_id() std::endl;return 0;
}2对日志进行边缘测试 测试了三种情况
//测试代码
#include util.hpp
#include level.hpp
#include message.hpp
#include format.hpp
int main()
{//日志格式化模块测试//边缘测试logslearn::LogMsg msg(logslearn::loglevel::value::INFO,53,main.c,root,格式化功能测试...);//logslearn::Formatter fmt(abc%%abc[%d{%H:%M:%S}]%m%n);//1.测试%//logslearn::Formatter fmt(abc%%abc[%d{%H:%M:%S}]%m%n{);//2.测试子项的{}logslearn::Formatter fmt(abc%%abc[%d{%H:%M:%S}]%m%);//3.%后的字符std::string strfmt.format(msg);std::coutstr;return 0;
}二、日志落地类设计
功能将格式化完成后的日志消息字符串输出到指定位置。(支持同时将日志落地到不同位置) 位置分类 1.标准输出 2.指定文件(事后进行日志分析) 3.滚动文件(文件按时间/大小进行滚动切换) 滚动⽇志⽂件输出的必要性 由于机器磁盘空间有限 我们不可能⼀直⽆限地向⼀个⽂件中增加数据 如果⼀个⽇志⽂件体积太⼤⼀⽅⾯是不好打开另⼀⽅⾯是即时打开了由于包含数据巨 ⼤也不利于查找我们需要的信息 所以实际开发中会对单个⽇志⽂件的⼤⼩也会做⼀些控制即当⼤⼩超过某个⼤⼩时如 1MB我们就重新创建⼀个新的⽇志⽂件来滚动写⽇志。 对于那些过期的⽇志 ⼤部分企业内部都有专⻔的运维⼈员去定时清理过期的⽇志或者设置系统定时任务定时清理过期⽇志。 ⽇志⽂件的滚动思想 ⽇志⽂件滚动的条件有两个:⽂件⼤⼩和时间. 我们可以选择 ▪ ⽇志⽂件在⼤于 1MB 的时候会更换新的⽂件 ▪ 每天定点滚动⼀个⽇志⽂件 本项⽬基于⽂件⼤⼩的判断滚动⽣成新的⽂件 扩展支持落地方向的扩展 用户可以自己编写一个新的落地模块将日志进行其他方向的落地 实现思想 1.抽象出落地模块类 2.不同落地方向从基类进行派生(使用基类指针指向子类对象就可以调用子类对象的接口进行扩展) 3.使用工厂模式进行创建与表示的分离
2.1 日志落地模块功能实现与测试
第一步先要设计日志落地的模块把大致的框架建好。
/*日志落地模块的实现
1.抽象落地基类
2.派生子类根据不同的落地方向进行派生
3.使用工厂模式进行创建与表示分离
*/
#ifndef __M_SINK_H__
#define __M_SINK_H__
#include util.hpp
#include fstream
#include memory
namespace logslearn
{// 抽象落地基类class LogSink{public:using ptr std::shared_ptrLogSink;LogSink() {}virtual ~LogSink() {}// 纯虚函数,日志落地功能virtual void log(const char *data, size_t len) 0;};// 落地方向标准输出class StdoutSink : public LogSink{public:// 将日志消息写入到标准输出void log(const char *data, size_t len);};// 落地方向指定文件class FileSink : public LogSink{public:// 构造时存入文件名并打开文件将操作句柄管理起来FileSink(const std::string pathname);// 将日志消息写入到指定文件void log(const char *data, size_t len);private:std::string _pathname;std::ofstream _ofs;};// 落地方向滚动文件以大小进行滚动class RoolBySizeSink : public LogSink{public:// 构造时存入文件名并打开文件将操作句柄管理起来RoolBySizeSink(const std::string basename,size_t max_fsize);//需要用户告知基础的文件名和文件大小// 将日志消息写入到标准输出--写入前判断文件大小超过了最大大小就要切换文件void log(const char *data, size_t len);private://创建一个新文件不需要用户去创建所有我们把权限设置为私有void createNewFile();//进行大小判断超过指定大小则需要创建新文件private://通过基础文件名扩展文件名以时间生成组成一个实际的当前输出文件名size_t _name_count;//名称计数器std::string _basename;//文件的基础名字如./logs/base- ./logs/base-20240710.logstd::ostream _ofs;size_t _max_fsize;//最大文件大小当前文件超过了这个大小就要切换文件size_t _cur_fsize;//记录当前文件已经写入的数据大小};//简单工厂模式进行生成管理class SinkFactory{};
}
#endif把框架的功能以及具体实现编写完成。
/*日志落地模块的实现
1.抽象落地基类
2.派生子类根据不同的落地方向进行派生
3.使用工厂模式进行创建与表示分离
*/
#ifndef __M_SINK_H__
#define __M_SINK_H__
#include util.hpp
#include fstream
#include sstream
#include memory
#include cassert
#include unistd.h
namespace logslearn
{// 抽象落地基类class LogSink{public:using ptr std::shared_ptrLogSink;LogSink() {}virtual ~LogSink() {}// 纯虚函数,日志落地功能virtual void log(const char *data, size_t len) 0;};// 落地方向标准输出class StdoutSink : public LogSink{public:// 将日志消息写入到标准输出void log(const char *data, size_t len){std::cout.write(data, len); // 因为日志输出不一定是字符串所以不能直接打印因此需要调用write接口,从data位置开始写写入len长度的数据}};// 落地方向指定文件class FileSink : public LogSink{public:// 构造时存入文件名并打开文件将操作句柄管理起来FileSink(const std::string pathname) : _pathname(pathname){// 1.创建日志文件所在的目录,没有文件就创建文件logsLearn::util::File::createDirectory(logsLearn::util::File::path(pathname));// 2.按特殊方式打开文件_ofs.open(_pathname, std::ios::binary | std::ios::app); // 二进制可写可追加权限assert(_ofs.is_open());}// 将日志消息写入到标准输出void log(const char *data, size_t len){_ofs.write(data, len);assert(_ofs.good()); // 打开失败就报错}private:std::string _pathname;std::ofstream _ofs; // 会默认以写的方式打开文件};// 落地方向滚动文件以大小进行滚动class RoolBySizeSink : public LogSink{public:// 构造时存入文件名并打开文件将操作句柄管理起来// 需要用户告知基础的文件名和文件大小RoolBySizeSink(const std::string basename, size_t max_fsize) :_basename(basename), _max_fsize(max_fsize), _cur_fsize(0),_name_count(0){std::string pathnamecreateNewFile();// 1.创建日志文件所在的目录,没有文件就创建文件logsLearn::util::File::createDirectory(logsLearn::util::File::path(pathname));// 2.按特殊方式打开文件_ofs.open(pathname, std::ios::binary | std::ios::app); //打开文件 二进制可写可追加权限assert(_ofs.is_open());} // 将日志消息写入到标准输出--写入前判断文件大小超过了最大大小就要切换文件void log(const char *data, size_t len){if(_cur_fsize_max_fsize){_ofs.close();//打开文件就必须关闭文件这里关闭以前的文件std::string pathname createNewFile();//创建新文件_ofs.open(pathname, std::ios::binary | std::ios::app); //打开文件 二进制可写可追加权限assert(_ofs.is_open());//打开失败就报错_cur_fsize0;}_ofs.write(data,len);assert(_ofs.good());//检测文件流状态和文件读写过程是否正常 _cur_fsizelen; }private:// 创建一个新文件不需要用户去创建所有我们把权限设置为私有std::string createNewFile(){//获取系统时间以时间来构造文件名的扩展名time_t tlogsLearn::util::Data::now();struct tm lt;localtime_r(t,lt);std::stringstream filename;filename_basename;filenamelt.tm_year1900;filenamelt.tm_mon1;filenamelt.tm_mday;filenamelt.tm_hour;filenamelt.tm_min;filenamelt.tm_sec;filename-;filename_name_count;filename.log;return filename.str();}; // 进行大小判断超过指定大小则需要创建新文件,将一个时间戳转化为时间结构private:// 通过基础文件名扩展文件名以时间生成组成一个实际的当前输出文件名size_t _name_count;//名称计数器std::string _basename; // 文件的基础名字如./logs/base- ./logs/base-20240710.logstd::ofstream _ofs;size_t _max_fsize; // 最大文件大小当前文件超过了这个大小就要切换文件size_t _cur_fsize; // 记录当前文件已经写入的数据大小};// 简单工厂模式进行生成管理class SinkFactory{};
}
#endif使用简单工厂模式 // 简单工厂模式进行生成管理//SinkType通过模板参数可以生产我们需要的落地方式因为落地方式需要传参的参数不一样这里我们需要用到不定参的知识class SinkFactory{public:templatetypename SinkType,typename ...Argsstatic LogSink::ptr create(Args ...args){return std::make_sharedSinkType(std::forwardArgs(args)...);}};功能测试以及完善
//测试代码
#include util.hpp
#include level.hpp
#include message.hpp
#include format.hpp
#include sink.hpp
int main()
{//日志落地模块的测试logslearn::LogMsg msg(logslearn::loglevel::value::INFO,53,main.c,root,格式化功能测试...);logslearn::Formatter fmt(abc%%abc[%d{%H:%M:%S}]%m%n);std::string strfmt.format(msg);//设置落地方向logslearn::LogSink::ptr stdout_lsplogslearn::SinkFactory::createlogslearn::StdoutSink();//标准输出落地logslearn::LogSink::ptr file_lsplogslearn::SinkFactory::createlogslearn::FileSink(./logfile/test.log);//文件落地方式logslearn::LogSink::ptr roll_lsplogslearn::SinkFactory::createlogslearn::RoolBySizeSink(./logfile/test.log,1024*1024);//滚动文件落地方式//通过指针去控制打印的日志stdout_lsp-log(str.c_str(),str.size());//把str转化成常量字符file_lsp-log(str.c_str(),str.size());size_t cursize0;size_t count0;//用滚动文件的方法希望生产10个文件while(cursize1024*1024*10){std::string tmpstd::to_string(count)str;//每个生产的日志都有信号roll_lsp-log(tmp.c_str(),tmp.size());cursizetmp.size();}return 0;
}测试结果 文件落地的日志消息 滚动文件的日志消息
2.2 日志落地模块功能功能扩展
扩展一个以时间作为日志文件滚动切换类型的日志落地模块
/*扩展一个以时间作为日志文件滚动切换类型的日志落地模块1.以时间进行文件滚动实际上是以时间段进行滚动实现思想以当前系统时间取模获得时间段大小可以得到当前时间段是第几个时间段time(nullptr)%gap;每次以当前系统时间取模判断与当前文件的时间段是否一致不一致代表不是同一个时间段*/
// 使用枚举来确定时间段的大小
enum class TimeGap
{GAP_SECOND,GAP_MINUTE,GAP_HOUR,GAP_DAY,
};class RollByTimeSink : public logslearn::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_MINUTE:_gap_size 60;break; // 以分钟为时间段case TimeGap::GAP_HOUR:_gap_size 3600;break; // 以小时为时间段case TimeGap::GAP_DAY:_gap_size 3600 * 24;break; // 以天为时间段}_cur_gap _gap_size1?logsLearn::util::Data::now():logsLearn::util::Data::now() / _gap_size; // 获取当前是第几个时间段// 创建文件std::string filename createNewFile();// 1.创建日志文件所在的目录,没有文件就创建文件logsLearn::util::File::createDirectory(logsLearn::util::File::path(filename));// 2.按特殊方式打开文件_ofs.open(filename, std::ios::binary | std::ios::app); // 打开文件 二进制可写可追加权限assert(_ofs.is_open());}// 将日志消息写入到标准输出判断当前时间是否是当前文件的时间段不是就要切换文件。void log(const char *data, size_t len){time_t cur logsLearn::util::Data::now(); // 获取当前系统时间,时间戳if ((cur / _gap_size) ! _cur_gap)//每次写日志时判断当前的时间段与上次的时间段是否是一致得一致的话就写入不一致就创建新文件{_ofs.close(); // 打开文件就必须关闭文件这里关闭以前的文件std::string pathname createNewFile(); // 创建新文件_cur_gap _gap_size1?logsLearn::util::Data::now():logsLearn::util::Data::now() / _gap_size; // 获取当前是第几个时间段_ofs.open(pathname, std::ios::binary | std::ios::app); // 打开文件 二进制可写可追加权限assert(_ofs.is_open()); // 打开失败就报错}_ofs.write(data, len);assert(_ofs.good()); // 检测文件流状态和文件读写过程是否正常}protected:// 创建一个新文件不需要用户去创建所有我们把权限设置为私有std::string createNewFile(){// 获取系统时间以时间来构造文件名的扩展名time_t t logsLearn::util::Data::now();struct tm lt;localtime_r(t, lt);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; // 时间段的大小
};对扩展的功能进行测试
int main()
{// 日志落地扩展模块的测试logslearn::LogMsg msg(logslearn::loglevel::value::INFO, 53, main.c, root, 格式化功能测试...);logslearn::Formatter fmt(abc%%abc[%d{%H:%M:%S}]%m%n);std::string str fmt.format(msg);// 设置落地方向logslearn::LogSink::ptr time_lsp logslearn::SinkFactory::createRollByTimeSink(./logfile/rool-, TimeGap::GAP_MINUTE); // 滚动文件落地方式time_t oldlogsLearn::util::Data::now();//获取当前系统时间while (logsLearn::util::Data::now() old63)//写3秒的数据{time_lsp-log(str.c_str(), str.size());usleep(1000);//等待1毫秒}return 0;
}显示测试的结果 创建了5个文件每个文件有900条左右的日志。 文章转载自: http://www.morning.hbhnh.cn.gov.cn.hbhnh.cn http://www.morning.skksz.cn.gov.cn.skksz.cn http://www.morning.qmkyp.cn.gov.cn.qmkyp.cn http://www.morning.kmqjx.cn.gov.cn.kmqjx.cn http://www.morning.jhtrb.cn.gov.cn.jhtrb.cn http://www.morning.hyxwh.cn.gov.cn.hyxwh.cn http://www.morning.zfqdt.cn.gov.cn.zfqdt.cn http://www.morning.thwhn.cn.gov.cn.thwhn.cn http://www.morning.nrrzw.cn.gov.cn.nrrzw.cn http://www.morning.llfwg.cn.gov.cn.llfwg.cn http://www.morning.rjnky.cn.gov.cn.rjnky.cn http://www.morning.zyndj.cn.gov.cn.zyndj.cn http://www.morning.bqmsm.cn.gov.cn.bqmsm.cn http://www.morning.pxlsh.cn.gov.cn.pxlsh.cn http://www.morning.yzdth.cn.gov.cn.yzdth.cn http://www.morning.nnmnz.cn.gov.cn.nnmnz.cn http://www.morning.nwynx.cn.gov.cn.nwynx.cn http://www.morning.bqxxq.cn.gov.cn.bqxxq.cn http://www.morning.plzgt.cn.gov.cn.plzgt.cn http://www.morning.tpnxr.cn.gov.cn.tpnxr.cn http://www.morning.ayftwl.cn.gov.cn.ayftwl.cn http://www.morning.ymwny.cn.gov.cn.ymwny.cn http://www.morning.hmhdn.cn.gov.cn.hmhdn.cn http://www.morning.fgtls.cn.gov.cn.fgtls.cn http://www.morning.nfgbf.cn.gov.cn.nfgbf.cn http://www.morning.sfmqm.cn.gov.cn.sfmqm.cn http://www.morning.bswxt.cn.gov.cn.bswxt.cn http://www.morning.rbkl.cn.gov.cn.rbkl.cn http://www.morning.xsymm.cn.gov.cn.xsymm.cn http://www.morning.pwwdp.cn.gov.cn.pwwdp.cn http://www.morning.pdghl.cn.gov.cn.pdghl.cn http://www.morning.jwskq.cn.gov.cn.jwskq.cn http://www.morning.phzrq.cn.gov.cn.phzrq.cn http://www.morning.wqwbj.cn.gov.cn.wqwbj.cn http://www.morning.qfzjn.cn.gov.cn.qfzjn.cn http://www.morning.fqqlq.cn.gov.cn.fqqlq.cn http://www.morning.phjyb.cn.gov.cn.phjyb.cn http://www.morning.gqtxz.cn.gov.cn.gqtxz.cn http://www.morning.mmjyk.cn.gov.cn.mmjyk.cn http://www.morning.ldqzz.cn.gov.cn.ldqzz.cn http://www.morning.ckfyp.cn.gov.cn.ckfyp.cn http://www.morning.c7630.cn.gov.cn.c7630.cn http://www.morning.hkpn.cn.gov.cn.hkpn.cn http://www.morning.xnbd.cn.gov.cn.xnbd.cn http://www.morning.rpzth.cn.gov.cn.rpzth.cn http://www.morning.bpmmq.cn.gov.cn.bpmmq.cn http://www.morning.qmzhy.cn.gov.cn.qmzhy.cn http://www.morning.tqjwx.cn.gov.cn.tqjwx.cn http://www.morning.jzklb.cn.gov.cn.jzklb.cn http://www.morning.xq3nk42mvv.cn.gov.cn.xq3nk42mvv.cn http://www.morning.cttti.com.gov.cn.cttti.com http://www.morning.fwgnq.cn.gov.cn.fwgnq.cn http://www.morning.hytr.cn.gov.cn.hytr.cn http://www.morning.ydryk.cn.gov.cn.ydryk.cn http://www.morning.hhxwr.cn.gov.cn.hhxwr.cn http://www.morning.qiyelm.com.gov.cn.qiyelm.com http://www.morning.tmnyj.cn.gov.cn.tmnyj.cn http://www.morning.dqbpf.cn.gov.cn.dqbpf.cn http://www.morning.xknmn.cn.gov.cn.xknmn.cn http://www.morning.lzjxn.cn.gov.cn.lzjxn.cn http://www.morning.mszwg.cn.gov.cn.mszwg.cn http://www.morning.htbbp.cn.gov.cn.htbbp.cn http://www.morning.xrrjb.cn.gov.cn.xrrjb.cn http://www.morning.gwwtm.cn.gov.cn.gwwtm.cn http://www.morning.kndyz.cn.gov.cn.kndyz.cn http://www.morning.kqbwr.cn.gov.cn.kqbwr.cn http://www.morning.qckwj.cn.gov.cn.qckwj.cn http://www.morning.hffjj.cn.gov.cn.hffjj.cn http://www.morning.yszrk.cn.gov.cn.yszrk.cn http://www.morning.mznqz.cn.gov.cn.mznqz.cn http://www.morning.xbmwh.cn.gov.cn.xbmwh.cn http://www.morning.xpwdf.cn.gov.cn.xpwdf.cn http://www.morning.yymlk.cn.gov.cn.yymlk.cn http://www.morning.mkhwx.cn.gov.cn.mkhwx.cn http://www.morning.yrhd.cn.gov.cn.yrhd.cn http://www.morning.gmnmh.cn.gov.cn.gmnmh.cn http://www.morning.kszkm.cn.gov.cn.kszkm.cn http://www.morning.ndhxn.cn.gov.cn.ndhxn.cn http://www.morning.nslwj.cn.gov.cn.nslwj.cn http://www.morning.fxzgw.com.gov.cn.fxzgw.com