日志与策略模式

日志与策略模式

策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它允许你定义一系列算法或行为,并将它们封装在独立的类中,使得它们可以互相替换。策略模式使得算法可以独立于使用它的客户端而变化。


1. 策略模式的核心思想

  • 分离变化:将可能变化的算法或行为抽象出来,独立于客户端。
  • 多态性:通过接口或基类定义算法的通用行为,具体实现由子类完成。
  • 动态切换:客户端可以在运行时动态切换具体的策略。

2. 策略模式的组成部分

  1. 策略接口(Strategy Interface)

    • 定义算法的通用接口。
    • 所有具体策略类必须实现这个接口。
  2. 具体策略类(Concrete Strategies)

    • 实现策略接口,提供具体的算法或行为。
  3. 上下文类(Context)

    • 持有一个策略对象的引用。
    • 通过策略接口调用具体的算法。

3. 策略模式的优点

  • 开闭原则:新增策略无需修改现有代码。
  • 消除条件语句:避免使用大量的 if-elseswitch-case 语句。
  • 提高代码复用性:策略可以被多个上下文共享。

4. 策略模式的适用场景

  • 当一个系统需要在多种算法或行为之间动态切换时。
  • 当一个类有多种行为,且这些行为在类中以条件语句的形式出现时。
  • 当算法的实现需要独立于使用它的客户端时。

简单日志模拟

​ 根据策略模式,我们可以实现一个简单的日志模块。

1. 日志模块的核心功能

我们的日志模块需要支持以下功能:

  • 日志等级:支持 DEBUGINFOWARNINGERRORFATAL 五个等级。
  • 日志输出方式:支持控制台输出和文件输出。
  • 日志格式化:每条日志包含时间、日志等级、进程 ID、文件名和行号。
  • 线程安全:确保多线程环境下日志输出的正确性。

2. 设计思路

为了实现上述功能,我们采用以下设计思路:

  1. 策略模式:将日志的输出方式抽象为策略接口,支持动态切换。
  2. RAII 技术:通过对象的析构函数自动刷新日志。
  3. 宏定义:简化日志调用,自动填充文件名和行号。

3. 代码实现

3.1 日志等级

我们使用枚举类 LogLevel 定义日志等级:

enum class LogLevel {
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
3.2 日志策略接口

日志策略接口 LogStrategy 定义了日志输出的通用行为:

class LogStrategy {
public:
    virtual ~LogStrategy() = default;
    virtual void SyncLog(const std::string &message) = 0;
};
3.3 具体策略类
  • 控制台策略:将日志输出到控制台。

    class ConsoleLogStrategy : public LogStrategy {
    public:
        void SyncLog(const std::string &message) override {
            LockGuard lockGuard(_mutex);
            std::cout << message << std::endl;
        }
    
    private:
        Mutex _mutex;
    };
    
  • 文件策略:将日志输出到文件。

    class FileLogStrategy : public LogStrategy {
    public:
        FileLogStrategy(const std::string &logpath = "./log", const std::string &logname = "log.txt")
            : _logpath(logpath), _logname(logname) {
            if (!std::filesystem::exists(_logpath)) {
                std::filesystem::create_directories(_logpath);
            }
        }
    
        void SyncLog(const std::string &message) override {
            LockGuard lockguard(_mutex);
            std::string log = _logpath + _logname;
            std::ofstream out(log, std::ios::app);
            if (!out.is_open()) return;
            out << message << '\n';
            out.close();
        }
    
    private:
        std::string _logpath;
        std::string _logname;
        Mutex _mutex;
    };
    
3.4 日志记录器

Logger 类是日志模块的核心,负责管理日志策略和生成日志消息:

class Logger {
public:
    Logger() {
        _strategy = std::make_shared<ConsoleLogStrategy>(); // 默认策略
    }

    void EnableConsoleLog() {
        _strategy = std::make_shared<ConsoleLogStrategy>();
    }

    void EnableFileLog() {
        _strategy = std::make_shared<FileLogStrategy>();
    }

    class LogMessage {
    public:
        LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger)
            : _currtime(CurrentTime()),
              _level(level),
              _pid(::getpid()),
              _filename(filename),
              _line(line),
              _logger(logger)
        {
            std::stringstream ssbuffer;
            ssbuffer << "[" << _currtime << "] "
                     << "[" << Level2String(_level) << "] "
                     << "[" << _pid << "] "
                     << "[" << _filename << "] "
                     << "[" << _line << "] - ";
            _loginfo = ssbuffer.str();
        }

        template<typename T>
        LogMessage &operator<<(const T &info) {
            std::stringstream ss;
            ss << info;
            _loginfo += ss.str();
            return *this;
        }

        ~LogMessage() {
            if (_logger._strategy)
                _logger._strategy->SyncLog(_loginfo);
        }

    private:
        std::string _currtime;
        LogLevel _level;
        pid_t _pid;
        std::string _filename;
        int _line;
        Logger &_logger;
        std::string _loginfo;
    };

    LogMessage operator()(LogLevel level, const std::string &filename, int line) {
        return LogMessage(level, filename, line, *this);
    }

private:
    std::shared_ptr<LogStrategy> _strategy;
};
3.5 宏定义

为了方便使用,我们定义了以下宏:

#define LOG(level) logger(level, __FILE__, __LINE__)
#define ENABLECONSOLELOG() logger.EnableConsoleLog()
#define ENABLEFILELOG() logger.EnableFileLog()

4. 使用示例

以下是日志模块的使用示例:

#include "Log.hpp"

using namespace LogModule;

int main() {
    LOG(LogLevel::DEBUG) << "This is a debug log.";
    LOG(LogLevel::INFO) << "This is an info log.";

    ENABLEFILELOG();
    LOG(LogLevel::WARNING) << "This is a warning log written to a file.";

    return 0;
}

5. 逻辑分析

​ 通过 LOG(LogLevel::DEBUG) 这行代码,日志模块首先会调用 Logger 类的 operator() 函数,创建一个 LogMessage 临时对象。这个对象的构造函数会初始化日志的基本信息,包括当前时间、日志等级、进程 ID、文件名和行号,并将这些信息格式化为日志头(如 [2023-10-01 12:34:56] [DEBUG] [1234] [main.cpp] [42] -)。接着,通过链式调用 operator<<,日志内容(如 "hello file"12)被逐步追加到日志信息中。最后,当 LogMessage 对象超出作用域时,其析构函数会自动调用 SyncLog 方法,将完整的日志信息(如 [2023-10-01 12:34:56] [DEBUG] [1234] [main.cpp] [42] - hello file12)根据当前的日志策略(控制台或文件)输出到目标位置。整个过程通过 RAII 技术确保日志的自动刷新,并通过策略模式支持动态切换输出方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值