Spring Boot日志冗余问题:如何提取有效请求路径?
问题场景
Spring Boot应用日志存在冗余内容,包含乱码和无效字符,例如日志条目:
"\u0006stdout\u0010��������\u0017\u001a�\u00012023-05-20 08:48:38.366 [http-nio-8080-exec-5] DEBUG o.s.w.f.CommonsRequestLoggingFilter - REQUEST DATA : GET /dg/public/course]\u0000\u0000\u0000�\u0000\u0000\u0000�"
仅需提取其中的GET /dg/public/course部分。当前使用的Logback配置如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <property name="LOGS" value="/tmp/logs" /> <appender name="Console" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern> %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable </Pattern> </layout> </appender> <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOGS}/spring-boot-logger.log</file> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <Pattern>%d %p %C{1.} [%t] %m%n</Pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- rollover daily and when the file reaches 10 MegaBytes --> <fileNamePattern>${LOGS}/archived/spring-boot-logger-%d{yyyy-MM-dd}.%i.log </fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> </appender> <!-- LOG everything at INFO level --> <root level="info"> <appender-ref ref="RollingFile" /> <appender-ref ref="Console" /> </root> <!-- LOG "com.teaching.gradzee*" at TRACE level --> <logger name="com.teaching.gradzee" level="trace" additivity="false"> <appender-ref ref="RollingFile" /> <appender-ref ref="Console" /> </logger> <logger name="org.hibernate.SQL" level="ERROR" /> <logger name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="DEBUG"/> <!-- <logger name="org.zalando.logbook" level="TRACE" />--> </configuration>
日志采集使用New Relic,Agent部署在EC2上,应用运行在Docker容器中,需实现仅提取目标请求路径的需求。
解决方案
1. 修复乱码根源
乱码多由Docker容器与EC2主机字符编码不一致导致,先统一编码:
- 在Docker启动命令或Dockerfile中添加环境变量:
ENV LANG=C.UTF-8,强制容器使用UTF-8编码。 - 检查New Relic Agent配置,确保日志采集时以UTF-8编码解析内容。
2. 自定义CommonsRequestLoggingFilter输出
直接修改过滤器日志格式,只输出需要的内容:
- 创建自定义过滤器子类,重写日志输出逻辑:
import org.springframework.web.filter.CommonsRequestLoggingFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; public class CustomRequestLoggingFilter extends CommonsRequestLoggingFilter { private static final Logger logger = LoggerFactory.getLogger(CustomRequestLoggingFilter.class); @Override protected void beforeRequest(HttpServletRequest request, String message) { // 仅输出请求方法和路径 logger.debug("REQUEST DATA : {} {}", request.getMethod(), request.getRequestURI()); } }
- 在Spring Boot配置类中注册自定义过滤器,替换默认实现:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public CustomRequestLoggingFilter customRequestLoggingFilter() { CustomRequestLoggingFilter filter = new CustomRequestLoggingFilter(); filter.setIncludeQueryString(false); // 不需要查询字符串则关闭 filter.setIncludePayload(false); // 不需要请求体则关闭 return filter; } }
3. Logback层面过滤修改日志
无需修改业务代码,通过Logback的TurboFilter处理日志内容:
- 在Logback配置中添加自定义TurboFilter:
<configuration> <!-- 原有配置保持不变 --> <turboFilter class="com.teaching.gradzee.filter.LogMessageFilter" /> </configuration>
- 实现日志过滤类,提取目标内容:
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.turbo.TurboFilter; import ch.qos.logback.core.spi.FilterReply; import org.slf4j.Marker; import java.util.regex.Matcher; import java.util.regex.Pattern; public class LogMessageFilter extends TurboFilter { private static final Pattern REQUEST_PATTERN = Pattern.compile("REQUEST DATA : (GET|POST|PUT|DELETE) ([^\\]]+)"); @Override public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format, Object[] params, Throwable t) { if (logger.getName().equals("org.springframework.web.filter.CommonsRequestLoggingFilter") && level == Level.DEBUG) { if (params != null && params.length > 0) { String originalMsg = params[0].toString(); Matcher matcher = REQUEST_PATTERN.matcher(originalMsg); if (matcher.find()) { // 替换为提取后的内容 params[0] = matcher.group(1) + " " + matcher.group(2); } else { // 匹配失败则过滤该日志 return FilterReply.DENY; } } } return FilterReply.NEUTRAL; } }
4. New Relic控制台配置解析规则
若上述方法无法实现,可在New Relic端处理:
- 登录New Relic控制台,找到对应日志数据源。
- 创建日志解析规则,使用正则表达式
REQUEST DATA : (GET|POST|PUT|DELETE) ([^\]]+)提取目标字段,将提取后的内容设为日志主消息,或存储为自定义属性。
内容的提问来源于stack exchange,提问作者Alpit Anand
相关产品推荐
相关产品推荐

