接口中直接获取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)")
相关推荐
创码小奇客2 分钟前
保姆级 Talos 超参数优化实战指南:从入门到封神
java·后端·架构
青云交10 分钟前
Java 大视界 -- Java 大数据在智能安防入侵检测系统中的多源数据融合与误报率降低策略(369)
java·大数据·入侵检测·智能安防·多源数据融合·误报率降低·视频语义理解
我会冲击波22 分钟前
告别手写注释!变量命名、注释IDEA插件更新了,AI帮你搞定一切代码注释
java·intellij idea
BUG创建者33 分钟前
openlayer根据不同的状态显示不同的图层颜色
android·java·javascript
羊锦磊1 小时前
[java 常用类API] 新手小白的编程字典
java
都叫我大帅哥1 小时前
TOGAF技术架构阶段全解析:从理论到Java实战,避坑指南附赠!
java
星辰大海的精灵1 小时前
深入解析 CopyOnWriteArrayList
java·后端·算法
都叫我大帅哥1 小时前
Java ScopedValue:线程安全的"数据气泡"指南
java
Code季风1 小时前
深入 Spring IoC 容器:六大关键应用场景助力项目开发
java·spring
这些不会的1 小时前
iphone手机使用charles代理,chls.pro/ssl 后回车 提示浏览器打不开该网页
java