Spring应用如何打印access日志和out日志(用于分析请求总共在服务耗费多长时间)

我们经常会被问到这样一个问题。你接口返回的好慢呀,能不能提升一下接口响应时间啊?这个时候我们就需要去分析,为什么慢,慢在哪。而这首先应该做的就是确定接口返回时间过长确实是在服务内消耗的时间。而不是我们将请求发给网关或者nignx,由nignx或者网关转发给我们服务的过程消耗时间。

这个时间我们就需要借助我们的acccess日志和out日志了。

1,借助tomcat实现access日志打印

首先说一点总所周知的内容,我们都知道tomcat是有access日志的,所以简单通过tomcat配置实现应该不难。大概的配置如下,都做了解释说明。

# 开启Tomcat的访问日志
server.tomcat.accesslog.enabled=true

# 指定日志文件的路径(例如:在当前项目根目录下生成名为 access.log 的日志文件)
# 指定Tomcat访问日志文件的目录。默认值:logs
server.tomcat.accesslog.directory=./
# 指定Tomcat访问日志文件的前缀。默认值:access_log
server.tomcat.accesslog.prefix=access
# 指定Tomcat访问日志文件的后缀。默认值:.log
server.tomcat.accesslog.suffix=.log

# 配置 Tomcat 访问日志文件名中日期部分的格式。默认值是 logback-ext,表示使用 Logback 默认的日期格式
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd

# 是用于配置 Tomcat 访问日志的格式模式
# 以下是一些常见的日志模式格式:
# %h:远程主机名
# %l:远程登录名
# %u:远程用户
# %t:日期和时间,使用 Common Log Format 的格式(例如:[10/Oct/2000:13:55:36 -0700])
# %r:请求的第一行
# %s:HTTP状态码
# %b:响应内容长度,以字节为单位(省略则表示 -)
# %{xxx}i:请求头中的指定字段
# %{xxx}o:响应头中的指定字段
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D

# Tomcat访问日志文件轮换时是否重命名旧日志文件
# 设置为 true,则旧的访问日志文件在轮换时会被重命名,文件名会添加一个时间戳或者序号作为后缀。
# 如果设置为 false,则旧的访问日志文件不会被重命名,而是直接覆盖或者删除。
server.tomcat.accesslog.rename-on-rotate=false

# 用于配置是否在Tomcat访问日志中记录请求的属性。默认为 false。
# 设置为 true,Tomcat访问日志将包含额外的请求属性信息,这些属性信息通常是通过Servlet容器或者应用程序添加到请求中的。
#  这些额外的属性可以包括HTTP请求的各种元数据,例如请求的方法、协议、URI等等
server.tomcat.accesslog.request-attributes-enabled=false

# Tomcat访问日志的轮换策略。设置为 true,Tomcat访问日志将在达到一定大小限制时进行轮换,以防止日志文件过大。
#  一旦达到了指定的大小限制,Tomcat会停止往当前日志文件中写入内容,并创建一个新的日志文件继续记录访问日志。
server.tomcat.accesslog.rotate=true

# 设置Tomcat访问日志文件的最大大小。可以指定大小,如 10MB 或 100KB,也可以使用 -1 表示不限制大小,默认为 -1。
server.tomcat.accesslog.max-file-size=10MB


# 设置HTTP POST请求的最大大小(以字节为单位)。默认值:2097152字节(即2MB)
# server.tomcat.max-http-post-size=

# 设置HTTP请求头的最大大小(以字节为单位)。默认值:0(表示不限制大小)
# server.tomcat.max-http-header-size=

# 设置URI编码,用于解析URL中的参数。默认值:UTF-8
# server.tomcat.uri-encoding=

# 设置Tomcat服务器的最大线程数,用于处理请求。默认值:200
# server.tomcat.max-threads=

# 设置客户端和服务器之间的连接超时时间(以毫秒为单位)。默认值:-1(表示不设置超时)
# server.tomcat.connection-timeout=

我们配置好了,然后兴致冲冲地去运行项目一看,发现咋没打印呢,我不是开启了tomcat日志了么?

在 Tomcat 中,访问日志是由 Tomcat 的 AccessLogValve 来处理的,而不是由 Logback。因此,如果已经在 Logback 中配置了日志输出,可能会影响到 Tomcat 的访问日志的输出。

这里可能需要做别的配置,我这里介绍几种方法。

2,借助拦截器interceptor实现

java 复制代码
package com.luojie.config.logconfig;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AccessLogInterceptor());
    }
}
java 复制代码
package com.luojie.config.logconfig;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class AccessLogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info("From Interceptor Request IN URL: {} - Method: {} -IP:{}", request.getRequestURL(), request.getMethod(), request.getRemoteAddr());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // Do nothing
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // Do nothing
        log.info("From Interceptor Request OUT URL: {} - Method: {} -IP:{}", request.getRequestURL(), request.getMethod(), request.getRemoteAddr());
    }
}

3,借助aop实现

java 复制代码
package com.luojie.config.logconfig;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Aspect // 表示该类是一个切面类
@Component // 将该类注册为一个bean放入IOC容器
@Slf4j
public class LogAspectConfig {

    @Autowired
    private HttpServletRequest request;

    @Pointcut(value = "execution(* com.luojie.controller.*.*(..))")
    public void pointcut() {

    }

    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName(); // 获取方法名
        Object[] args = joinPoint.getArgs();// 获取参数列表
//        log.info("前置通知: 开启调用,方法名:{}, 参数:{}", name, Arrays.toString(args));
        log.info(" From AOP Request In URL: {} - Method: {} -IP:{}", request.getRequestURL(), request.getMethod(), request.getRemoteAddr());
    }

    @After(value = "pointcut()")
    public void after(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName(); // 获取方法名
//        log.info("后置通知: 开启调用,方法名:{}", name);
        log.info(" From AOP Request Out URL: {} - Method: {} -IP:{}", request.getRequestURL(), request.getMethod(), request.getRemoteAddr());
    }

}

aop的使用不知道的可以详细查看Spring AOP(基本认识与使用)-CSDN博客

4,修改logback.xml文件(目的:所有进出日志统一保存到同一个文件且无其它日志)

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!-- 定义输出到控制台的日志记录器 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 设置日志输出格式 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<!--            <pattern>${ACCESS_LOG_PATTERN}</pattern>-->
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/mylog.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <!-- 日志文件名称格式,按日期滚动 -->
            <fileNamePattern>logs/mylog.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
            <!-- 最多保留的日志文件数量 -->
            <maxIndex>30</maxIndex>
            <minIndex>1</minIndex>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!-- 单个日志文件大小,例如: 千字节:10KB 或 10kb;
             兆字节:10MB 或 10mb;  以及10GB 或 10gb:-->
            <maxFileSize>10MB</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%date [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--打印数据库连接池日志信息-->
    <logger name="com.zaxxer.hikari" level="DEBUG"/>
    <!--打印sql信息-->
    <logger name="com.luojie.dao" level="DEBUG"/>
    <!--additivity="false" 确保特定类的日志不会传递给跟logger,避免重复输出-->
    <logger name="com.luojie.config.logconfig" level="INFO" additivity="false">
        <appender-ref ref="FILE"/>
    </logger>

    <!-- 设置根日志级别为 INFO -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/> <!-- 将日志输出到控制台 -->
    </root>

</configuration>

5,查看测试结果

希望对各位看官老爷有帮助,如果可以的话,能否请各位老爷点个赞,关注一下博主呢,在这里非常感谢各位老爷了。

相关推荐
我只会发热2 分钟前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
是老余3 分钟前
本地可运行,jar包运行错误【解决实例】:通过IDEA的maven package打包多模块项目
java·maven·intellij-idea·jar
crazy_wsp3 分钟前
IDEA怎么定位java类所用maven依赖版本及引用位置
java·maven·intellij-idea
.Ayang6 分钟前
tomcat 后台部署 war 包 getshell
java·计算机网络·安全·web安全·网络安全·tomcat·网络攻击模型
一直学习永不止步11 分钟前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
hummhumm25 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
chusheng184029 分钟前
Java项目-基于SpringBoot+vue的租房网站设计与实现
java·vue.js·spring boot·租房·租房网站
宁静@星空31 分钟前
006-自定义枚举注解
java·开发语言
hummhumm42 分钟前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
游走于计算机中摆烂的44 分钟前
启动前后端分离项目笔记
java·vue.js·笔记