原文链接:https://download.csdn.net/blog/column/12433305/133862792#_1613
1、工厂模式应用
C++17及之后可编译
/*日志落地模块的实现
1.抽象落地基类
2.派生子类(根据不同落地方向进行派生)
3.使用工厂模式进行创建与表示的分离
*/
#ifndef __M_SINK_H__
#define __M_SINK_H__
//#include "util.hpp"
#include <memory>
#include <fstream>
#include <sstream>
#include <cassert>
#include <filesystem>
#include <iostream>
namespace lgrlog
{
class LogSink
{
public:
//ptr有什么用?
//ptr是一个智能指针,可以自动管理对象生命周期,不需要手动释放
//ptr是一个父类指针,可以指向子类对象,但是不能调用子类独有接口,只能调用父类接口,所以这里创建的是LogSink的智能指针
using ptr = std::shared_ptr<LogSink>;// 定义智能指针类型(用强指针类型)
// 日志落地接口
LogSink() {}
virtual ~LogSink() {}
virtual void log(const char* data, size_t len) = 0;
};
// 落地方向类型1:标准输出(控制台)
class StdoutSink : public LogSink
{
public:
// 将日志写入到标准输出
void log(const char* data, size_t len) override
{
std::cout.write(data, len);
}
};
// 落地方向类型2:指定文件
class FileSink : public LogSink
{
public:
// 构造时传入文件名,并打开文件,将操作句柄管理起来
FileSink(const std::string& pathname) :_pathname(pathname)
{
// 1.创建日志文件所在的目录
//util::File::createDirectory(util::File::path(pathname));
//util::File::createDirectory(util::File::path(pathname));
std::filesystem::path path(pathname);
if (!std::filesystem::exists(path.parent_path()))
{
std::filesystem::create_directories(path.parent_path());
}
// 2.创建并打开日志文件
_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;
};
// 落地方向类型3:滚动文件(以大小进行滚动)
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));
std::filesystem::path path(pathname);
if (!std::filesystem::exists(path.parent_path()))
{
std::filesystem::create_directories(path.parent_path());
}
// 2.创建并打开日志文件
_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)
{
_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::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;
std::string _basename; // ./log/base- -> ./log/base-20250114110125.log
std::ofstream _ofs;
size_t _max_fsize; // 记录最大大小,当前文件超过这个大小就要切换文件
size_t _cur_fsize; // 记录当前文件已经写入的数据大小
};
class SinkFactory
{
public:
//SinkType: 落地方向 类型(继承自LogSink的3个派生类之一)
//Args...: 构造函数参数类型
template<typename SinkType, typename ...Args>
static LogSink::ptr create(Args&& ...args)// 这里的Args...表示参数包,可以传入多个参数
{
//模式:工厂模式
// 1.创建对象
// 2.返回对象
// 3.对象管理由工厂负责,不由调用者管理
// 4.对象创建与表示分离
return std::make_shared<SinkType>(std::forward<Args>(args)...);
}
};
}
int main()
{
// 1.1创建落地对象
//ptr是一个智能指针,可以自动管理对象生命周期,不需要手动释放
//ptr是一个父类指针,可以指向子类对象,但是不能调用子类独有接口,只能调用父类接口,所以这里创建的是LogSink的智能指针
lgrlog::LogSink::ptr sink = lgrlog::SinkFactory::create<lgrlog::StdoutSink>();
// 1.2落地日志
sink->log("hello world", 11);// 落地方向类型1:log标准输出
// 2.1 创建另一个落地对象
std::string filename = "./log/test.log";
lgrlog::LogSink::ptr sink2 = lgrlog::SinkFactory::create<lgrlog::FileSink>(filename);
// 2.2 落地日志
sink2->log("hello world", 11);// 落地方向类型2:log指定到文件
// 3.1 创建另一个落地对象
lgrlog::LogSink::ptr sink3 = lgrlog::SinkFactory::create<lgrlog::RollBySizeSink>("./log/roll-by-size", 1024 * 1024);
// 3.2 落地日志
sink3->log("hello world", 11);// 落地方向类型3:滚动显示log
return 0;
}
#endif
2、