接口中直接获取HttpServletRequest打印日志

不改动接口参数和返回,打印接口ip/url/时间,之后加到拦截器或者AOP中

注意事项

  1. IPrequest.getRemoteAddr()获取的可能是代理服务器IP而非真实客户端IP,真实IP需要从X-Forwarded-For等header中获取。

  2. 线程安全RequestContextHolder是基于ThreadLocal实现的,在异步方法中可能无法获取到request

  3. 日志框架:建议使用SLF4J等日志框架替代System.out.println

  4. 异常处理:确保在catch块中也记录结束时间,否则会丢失异常请求的日志

方法1:在入参中添加HttpServletRequest

java 复制代码
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@RestController
@RequestMapping("/api")
public class YourController {

    @GetMapping("/your-endpoint")
    public ResponseEntity<?> yourMethod(HttpServletRequest request) {
        // 获取请求信息
        String ip = request.getRemoteAddr();
        String methodName = "yourMethod";
        Date startTime = new Date();
        
        System.out.println("请求开始 - IP: " + ip + ", 方法: " + methodName + ", 时间: " + startTime);
        
        try {
            // 你的业务逻辑代码
            // ...
            
            Date endTime = new Date();
            long duration = endTime.getTime() - startTime.getTime();
            System.out.println("请求结束 - 方法: " + methodName + ", 时间: " + endTime + ", 耗时: " + duration + "ms");
            
            return ResponseEntity.ok("Success");
        } catch (Exception e) {
            Date endTime = new Date();
            System.out.println("请求异常 - 方法: " + methodName + ", 时间: " + endTime);
            throw e;
        }
    }
}

方法2:使用RequestContextHolder获取Request

java 复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@RestController
@RequestMapping("/api")
public class YourController {

    @GetMapping("/your-endpoint")
    public ResponseEntity<?> yourMethod() {
        // 获取当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        
        String ip = request.getRemoteAddr();
        String methodName = "yourMethod";
        Date startTime = new Date();
        
        System.out.println("请求开始 - IP: " + ip + ", 方法: " + methodName + ", 时间: " + startTime);
        
        try {
            // 你的业务逻辑代码
            // ...
            
            Date endTime = new Date();
            long duration = endTime.getTime() - startTime.getTime();
            System.out.println("请求结束 - 方法: " + methodName + ", 时间: " + endTime + ", 耗时: " + duration + "ms");
            
            return ResponseEntity.ok("Success");
        } catch (Exception e) {
            Date endTime = new Date();
            System.out.println("请求异常 - 方法: " + methodName + ", 时间: " + endTime);
            throw e;
        }
    }
}


...
private String getClientIP(HttpServletRequest request) { 
    String ip = request.getHeader("X-Forwarded-For"); 
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) 
    { ip = request.getRemoteAddr();} 
    return ip.split(",")[0]; 
   // 处理多级代理的情况 }
 }

String apiName = request.getRequestURI(); 

方法3:方法2封装成工具类

日志工具类代码复用

java 复制代码
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class RequestLogUtil {
    
    public static void logRequestStart(String methodName) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getRemoteAddr();
        Date startTime = new Date();
        System.out.println("请求开始 - IP: " + ip + ", 方法: " + methodName + ", 时间: " + startTime);
    }
    
    public static void logRequestEnd(String methodName) {
        Date endTime = new Date();
        System.out.println("请求结束 - 方法: " + methodName + ", 时间: " + endTime);
    }
}

然后在控制器中使用:

java 复制代码
@GetMapping("/your-endpoint")
public ResponseEntity<?> yourMethod() {
    RequestLogUtil.logRequestStart("yourMethod");
    
    try {
        // 业务逻辑
        // ...
        
        RequestLogUtil.logRequestEnd("yourMethod");
        return ResponseEntity.ok("Success");
    } catch (Exception e) {
        RequestLogUtil.logRequestEnd("yourMethod");
        throw e;
    }
}

后期

方式一:拦截器(Interceptor)

1. 创建日志拦截器类
java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;

public class RequestLogInterceptor implements HandlerInterceptor {
    private static final Logger LOG = LoggerFactory.getLogger(RequestLogInterceptor.class);
    private static final ThreadLocal<Instant> startTimeThreadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        startTimeThreadLocal.set(Instant.now());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) {
        Instant startTime = startTimeThreadLocal.get();
        Instant endTime = Instant.now();
        long duration = Duration.between(startTime, endTime).toMillis();

        String clientIP = getClientIP(request);
        String apiName = request.getRequestURI();

        LOG.info("接口请求日志 - IP: {}, 接口: {}, 开始时间: {}, 结束时间: {}, 耗时: {}ms",
                clientIP, apiName, startTime, endTime, duration);

        startTimeThreadLocal.remove();
    }

    private String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
2. 注册拦截器
java 复制代码
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 WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestLogInterceptor())
                .addPathPatterns("/api/**"); // 拦截所有/api路径的请求
    }
}

方式二:AOP

1. 添加AOP依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建切面类
java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;

@Aspect
@Component
public class RequestLogAspect {
    private static final Logger LOG = LoggerFactory.getLogger(RequestLogAspect.class);

    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        
        Instant startTime = Instant.now();
        String clientIP = getClientIP(request);
        String apiName = request.getRequestURI();

        try {
            Object result = joinPoint.proceed(); // 执行目标方法
            return result;
        } finally {
            Instant endTime = Instant.now();
            long duration = endTime.toEpochMilli() - startTime.toEpochMilli();
            LOG.info("接口请求日志 - IP: {}, 接口: {}, 开始时间: {}, 结束时间: {}, 耗时: {}ms",
                    clientIP, apiName, startTime, endTime, duration);
        }
    }

    private String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.split(",")[0]; // 处理多级代理的情况
    }
}

说明

  1. 获取客户端IP

    • 优先从 X-Forwarded-For 头获取真实IP(适用于通过代理的情况)。
    • 直接使用 request.getRemoteAddr() 作为备用方案。
  2. 接口名称

    • 使用 request.getRequestURI() 获取请求路径(如 /api/user)。
    • 若需更精细的接口名(如方法名),可在AOP中通过 joinPoint.getSignature().getName() 获取。
  3. 耗时计算

    • 使用 Instant 记录时间戳,确保高精度。
    • 在拦截器的 afterCompletion 或AOP的 finally 块中计算总耗时。
  4. 线程安全

    • 拦截器中通过 ThreadLocal 存储开始时间,避免并发问题。

日志输出示例

复制代码
2023-10-05 14:20:00 INFO  RequestLogAspect - 接口请求日志 - IP: 192.168.1.100, 接口: /api/user, 开始时间: 2023-10-05T14:20:00.123Z, 结束时间: 2023-10-05T14:20:00.456Z, 耗时: 333ms

扩展建议

  • 异步日志 :若日志频繁,可使用异步Appender(如Logback的AsyncAppender)提升性能。

  • JSON格式 :输出为JSON便于ELK采集:

    java 复制代码
    LOG.info("{}", new ObjectMapper().writeValueAsString(logMap));
  • 自定义注解 :通过注解标记需要记录日志的接口:

    java 复制代码
    @Around("@annotation(com.example.YourLoggableAnnotation)")
相关推荐
北执南念5 分钟前
项目代码生成工具
java
中国lanwp11 分钟前
springboot logback 默认加载配置文件顺序
java·spring boot·logback
苹果酱056743 分钟前
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
java·vue.js·spring boot·mysql·课程设计
Java致死1 小时前
单例设计模式
java·单例模式·设计模式
胡子发芽1 小时前
请详细解释Java中的线程池(ThreadPoolExecutor)的工作原理,并说明如何自定义线程池的拒绝策略
java
沫夕残雪1 小时前
Tomcat的安装与配置
java·tomcat
胡子发芽1 小时前
请解释Java中的NIO(New I/O)与传统I/O的区别,并说明NIO中的关键组件及其作用
java
柚个朵朵2 小时前
IDEA中使用Git
java·git·spring
jerry6092 小时前
优先队列、堆笔记(算法第四版)
java·笔记·算法
666HZ6662 小时前
关于IDEA的循环依赖问题
java·ide·intellij-idea