在Java生态中涉及到日志的框架有很多,比如说Log4j、Logback、Log4j2以及Slf4j等等(日志体系结构见这篇博客),如果在我们的开发过程中直接使用具体的API,那么在未来如果需要去实现日志组件的升级和切换,就会变得很困难,基本上是属于牵一发而动全身。
一般情况下,推荐的做法是直接使用门面模式,也就是说提供一个统一的接口去访问多个子系统的不同的实现类,这样的话对于应用程序来说无论底层的日志框架如何变都不需要有任何的感知,只要门面服务做的足够好,随意更换另外一个日志框架,应用程序不需要修改任意一行代码就可以直接上线,即使有一天要更换代码的目志框架,只需要修改一个jar包,最多再改改日志输出相关的配置文件就可以了。
这个设计有一个很好的点,就是解除了应用,和日志框架之间的耦合。
下面我将通过一个简单的例子演示如何使用门面模式(Facade Pattern)来封装日志系统,从而解耦应用程序与具体的日志框架。
1.定义日志门面接口 首先,创建一个统一的日志接口,作为应用程序与日志系统之间的桥梁。
// LogFacade.java
public interface LogFacade {
void debug(String message);
void info(String message);
void warn(String message);
void error(String message);
}
2.实现具体的日志门面 假设我们当前使用的是 SLF4J 作为底层日志框架,可以创建一个实现类来适配它。
// Slf4jLogFacade.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jLogFacade implements LogFacade {
private Logger logger;
public Slf4jLogFacade(Class<?> clazz) {
this.logger = LoggerFactory.getLogger(clazz);
}
@Override
public void debug(String message) {
logger.debug(message);
}
@Override
public void info(String message) {
logger.info(message);
}
@Override
public void warn(String message) {
logger.warn(message);
}
@Override
public void error(String message) {
logger.error(message);
}
}
如果将来需要切换到 Log4j2,只需创建另一个实现类:
// Log4j2LogFacade.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2LogFacade implements LogFacade {
private Logger logger;
public Log4j2LogFacade(Class<?> clazz) {
this.logger = LogManager.getLogger(clazz);
}
@Override
public void debug(String message) {
logger.debug(message);
}
@Override
public void info(String message) {
logger.info(message);
}
@Override
public void warn(String message) {
logger.warn(message);
}
@Override
public void error(String message) {
logger.error(message);
}
}
3.创建日志工厂 为了简化日志门面的实例化,可以使用工厂模式来创建 LogFacade 的实例。
// LogFactory.java
public class LogFactory {
private static final String LOG_IMPLEMENTATION = "slf4j"; // 可以通过配置文件读取
public static LogFacade getLog(Class<?> clazz) {
switch (LOG_IMPLEMENTATION.toLowerCase()) {
case "log4j2":
return new Log4j2LogFacade(clazz);
case "slf4j":
default:
return new Slf4jLogFacade(clazz);
}
}
}
4.在应用程序中使用日志门面 应用程序代码仅依赖于 LogFacade 接口,不直接使用具体的日志框架。
// ExampleService.java
public class ExampleService {
private static final LogFacade logger = LogFactory.getLog(ExampleService.class);
public void performAction() {
logger.info("Action started.");
try {
// 业务逻辑
logger.debug("Performing detailed debug information.");
} catch (Exception e) {
logger.error("An error occurred: " + e.getMessage());
}
logger.info("Action completed.");
}
}
5.切换日志框架 假设你现在想将日志框架从 SLF4J 切换到 Log4j2,只需:修改 LogFactory 中的 LOG_IMPLEMENTATION 变量为 “log4j2”。这可以通过读取配置文件来实现,而不需要修改代码。
// LogFactory.java (修改部分)
private static final String LOG_IMPLEMENTATION = Config.get("log.implementation", "slf4j");
确保引入 Log4j2 的依赖,并移除或排除 SLF4J 相关的依赖。应用程序代码无需任何修改,日志系统将自动切换到新的框架。