青岛网站推广途径,电脑做网站用word,wordpress占用id,160加工网文章目录 专栏导读抽象基类StdoutSink类设计FileSink类设计RollBySizeSink类设计日志落地工厂类设计日志落地类整理日志落地拓展测试RollByTimeSink类设计测试代码测试完整代码 专栏导读 #x1f338;作者简介#xff1a;花想云 #xff0c;在读本科生一枚#xff0c;C/C领… 文章目录 专栏导读抽象基类StdoutSink类设计FileSink类设计RollBySizeSink类设计日志落地工厂类设计日志落地类整理日志落地拓展测试RollByTimeSink类设计测试代码测试完整代码 专栏导读 作者简介花想云 在读本科生一枚C/C领域新星创作者新星计划导师阿里云专家博主CSDN内容合伙人…致力于 C/C、Linux 学习。 专栏简介本文收录于 C项目——基于多设计模式下的同步与异步日志系统 相关专栏推荐C语言初阶系列、C语言进阶系列 、C系列、数据结构与算法、Linux 日志落地类主要负责将日志消息输出到指定的位置。目前实现了三个日志落地方向
标准输出StdoutSink固定文件FileSink滚动文件文件按照时间/大小进行滚动切换RollSink
同时日志落地类还应提供可扩展落地方向的功能。用户可以自己编写一个新的落地模块将日志进行其他方向的落地。
实现思想 抽象出落地模块类不同落地方向从基类进行派生使用工厂模式进行创建与表示分离。
抽象基类
提供一个智能指针对象方便管理将日志输函数log作与析构函数设置为虚函数
class LogSink
{
public:using ptr std::shared_ptrLogSink;LogSink() {}virtual ~LogSink() {}virtual void log(const char *data, size_t len) 0;
};StdoutSink类设计
// 落地方向标准输出
class StdOutSink : public LogSink
{
public:void log(const char *data, size_t len){std::cout.write(data, len);}
};FileSink类设计
类中包含两个成员
pathname文件名用来指定日志消息输出到哪个文件ofs文件输出类对象进行输出操作
在C中ofstream 是用于文件输出的类它是 C 标准库中的一部分通常与 ifstream用于文件输入一起使用。ofstream 类允许你创建、打开、写入和关闭文本文件。你可以使用它来将数据写入文件例如文本、数字或二进制数据。
// 落地方向指定文件
class FileSink : public LogSink
{
public:FileSink(const std::string pathname):_pathname(pathname){// 1.创建日志文件所在目录util::File::createDirectory(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;
};RollBySizeSink类设计
日志文件滚动的条件有两个文件大小和时间。我们可以选择
日志文件在大于1GB的时候会更换新的文件每天定点滚动一个日志文件。
本项目基于文件大小的判断滚动生成新的文件。
滚动文件输出的必要性
由于磁盘空间有限我们不可能一直无限的向一个文件中增加数据如果一个日志文件的体积太大一方面是不好打开另一方面是即使打开了由于包含数据巨大也不利于查找我们需要的信息所以实际开发中会对单个日志文件的大小也会做一些限制即当大小超过了某个大小时如1GB我们就重新创建一个新的日志文件来滚动写日志。对于那些过期的文件大部分企业内都有专门的运维人员去定时清理过期的日志文件或者设置定时任务定时清理过期日志。
// 落地方向滚动文件
class RollBySizeSink : public LogSink
{
public:RollBySizeSink(const std::string basename, size_t max_size): _basename(basename),_max_fsize(max_size),_cur_fsize(0),_name_count(0){std::string pathname createNewFile();// 1.创建日志文件所在的目录util::File::createDirectory(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_fsize 0;}_ofs.write(data, len);assert(_ofs.good());_cur_fsize len;}private:// 切换文件后以时间格式创建新的文件名std::string createNewFile(){time_t t util::Date::getTime();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 -;filename _name_count;filename .log;return filename.str();}private:size_t _name_count;std::string _basename;std::ofstream _ofs;size_t _max_fsize; // 日志文件最大大小size_t _cur_fsize; // 已经写入的文件大小
};日志落地工厂类设计
为了避免用户将来实现自己的落地方向时需要修改源代码这违背了开闭原则所以我们采用工厂类的设计由于不同的落地方向如StdoutSink、FileSink、RollBySizeSink它们各自的构造函数所需参数并不相同无法统一的管理所以我们采用参数包的方式来解决。
class SinkFactory
{
public:template typename SinkType, typename... Argsstatic LogSink::ptr create(Args ...args){return std::make_sharedSinkType(std::forwardArgs(args)...);}
};日志落地类整理
#ifndef __M_SINK_H__
#define __M_SINK_H__#include util.hpp
#include memory
#include sstream
#include fstream
#include cassertnamespace LOG
{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);}};// 落地方向指定文件class FileSink : public LogSink{public:FileSink(const std::string pathname):_pathname(pathname){// 1.创建日志文件所在目录util::File::createDirectory(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 RollBySizeSink : public LogSink{public:RollBySizeSink(const std::string basename, size_t max_size): _basename(basename),_max_fsize(max_size),_cur_fsize(0),_name_count(0){std::string pathname createNewFile();// 1.创建日志文件所在的目录util::File::createDirectory(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_fsize 0;}_ofs.write(data, len);assert(_ofs.good());_cur_fsize len;}private:std::string createNewFile(){time_t t util::Date::getTime();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 -;filename _name_count;filename .log;return filename.str();}private:size_t _name_count;std::string _basename;std::ofstream _ofs;size_t _max_fsize; // 日志文件最大大小size_t _cur_fsize; // 已经写入的文件大小};class SinkFactory{public:template typename SinkType, typename... Argsstatic LogSink::ptr create(Args ...args){return std::make_sharedSinkType(std::forwardArgs(args)...);}};
}
#endif日志落地拓展测试
本小节主要内容为测试日志落地类是否支持扩展功能。我们新增一个基于时间的滚动文件类RollByTimeSink。
RollByTimeSink类设计
#include unistd.henum class TimeGap
{GAP_SECOND,GAP_MINUTE,GAP_HOUR,GAP_DAY
};class RollByTimeSink : public LOG::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_size 1? LOG::util::Date::getTime() : LOG::util::Date::getTime() % _gap_size; // 获取当前是第几个时间段std::string filename createNewFile();LOG::util::File::createDirectory(LOG::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){time_t cur LOG::util::Date::getTime();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 LOG::util::Date::getTime();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 -;filename .log;return filename.str();}
private:std::string _basename;size_t _gap_size; // 时间段的大小int _cur_gap; // 当前是第几个时间段std::ofstream _ofs;
};测试代码
int main()
{ LOG::LogMsg msg(LOG::LogLevel::value::INFO, 53, main.cc, root, 格式化功能测试...);LOG::Formatter fmt;std::string str fmt.format(msg);LOG::LogSink::ptr time_lsp LOG::SinkFactory::createRollByTimeSink(./logfile/roll-, TimeGap::GAP_SECOND);time_t old LOG::util::Date::getTime();while(LOG::util::Date::getTime() old 5){time_lsp-log(str.c_str(), str.size());sleep(1);}
}测试结果
测试完整代码
test.cc
#include LogSink.hpp
#include util.hpp
#include unistd.henum class TimeGap
{GAP_SECOND,GAP_MINUTE,GAP_HOUR,GAP_DAY
};class RollByTimeSink : public LOG::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_size 1? LOG::util::Date::getTime() : LOG::util::Date::getTime() % _gap_size; // 获取当前是第几个时间段std::string filename createNewFile();LOG::util::File::createDirectory(LOG::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){time_t cur LOG::util::Date::getTime();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 LOG::util::Date::getTime();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 -;filename .log;return filename.str();}
private:std::string _basename;size_t _gap_size; // 时间段的大小int _cur_gap; // 当前是第几个时间段std::ofstream _ofs;
};int main()
{ LOG::LogMsg msg(LOG::LogLevel::value::INFO, 53, main.cc, root, 格式化功能测试...);LOG::Formatter fmt;std::string str fmt.format(msg);LOG::LogSink::ptr time_lsp LOG::SinkFactory::createRollByTimeSink(./logfile/roll-, TimeGap::GAP_SECOND);time_t old LOG::util::Date::getTime();while(LOG::util::Date::getTime() old 5){time_lsp-log(str.c_str(), str.size());sleep(1);}
}