🚀 Spring Boot 实现 API 请求自定义日志记录(含性能分析与日志分类)

699 阅读2分钟

在企业级系统中,记录每一次 API 调用的详细信息(包括请求耗时、参数、IP、异常等)是非常重要的,无论是为了问题排查性能优化还是审计追踪。本篇文章将手把手带你通过拦截器 + logback 自定义日志文件,实现高性能、高可读性的 API 访问日志记录。


✅ 实现目标

  • 每次 HTTP 请求都被记录日志
  • 记录内容包括:URL、方法、参数、请求 IP、耗时
  • 根据耗时分类:FAST、NORMAL、SERIOUS
  • 将日志写入专属日志文件 track.log,不影响主业务日志
  • 可支持 JSON 格式输出,方便 ELK/SLS 分析

🧱 步骤一:定义日志数据模型 MTrackLog

import lombok.Data;
import java.io.Serializable;

@Data
public class MTrackLog implements Serializable {

    private String url;
    private String method;
    private long start;
    private long end;
    private String remoteIp;
    private String parameter;

    @Override
    public String toString() {
        long cost = end - start;
        String level = cost >= 1500 ? "SERIOUS" : (cost > 300 ? "NORMAL" : "FAST");

        return String.format(
            "{"url":"%s", "method":"%s", "cost":%d, "level":"%s", "ip":"%s", "param":"%s"}",
            url, method, cost, level, remoteIp, parameter
        );
    }
}

✅ 可以根据实际需求加入 traceIduserId 等字段。


🔧 步骤二:自定义拦截器记录请求信息

@Component
public class RequestTrackInterceptor implements HandlerInterceptor {

    private static final Logger trackLogger = LoggerFactory.getLogger("TRACK_FILE_NAME");
    private static final String TRACK_LOG_KEY = "_trackLog";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        MTrackLog mTrackLog = new MTrackLog();
        mTrackLog.setStart(System.currentTimeMillis());
        request.setAttribute(TRACK_LOG_KEY, mTrackLog);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        Object obj = request.getAttribute(TRACK_LOG_KEY);
        if (obj instanceof MTrackLog mTrackLog) {
            mTrackLog.setEnd(System.currentTimeMillis());
            mTrackLog.setMethod(request.getMethod());
            mTrackLog.setUrl(request.getRequestURL().toString());
            mTrackLog.setRemoteIp(request.getRemoteAddr());

            String parameter;
            if ("GET".equalsIgnoreCase(request.getMethod())) {
                parameter = request.getQueryString();
            } else if (new StandardServletMultipartResolver().isMultipart(request)) {
                parameter = "multipart";
            } else {
                try {
                    parameter = new CustomHttpServletRequestWrapper(request).getBody();
                } catch (Exception e) {
                    parameter = "error_reading_body";
                }
            }
            mTrackLog.setParameter(parameter);

            trackLogger.info(mTrackLog.toString());
        }
    }
}

🧩 步骤三:注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private RequestTrackInterceptor requestTrackInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestTrackInterceptor).addPathPatterns("/**");
    }
}

📦 步骤四:logback-spring.xml 日志配置

<!-- 跟踪日志输出到独立文件 -->
<appender name="TRACK_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/track.%d{yyyyMMdd}.%i.log.gz</fileNamePattern>
        <maxFileSize>20MB</maxFileSize>
        <maxHistory>30</maxHistory>
        <totalSizeCap>1GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%msg%n</pattern> <!-- 输出格式为纯内容 JSON -->
    </encoder>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>INFO</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

<logger name="TRACK_FILE_NAME" level="INFO" additivity="false">
    <appender-ref ref="TRACK_FILE"/>
</logger>

📋 示例输出日志

{"url":"/api/user", "method":"GET", "cost":102, "level":"FAST", "ip":"192.168.1.101", "param":"id=1001"}
{"url":"/api/order", "method":"POST", "cost":1580, "level":"SERIOUS", "ip":"192.168.1.10", "param":"{"userId":10}"}

🧠 小结

这种方式通过 拦截器 + 自定义日志对象 + logback 多日志文件输出 的组合,可以实现一个:

  • 高性能
  • 高可读性
  • 易于追踪与分析

的 API 日志追踪机制,非常适合中大型项目使用,特别是微服务场景中每个服务需要独立输出访问日志时。


如果你想继续拓展,还可以考虑:

  • 加入 TraceId(链路追踪)
  • 整合 MDC 实现日志链路穿透
  • 配合 ELK/SLS 实现实时查询和报警

如果这篇文章对你有帮助,欢迎点赞、收藏、关注我获取更多 Spring Boot 实战技巧!