原文鏈接: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.logstd::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:滾動顯示logreturn 0;
}#endif
2、