Logback 是一个强大且灵活的日志框架,适用于各种规模的应用程序。通过自定义 Appender,可以实现复杂的日志处理逻辑,如敏感信息脱敏和异步写入,从而提升系统的安全性和性能。
图片
在某些情况下,默认的 Appender 无法满足特定需求,例如:
自动脱敏敏感信息。
异步处理日志以提高性能。
将日志发送到外部系统或服务。
<!-- Disruptor --><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.4.4</version></dependency><!-- Logback Classic --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency>
创建一个名为SensitiveDataMaskingAppender的类,该类继承自AppenderBase<ILoggingEvent>,并在其中使用Disruptor队列进行异步处理。
package com.example.demo.logging;importch.qos.logback.classic.spi.ILoggingEvent;importch.qos.logback.core.AppenderBase;importcom.lmax.disruptor.*;importcom.lmax.disruptor.dsl.Disruptor;importorg.slf4j.MDC;importjava.util.concurrent.Executors;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclass SensitiveDataMaskingAppender extends AppenderBase<ILoggingEvent>{// 正则表达式模式用于匹配身份证号码privatestaticfinal Pattern ID_CARD_PATTERN=Pattern.compile("\\d{15}(\\d{2}[A-Za-z])?");// 正则表达式模式用于匹配手机号码privatestaticfinal Pattern PHONE_NUMBER_PATTERN=Pattern.compile("(\\+86)?(1[3-9]\\d{9})");private RingBuffer<Event>ringBuffer;@Overridepublicvoidstart(){ super.start();// 创建事件工厂EventFactory<Event>factory=Event::new;// 设置环形缓冲区大小,必须是2的幂intbufferSize=1024;// 使用缓存线程池Executor executor=Executors.newCachedThreadPool();// 创建Disruptor实例Disruptor<Event>disruptor=new Disruptor<>(factory,bufferSize,executor,ProducerType.MULTI,new BusySpinWaitStrategy());// 设置事件处理器disruptor.handleEventsWith(new EventHandler<Event>(){@Overridepublicvoid onEvent(Event event,long sequence,booleanendOfBatch)throws Exception {// 脱敏日志消息String logMessage=maskSensitiveData(event.getLogMessage());// 打印脱敏后的日志消息到控制台System.out.println(logMessage);} });// 获取RingBufferringBuffer=disruptor.getRingBuffer();// 启动Disruptordisruptor.start();}@Overrideprotected void append(ILoggingEvent eventObject){// 获取下一个序列号long sequence=ringBuffer.next();try {// 根据序列号获取事件对象Event event=ringBuffer.get(sequence);// 设置日志消息event.setLogMessage(eventObject.getMessage());} finally {// 发布事件ringBuffer.publish(sequence);} }/** * 脱敏日志消息中的敏感信息 * @param message 日志消息 * @return 脱敏后的日志消息 */private String maskSensitiveData(String message){ Matcher idCardMatcher=ID_CARD_PATTERN.matcher(message);while(idCardMatcher.find()){// 替换身份证号码中间部分为星号String maskedIdCard=idCardMatcher.group().substring(0,6)+"********"+idCardMatcher.group().substring(14);message=message.replace(idCardMatcher.group(),maskedIdCard);} Matcher phoneNumberMatcher=PHONE_NUMBER_PATTERN.matcher(message);while(phoneNumberMatcher.find()){// 替换手机号码中间部分为星号String maskedPhoneNumber=phoneNumberMatcher.group().substring(0,3)+"****"+phoneNumberMatcher.group().substring(7);message=message.replace(phoneNumberMatcher.group(),maskedPhoneNumber);}returnmessage;}// 定义事件类privatestaticclass Event { private String logMessage;publicString getLogMessage(){returnlogMessage;}publicvoid setLogMessage(String logMessage){ this.logMessage=logMessage;} } }
在src/main/resources/logback-spring.xml文件中配置自定义appender:
<configuration> <!-- 自定义Appender配置 --> <appender name="SENSITIVE_MASKING_APPENDER" class="com.example.demo.logging.SensitiveDataMaskingAppender"> </appender> <!-- 根Logger配置 --> <root level="info"> <appender-ref ref="SENSITIVE_MASKING_APPENDER"/> </root> </configuration>
创建一个简单的控制器来测试日志记录功能。
package com.example.demo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController publicclass UserController { // 获取日志记录器 privatestaticfinal Logger logger = LoggerFactory.getLogger(UserController.class); /** * 处理/user请求,记录用户信息并返回响应 * @param idCard 用户身份证号码 * @param phoneNumber 用户手机号码 * @return 响应字符串 */ @GetMapping("/user") public String getUserInfo(@RequestParam String idCard, @RequestParam String phoneNumber) { // 记录用户信息到日志 logger.info("User Info - ID Card: {}, Phone Number: {}", idCard, phoneNumber); return"User info logged"; } }
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
curl "http://localhost:8080/user?idCard=123456123456123456&phnotallow=13800138000"
User Info - ID Card: 123456********56, Phone Number: 138****8000