资讯动态

Linux加餐(一):藏在Linux中的设计模式(一)策略模式与日志

发布时间:2026/8/21 20:21:26 来源:尧图企业网站定制
◆ 博主名称 小此方-CSDN博客大家好欢迎来到小此方的博客。⭐️Linux系列个人专栏 【主题曲】Linux⭐️此方的GitHub github_此方⭐️Re系列专栏我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)文章目录概要序論一、什么是设计模式1.2 23种设计模式由Gemini生成资料1.2.1 最常用的设计模式1.2.2 一般常用的设计模式1.2.3 不常用的设计模式二、初步认识日志2.1 日志的概念与作用2.2 日志的格式指标2.3 日志的实现方案三、日志的完整代码与解析3.1Log.hpp3.2Mutex.hpp3.3 策略模式在Log.hpp中的体现3.3.1 抽象策略角色LogFlashStrategy3.3.2 具体策略角色一ConsoleLogStrategy3.3.3 具体策略角色二FileLogStrategy3.3.4 策略模式带来的设计优势概要序論Hello大家好我是此方。本文介绍设计模式的基本概念及23种常见设计模式并以日志系统为例讲解日志的作用、格式与实现方案。在代码实践中重点分析策略模式在日志组件中的应用以及其带来的解耦与扩展优势。一、什么是设计模式在软件开发领域随着行业规模的扩张开发者之间的技术水平和经验存在差异。为了避免重复踩坑并提升代码质量前辈们针对许多经典且高频出现的软件设计场景总结提炼出了一套对应的标准解决方案这就是设计模式。1.2 23种设计模式由Gemini生成资料经典的 GoFGang of Four设计模式共有 23 种划分为创建型、结构型和行为型三大类。结合实际工业界开发频次划分如下1.2.1 最常用的设计模式单例模式Singleton确保一个类只有一个实例并提供全局访问点。工厂方法模式Factory Method定义创建对象的接口让子类决定实例化哪一个类。抽象工厂模式Abstract Factory提供创建一系列相关或相互依赖对象的接口无需指定具体类。策略模式Strategy定义算法族分别封装使它们可以互相替换本篇重点。观察者模式Observer定义对象间一对多的依赖关系状态改变时自动通知所有依赖者。适配器模式Adapter将一个类的接口转换成客户希望的另一个接口。装饰器模式Decorator动态地给一个对象添加额外职责比继承更灵活。1.2.2 一般常用的设计模式建造者模式Builder将复杂对象的构建与表示分离使同样的构建过程可创建不同的表示。代理模式Proxy为其他对象提供一种代理以控制对这个对象的访问。外观模式Facade为子系统中的一组接口提供一个一致的高层界面。模板方法模式Template Method定义算法骨架将部分步骤延迟到子类实现。组合模式Composite将对象组合成树形结构以表示“部分-整体”的层次结构。命令模式Command将请求封装为对象实现请求调用者与接收者的解耦。状态模式State允许对象在内部状态改变时改变其行为。1.2.3 不常用的设计模式原型模式Prototype通过拷贝现有的原型对象来创建新对象。桥接模式Bridge将抽象部分与实现部分分离使它们都可以独立变化。享元模式Flyweight运用共享技术有效地支持大量细粒度的对象。责任链模式Chain of Responsibility将请求沿着处理链传递直到有对象处理它为止。迭代器模式Iterator顺序访问聚合对象中的元素现代编程语言大多已在标准库层面内置实现。中介者模式Mediator用一个中介对象来封装一系列对象的交互降低耦合度。备忘录模式Memento在不破坏封装性的前提下捕获并保存对象的内部状态用于恢复。访问者模式Visitor在不改变元素类的前提下定义作用于这些元素的新操作。解释器模式Interpreter给定一个语言定义它的文法表示及对应的解释器。二、初步认识日志2.1 日志的概念与作用计算机中的日志是记录系统和软件运行过程中发生事件的文件。它是系统维护、故障排查和安全管理的重要工具主要作用包括监控运行状态实时了解程序的执行流程与健康状况。记录异常信息在系统出现错误时精准捕获现场数据。定位与修复问题帮助程序员快速查找 Bug 根源并进行针对性修复。2.2 日志的格式指标设计一个标准的日志系统需要明确其输出格式。通常包含以下指标核心必选指标时间戳记录日志产生的具体时间。日志等级标注日志的严重程度如 DEBUG、INFO、WARNING、ERROR、FATAL。日志内容具体的文本描述或数据细节。扩展可选指标文件名与行号精准定位到产生日志的源码位置。进程与线程 ID在多线程或并发环境下区分不同执行流。2.3 日志的实现方案在实际开发中市面上存在许多现成的优秀日志库解决方案例如spdlog、glog、Boost.Log以及Log4cxx等。为了深入理解日志系统的设计思想本项目将采用自定义日志的方式进行实现并引入设计模式中的策略模式来灵活处理日志的输出行为。三、日志的完整代码与解析手写代码如有错误还请指出。3.1Log.hpp#ifndef__LOG_HPP__#define__LOG_HPP__#includeMutex.hpp#includefilesystem#includeiostream#includectime#includestring#includesys/types.h#includeunistd.h#includefstream#includesstream#includememory#includecstdionamespaceLogModule{conststd::string CAGE\n;classLogFlashStrategy{public:~LogFlashStrategy()default;virtualvoidSyncLog(conststd::stringmessage)0;};classConsoleLogStrategy:publicLogFlashStrategy{public:voidSyncLog(conststd::stringlogmessage)override{MyMutex::GuardMutex_guardmutex(_mutex);std::coutlogmessageCAGE;}private:MyMutex::Mutex _mutex;};conststd::string default_filenamelog.txt;conststd::string default_filepath./log;//./log/log.txtclassFileLogStrategy:publicLogFlashStrategy{public:FileLogStrategy(conststd::string filedefault_filename,conststd::string pathdefault_filepath):_filename(file),_filepath(path){MyMutex::GuardMutex_guardmutex(_mutex);if(std::filesystem::exists(_filepath)){return;}try{std::filesystem::create_directories(_filepath);}catch(conststd::filesystem::filesystem_error_exception){std::cout_exception.what()CAGE;}}voidSyncLog(conststd::stringlogmessage){MyMutex::GuardMutex_guardmutex(_mutex);std::string pathfile_filepath(_filepath.back()/?:/)_filename;std::ofstreamout(pathfile,std::ios::app);if(!out.is_open()){return;}outlogmessageCAGE;out.close();}private:MyMutex::Mutex _mutex;std::string _filename;std::string _filepath;};enumclassLEVEL{DEBUG,INFO,WARNING,ERROR,FATAL};std::stringGetCurrentTime(){time_t cur_timetime(nullptr);structtmresult;localtime_r(cur_time,result);// std:: string current_time ;// current_time [ // std::to_string(result.tm_year1900) // std::to_string(result.tm_mon1) // std::to_string(result.tm_mday) // std::to_string(result.tm_hour) // std::to_string(result.tm_min) // std::to_string(result.tm_sec) // ];chartimebuffer[128];snprintf(timebuffer,sizeof(timebuffer),%4d-%02d-%02d %02d:%02d:%02d,result.tm_year1900,result.tm_mon1,result.tm_mday,result.tm_hour,result.tm_min,result.tm_sec);returntimebuffer;}std::stringLevelToString(LEVEL level){switch(level){caseLEVEL::DEBUG:returnDEBUG;caseLEVEL::INFO:returnINFO;caseLEVEL::WARNING:returnWARNING;caseLEVEL::ERROR:returnERROR;caseLEVEL::FATAL:returnFATAL;default:returnUNKNOWORNING;}}classLogger{public:Logger(){EnableConsolLogStrategy();}voidEnableConsolLogStrategy(){_current_flash_tsrategystd::make_uniqueConsoleLogStrategy();}voidEnableFileLogStrategy(){_current_flash_tsrategystd::make_uniqueFileLogStrategy();}classLogMessage{public:LogMessage(LEVELlevel,std::stringfile,size_t line,LogModule::Loggerlogger):_current_time(GetCurrentTime()),_level(level),_pid(getpid()),_src_file(file),_line(line),_logger(logger){// std::string ret ;// ret [ _current_time ];// ret [ LevelToString(_level) ];// ret [ std::to_string(_pid) ];// ret [ _src_file ] ;// ret [ std::to_string(_line) ] ;std::stringstream ret;ret[_current_time][LevelToString(_level)][_pid][_src_file][_line] -;_hole_messageret.str();}templateclassTLogMessageoperator(constTinfo){std::stringstream ss;ssinfo;_hole_messagess.str();return*this;}~LogMessage(){if(_logger._current_flash_tsrategy)_logger._current_flash_tsrategy-SyncLog(_hole_message);}private:std::string _current_time;LEVEL _level;pid_t _pid;std::string _src_file;size_t _line;std::string _hole_message;LogModule::Logger_logger;};LogMessageoperator()(LEVEL level,std::string filename,size_t line){returnLogMessage(level,filename,line,*this);}private:std::unique_ptrLogFlashStrategy_current_flash_tsrategy;};Logger logger;#defineLOG(level)logger(level,__FILE__,__LINE__)#defineENABLE_CONSOLE_LOG_STRATEGY()logger.EnableConsolLogStrategy()#defineENABLE_FILE_LOG_STRATEGY()logger.EnableFileLogStrategy()}#endif3.2Mutex.hpp#ifndef__MUTEX_HPP__#define__MUTEX_HPP__#includepthread.hnamespaceMyMutex{classMutex{public:Mutex(){pthread_mutex_init(_mutex,nullptr);}voidLock(){pthread_mutex_lock(_mutex);}voidUnLock(){pthread_mutex_unlock(_mutex);}~Mutex(){pthread_mutex_destroy(_mutex);}private:pthread_mutex_t _mutex;};classGuardMutex{public:GuardMutex(Mutex mutex):_mutex(mutex){_mutex.Lock();}~GuardMutex(){_mutex.UnLock();}private:Mutex _mutex;};}#endif3.3 策略模式在Log.hpp中的体现在日志模块的实际落地中日志消息的生成与格式化和日志消息的刷新去向如打印到终端、写入磁盘文件、发送至网络等属于两个独立的关注点。通过策略模式我们将“日志刷新”行为抽象为统一的策略接口从而实现了日志系统对扩展开放、对修改封闭的核心设计原则。3.3.1 抽象策略角色LogFlashStrategyLogFlashStrategy是所有日志刷盘策略的基类抽象接口它规定了所有具体策略必须实现的统一刷新行为classLogFlashStrategy{public:virtual~LogFlashStrategy()default;virtualvoidSyncLog(conststd::stringmessage)0;};虚析构函数确保通过基类指针销毁派生类策略对象时能够正确调用子类的析构函数防止内存泄漏。纯虚函数 SyncLog定义了日志刷新的标准接口接收格式化好的日志字符串具体的刷盘逻辑延迟到子类中实现。3.3.2 具体策略角色一ConsoleLogStrategyConsoleLogStrategy继承自LogFlashStrategy负责将日志同步刷新输出到终端控制台classConsoleLogStrategy:publicLogFlashStrategy{public:voidSyncLog(conststd::stringlogmessage)override{MyMutex::GuardMutex_guardmutex(_mutex);std::coutlogmessageCAGE;}private:MyMutex::Mutex _mutex;};线程安全保证多线程并发调用日志打印时控制台输出很容易发生字符穿插混乱。这里利用 RAII 机制的锁GuardMutex对输出过程进行加锁保护保证每一条日志都能完整地打印到屏幕上。3.3.3 具体策略角色二FileLogStrategyFileLogStrategy同样继承自LogFlashStrategy负责将日志持久化写入本地磁盘文件conststd::string default_filenamelog.txt;conststd::string default_filepath./log;// ./log/log.txtclassFileLogStrategy:publicLogFlashStrategy{public:FileLogStrategy(conststd::string filedefault_filename,conststd::string pathdefault_filepath):_filename(file),_filepath(path){MyMutex::GuardMutex_guardmutex(_mutex);if(std::filesystem::exists(_filepath)){return;}try{std::filesystem::create_directories(_filepath);}catch(conststd::filesystem::filesystem_error_exception){std::cout_exception.what()CAGE;}}voidSyncLog(conststd::stringlogmessage)override{MyMutex::GuardMutex_guardmutex(_mutex);std::string pathfile_filepath(_filepath.back()/?:/)_filename;std::ofstreamout(pathfile,std::ios::app);if(!out.is_open()){return;}outlogmessageCAGE;out.close();}private:MyMutex::Mutex _mutex;std::string _filename;std::string _filepath;};目录自动建创构造函数中利用 C17 的std::filesystem::create_directories自动检测并创建日志存储路径增强了代码的健壮性。追加写入模式在SyncLog中使用std::ios::app追加模式打开文件确保新日志顺次追加到文件末尾不会覆盖已有历史记录。并发文件保护同样在写入文件前后加上互斥锁保护防止多线程同时写入同一文件导致的数据竞争和内容损坏。3.3.4 策略模式带来的设计优势核心逻辑与刷盘细节解耦Logger只需要持有LogFlashStrategy的基类指针在需要刷盘时直接调用SyncLog即可根本不需要关心底层究竟是打印到控制台还是写入到了磁盘。极佳的扩展性符合开闭原则 OCP如果后续需要新增其他的刷盘方式例如按日志等级拆分文件存储的GroupLogStrategy或者通过网络发送日志的NetLogStrategy只需要继承LogFlashStrategy并实现新的策略类即可现有的控制台策略和文件策略代码完全无需变动。业界最通用的五种核心日志等级如下日志等级代表含义典型应用场景影响范围与处理建议DEBUG调试信息详细的函数入参、中间变量值、执行路径等细节。仅用于开发调试生产环境默认关闭。INFO正常信息系统启动/关闭、关键业务流程节点、成功交易通知。记录系统正常运行轨迹供日常运维与留痕查验。WARN/WARNING潜在警告接口重试成功、配置参数缺省、资源利用率过高。系统尚能继续运行但存在安全隐患需定期排查。ERROR局部错误捕获到非预期异常、单次请求失败、数据库超时。局部功能受损但未导致整体宕机需尽快定位修复。FATAL/CRITICAL致命崩溃内存溢出OOM、核心服务宕机、主数据库断开。服务彻底不可用或系统崩溃必须触发紧急告警并人工抢修。好的本期内容就到这里如果对你有帮助还不要忘记点赞三联支持。我是此方我们下期再见。bye! Linux、C、算法持续连载中欢迎关注WeChat Official Account 【此方的技术栈】。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价