设计模式与日志系统:工厂模式创建Logger、装饰器增强日志、责任链处理日志

日志系统,说白了就是给程序装个黑匣子。我做了十几年项目,见过太多线上事故查起来像大海捞针——就是因为日志没设计好。今天咱们聊聊怎么用三种设计模式,搭一个既灵活又强大的日志框架。

一、为什么日志系统需要设计模式?

你想想看,一个日志系统要解决哪些问题?

  • 创建问题:不同场景需要不同Logger(文件日志、控制台日志、远程日志)
  • 增强问题:基础日志不够用,要加时间戳、线程信息、格式化输出
  • 路由问题:不同级别的日志要去不同地方(DEBUG写文件,ERROR发邮件)

这三个问题,正好对应三种设计模式。我在项目中遇到过不少团队把日志代码写成一团乱麻,后来用这套组合拳,代码清晰多了。

核心思路:工厂模式管创建,装饰器模式管增强,责任链模式管分发。各司其职,互不干扰。

二、工厂模式:统一管理Logger创建

先看工厂模式。我个人习惯用简单工厂+反射,这样新增Logger类型不用改代码。

// Java 示例:Logger工厂
public class LoggerFactory {
    private static Map<String, Class<? extends Logger>> registry = new HashMap<>();
    
    static {
        registry.put("file", FileLogger.class);
        registry.put("console", ConsoleLogger.class);
        registry.put("remote", RemoteLogger.class);
    }
    
    public static Logger createLogger(String type) {
        try {
            Class<? extends Logger> clazz = registry.get(type);
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建Logger失败: " + type);
        }
    }
}
// C++ 示例:Logger工厂
class LoggerFactory {
public:
    static Logger* createLogger(const std::string& type) {
        if (type == "file") return new FileLogger();
        else if (type == "console") return new ConsoleLogger();
        else if (type == "remote") return new RemoteLogger();
        else return nullptr;
    }
};

避坑指南:我曾经在工厂里直接new对象,结果每次加新Logger都要改工厂代码。后来改成注册表模式,新增类型只需注册类名,工厂完全不用动。

三、装饰器模式:给日志动态加功能

基础Logger只能输出原始消息。但实际项目中,我们需要时间戳、线程ID、调用位置等信息。装饰器模式就是干这个的。

// Java 示例:装饰器模式
public interface Logger {
    void log(String message);
}

public class TimestampDecorator implements Logger {
    private Logger wrapped;
    
    public TimestampDecorator(Logger logger) {
        this.wrapped = logger;
    }
    
    @Override
    public void log(String message) {
        String timestamp = LocalDateTime.now().format(
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        wrapped.log("[" + timestamp + "] " + message);
    }
}

public class ThreadDecorator implements Logger {
    private Logger wrapped;
    
    public ThreadDecorator(Logger logger) {
        this.wrapped = logger;
    }
    
    @Override
    public void log(String message) {
        wrapped.log("[Thread-" + Thread.currentThread().getId() + "] " + message);
    }
}
// C++ 示例:装饰器模式
class TimestampDecorator : public Logger {
private:
    Logger* wrapped;
public:
    TimestampDecorator(Logger* logger) : wrapped(logger) {}
    void log(const std::string& message) override {
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        std::string ts = std::ctime(&time);
        ts.pop_back(); // 去掉换行
        wrapped->log("[" + ts + "] " + message);
    }
};

使用的时候,可以像搭积木一样组合:

Logger logger = new ThreadDecorator(
    new TimestampDecorator(
        LoggerFactory.createLogger("file")
    )
);
logger.log("用户登录成功");

关键点:装饰器模式遵循开闭原则。想加新功能?写个新装饰器类就行,不用改现有代码。

四、责任链模式:日志分级处理

日志是有级别的:DEBUG、INFO、WARN、ERROR。不同级别应该走不同通道。责任链模式正好解决这个问题。

// Java 示例:责任链模式
public abstract class LogHandler {
    protected LogHandler next;
    protected LogLevel level;
    
    public void setNext(LogHandler handler) {
        this.next = handler;
    }
    
    public void handle(LogLevel level, String message) {
        if (this.level.compareTo(level) <= 0) {
            write(message);
        }
        if (next != null) {
            next.handle(level, message);
        }
    }
    
    protected abstract void write(String message);
}

public class ConsoleHandler extends LogHandler {
    public ConsoleHandler() { this.level = LogLevel.DEBUG; }
    protected void write(String message) {
        System.out.println("[控制台] " + message);
    }
}

public class FileHandler extends LogHandler {
    public FileHandler() { this.level = LogLevel.INFO; }
    protected void write(String message) {
        // 写入文件
    }
}

public class EmailHandler extends LogHandler {
    public EmailHandler() { this.level = LogLevel.ERROR; }
    protected void write(String message) {
        // 发送邮件
    }
}
// C++ 示例:责任链模式
class LogHandler {
protected:
    LogHandler* next;
    LogLevel level;
public:
    void setNext(LogHandler* handler) { next = handler; }
    void handle(LogLevel lvl, const std::string& msg) {
        if (level <= lvl) write(msg);
        if (next) next->handle(lvl, msg);
    }
    virtual void write(const std::string& msg) = 0;
};

组装责任链:

LogHandler console = new ConsoleHandler();
LogHandler file = new FileHandler();
LogHandler email = new EmailHandler();

console.setNext(file);
file.setNext(email);

// DEBUG级别:控制台输出
console.handle(LogLevel.DEBUG, "调试信息");
// ERROR级别:控制台 + 文件 + 邮件
console.handle(LogLevel.ERROR, "数据库连接失败");

注意:责任链的顺序很重要。我曾经把邮件处理器放在最前面,结果DEBUG日志也发邮件,测试邮箱直接炸了。正确的做法是:低级别在前,高级别在后。

五、三种模式组合实战

现在我们把三种模式串起来。完整的日志系统是这样工作的:

  1. 工厂模式:根据配置创建基础Logger(文件、控制台等)
  2. 装饰器模式:给Logger加上时间戳、线程信息等增强功能
  3. 责任链模式:根据日志级别决定输出到哪些通道
// 完整使用示例
public class LogSystem {
    public static void main(String[] args) {
        // 1. 工厂创建基础Logger
        Logger baseLogger = LoggerFactory.createLogger("file");
        
        // 2. 装饰器增强
        Logger decorated = new TimestampDecorator(
            new ThreadDecorator(baseLogger));
        
        // 3. 责任链处理
        LogHandler chain = buildChain(decorated);
        
        // 4. 使用
        chain.handle(LogLevel.INFO, "系统启动完成");
        chain.handle(LogLevel.ERROR, "内存溢出");
    }
    
    private static LogHandler buildChain(Logger logger) {
        ConsoleHandler console = new ConsoleHandler(logger);
        FileHandler file = new FileHandler(logger);
        EmailHandler email = new EmailHandler(logger);
        
        console.setNext(file);
        file.setNext(email);
        return console;
    }
}

我的经验:实际项目中,我会把配置外移到YAML文件。工厂根据配置创建Logger,装饰器根据配置决定加哪些功能,责任链根据配置决定路由规则。这样改日志行为只需要改配置,不用改代码。

六、整体架构图

下面这张图展示了三种模式如何协作:

日志系统架构:三种设计模式协作 工厂模式 创建基础Logger FileLogger ConsoleLogger RemoteLogger 装饰器模式 增强Logger功能 TimestampDecorator ThreadDecorator FormatDecorator 责任链模式 日志分级处理 DEBUG → 控制台 INFO → 文件 ERROR → 邮件 工作流程 1. 应用层调用 log("用户登录") 2. 工厂模式创建 FileLogger 实例 3. 装饰器模式自动添加时间戳、线程ID 4. 责任链模式判断日志级别: - 如果是INFO,写入文件 - 如果是ERROR,写入文件 + 发送邮件

七、总结与避坑

这三种模式组合起来,日志系统就变得非常灵活。我总结几个关键点:

模式 职责 常见坑
工厂模式 创建Logger实例 不要硬编码类型,用配置或注册表
装饰器模式 动态增强功能 注意装饰顺序,时间戳应该在内容最外层
责任链模式 分级路由处理 链的顺序决定优先级,低级别在前

性能提醒:装饰器链不要过长。我曾经见过有人加了七八层装饰器,每次日志调用都要走一大串方法,性能直接掉了一个数量级。一般2-3层就够了。

嗯,这套方案我在好几个项目里用过,从几十万QPS的高并发系统到嵌入式设备都能跑。关键是——代码结构清晰,后期维护起来特别省心。

公众号:蓝海资料掘金营,微信deep3321