装饰器模式与日志框架引言装饰器模式允许在运行时动态地给对象添加额外功能是扩展对象行为的重要手段。Java日志框架如SLF4J、Logback、Log4j2等都大量使用了装饰器模式。本文将介绍装饰器模式的原理以及在日志框架中的应用场景。一、装饰器模式基础1.1 模式结构// 组件接口 public interface DataSource { void write(String data); String read(); } // 具体组件 public class FileDataSource implements DataSource { private String filename; public FileDataSource(String filename) { this.filename filename; } Override public void write(String data) { // 写入文件 } Override public String read() { // 读取文件 return null; } } // 装饰器基类 public class DataSourceDecorator implements DataSource { protected DataSource wrappee; public DataSourceDecorator(DataSource wrappee) { this.wrappee wrappee; } Override public void write(String data) { wrappee.write(data); } Override public String read() { return wrappee.read(); } } // 具体装饰器 public class EncryptionDecorator extends DataSourceDecorator { public EncryptionDecorator(DataSource wrappee) { super(wrappee); } Override public void write(String data) { String encrypted encrypt(data); super.write(encrypted); } Override public String read() { String data super.read(); return decrypt(data); } private String encrypt(String data) { return Base64.getEncoder().encodeToString( data.getBytes()); } private String decrypt(String data) { return new String(Base64.getDecoder().decode(data)); } }1.2 使用示例public class Client { public static void main(String[] args) { DataSource source new FileDataSource(data.txt); // 添加压缩装饰器 DataSource compressed new CompressionDecorator(source); // 添加加密装饰器 DataSource secure new EncryptionDecorator(compressed); // 使用 secure.write(Sensitive Data); } }二、日志框架的装饰器2.1 Appender装饰器// Logback的Appender public class CustomAppender extends AppenderBaseILoggingEvent { private AppenderILoggingEvent targetAppender; public void setTargetAppender(AppenderILoggingEvent appender) { this.targetAppender appender; } Override protected void append(ILoggingEvent event) { if (targetAppender ! null) { // 前置处理 ILoggingEvent decoratedEvent decorate(event); targetAppender.doAppend(decoratedEvent); // 后置处理 afterAppend(decoratedEvent); } } protected ILoggingEvent decorate(ILoggingEvent event) { // 装饰日志事件 return event; } }2.2 日志过滤器public class SensitiveDataFilter extends FilterILoggingEvent { Override public FilterReply decide(ILoggingEvent event) { String message event.getFormattedMessage(); // 过滤敏感信息 if (containsSensitiveData(message)) { return FilterReply.DENY; } return FilterReply.NEUTRAL; } private boolean containsSensitiveData(String message) { return message.contains(password) || message.contains(credit_card) || message.contains(ssn); } }2.3 格式化装饰器public class JsonFormattingAppender extends AppenderBaseILoggingEvent { private AppenderILoggingEvent targetAppender; private ObjectMapper objectMapper; Override protected void append(ILoggingEvent event) { MapString, Object logData new HashMap(); logData.put(timestamp, event.getTimeStamp()); logData.put(level, event.getLevel().toString()); logData.put(logger, event.getLoggerName()); logData.put(message, event.getFormattedMessage()); logData.put(thread, event.getThreadName()); try { String json objectMapper.writeValueAsString(logData); targetAppender.doAppend(new Log4jLogEvent(json)); } catch (Exception e) { addError(Failed to format log, e); } } }三、Spring日志集成3.1 日志切面Aspect Component public class LoggingAspect { private final Logger logger LoggerFactory .getLogger(LoggingAspect.class); Around(execution(* com.example.service.*.*(..))) public Object logServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { String methodName joinPoint.getSignature().toShortString(); long startTime System.currentTimeMillis(); logger.info( Entering: {}, methodName); try { Object result joinPoint.proceed(); long duration System.currentTimeMillis() - startTime; logger.info( Exiting: {} ({}ms), methodName, duration); return result; } catch (Exception e) { long duration System.currentTimeMillis() - startTime; logger.error(!!! Exception in: {} ({}ms), methodName, duration, e); throw e; } } }3.2 请求日志装饰器Component public class RequestLoggingFilter extends OncePerRequestFilter { private final Logger logger LoggerFactory .getLogger(RequestLoggingFilter.class); Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { CachedBodyHttpServletRequest wrappedRequest new CachedBodyHttpServletRequest(request); logger.info( {} {}, request.getMethod(), request.getRequestURI()); long startTime System.currentTimeMillis(); filterChain.doFilter(wrappedRequest, response); long duration System.currentTimeMillis() - startTime; logger.info( {} {} ({}ms) [{}], request.getMethod(), request.getRequestURI(), duration, response.getStatus()); } }四、自定义日志装饰器4.1 MDC装饰器public class MDCLoggingDecorator { private static final Logger logger LoggerFactory.getLogger(MDCLoggingDecorator.class); public static void withUserContext(String userId, Runnable action) { try { MDC.put(userId, userId); MDC.put(timestamp, String.valueOf(System.currentTimeMillis())); action.run(); } finally { MDC.clear(); } } public static T T withUserContext(String userId, SupplierT supplier) { try { MDC.put(userId, userId); MDC.put(timestamp, String.valueOf(System.currentTimeMillis())); return supplier.get(); } finally { MDC.clear(); } } } // 使用 Service public class UserService { public void processUserAction(String userId, Action action) { MDCLoggingDecorator.withUserContext(userId, () - { logger.info(Processing user action); // 业务逻辑 }); } }4.2 性能日志装饰器public class PerformanceLogging { private static final Logger logger LoggerFactory.getLogger(PERFORMANCE); public static T T measure(String operation, SupplierT supplier) { long startTime System.nanoTime(); try { return supplier.get(); } finally { long duration (System.nanoTime() - startTime) / 1_000_000; logger.info(Operation {} completed in {}ms, operation, duration); } } public static void measure(String operation, Runnable action) { measure(operation, () - { action.run(); return null; }); } }五、日志配置最佳实践5.1 Logback配置configuration property nameLOG_PATTERN value%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n/ appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern${LOG_PATTERN}/pattern /encoder /appender appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender filelogs/application.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePatternlogs/application.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy encoder pattern${LOG_PATTERN}/pattern /encoder /appender appender nameASYNC_FILE classch.qos.logback.classic.AsyncAppender queueSize512/queueSize discardingThreshold0/discardingThreshold appender-ref refFILE/ /appender springProfile nameprod root levelINFO appender-ref refASYNC_FILE/ /root /springProfile springProfile namedev root levelDEBUG appender-ref refCONSOLE/ /root /springProfile /configuration5.2 分环境配置logging: level: root: INFO com.example: DEBUG org.springframework: INFO org.hibernate: WARN pattern: console: %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n file: name: logs/application.log总结装饰器模式在日志框架中有广泛应用通过装饰器可以动态添加日志格式化、过滤、传输等功能。在Spring应用中可以利用AOP和Filter实现日志的自动记录和增强。结合MDC等工具可以在日志中附加丰富的上下文信息便于问题追踪和性能分析。