SpringBoot切面记录Http接口的请求日志
本文知识点:
- 切面及注解使用
- 全局获取请求参数、参数值
- 自定义Jackson序列化器及使用
- 获取请求IP地址的方法
- StopWatch的使用
1. 引入切面pom依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
2. 自定义@Logs注解
package com.fay.common;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logs {
@AliasFor("msg")
String value() default "";
@AliasFor("value")
String msg() default "";
String logType() default "query";
String bizType() default "common";
}
3. 切面实现
package com.fay.common;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.StringJoiner;
@Slf4j
@Aspect
@Component
public class LogsAspect {
private final ObjectMapper objectMapper = new ObjectMapper();
@Pointcut("@annotation(com.fay.test.common.Logs)")
public void pointcut() {}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
this.writeRequest(point);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = point.proceed();
stopWatch.stop();
this.writeResponse(point, result, stopWatch.getTotalTimeMillis());
return result;
}
private void writeRequest(JoinPoint point) {
HttpServletRequest request = RequestUtils.getRequest();
if (Objects.isNull(request)) {
return;
}
String ip = RequestUtils.getIp(request);
String requestMethod = request.getMethod();
String requestUri = request.getRequestURI();
String clazzName = point.getTarget().getClass().getName();
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
String methodName = method.getName();
String requestParams = this.getRequestParams(point);
log.info("请求开始:{} -> {} {} 参数:{}", ip, requestMethod, requestUri, requestParams);
log.info("{}#{}", clazzName, methodName);
}
private void writeResponse(JoinPoint point, Object result, long time) {
String resultStr = this.serialize(result);
log.info("执行结果:{},执行耗时:{}ms", resultStr, time);
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Logs logs = method.getAnnotation(Logs.class);
if (Objects.nonNull(logs)) {
log.info("log:{}, {}, {}", logs.msg(), logs.logType(), logs.bizType());
}
}
private String getRequestParams(JoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
StringJoiner joiner = new StringJoiner("&");
String[] paramNames = signature.getParameterNames();
Object[] paramArgs = point.getArgs();
for (int i = 0; i < paramNames.length; i++) {
Object paramArg = paramArgs[i];
if (paramArg instanceof ServletRequest || paramArg instanceof ServletResponse || paramArg instanceof MultipartFile) {
continue;
}
String paramArgsStr = this.serialize(paramArg);
joiner.add(paramNames[i] + "=" + paramArgsStr);
}
return joiner.toString();
}
private String serialize(Object obj) {
if (obj instanceof ServletRequest || obj instanceof ServletResponse || obj instanceof MultipartFile) {
return obj.getClass().getSimpleName();
}
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(String.class, new LargeStringSerializer());
try {
return objectMapper.registerModule(simpleModule).writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return "";
}
private static class LargeStringSerializer extends JsonSerializer<String> {
private final int maxStrLen;
private final int liveStrLen;
public LargeStringSerializer() {
this(500, 0);
}
public LargeStringSerializer (int maxStrLen, int liveStrLen) {
this.maxStrLen = maxStrLen;
this.liveStrLen = liveStrLen;
}
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (s.length() > maxStrLen && s.length() > liveStrLen) {
s = s.substring(0, liveStrLen) + "...";
}
jsonGenerator.writeString(s);
}
}
}
请求工具类
package com.fay.test.common;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
public class RequestUtils {
private RequestUtils() {}
public static HttpServletRequest getRequest(){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.isNull(requestAttributes)) {
return null;
}
return requestAttributes.getRequest();
}
public static String getIp(HttpServletRequest request){
String ip = request.getHeader("x-forwarded-for");
String unknown = "unknown";
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
使用示例
@Log(msg = "打印", bizType = "biz")
@GetMapping("/print")
public String print(String id, @RequestParam(required = false) Integer age, @RequestParam("name") String name) {
String print = testConfigServe.print();
System.out.println("代码:" + print + ", " + id + ", " + ", " + age + name);
return print;
}
@Log(msg = "打印Post", bizType = "biz")
@PostMapping("/printPost")
public Map<String, Object> printPost(@RequestBody Map<String, Object> param, @RequestParam(required = false) Integer age, @RequestParam("title") String title) {
String jsonStr = JSONUtil.toJsonStr(param);
System.out.println("代码:" + jsonStr + (age + title));
param.put("title", title);
return param;
}
启动后请求测试
GET http:
Accept: application/json
###
POST http:
Content-Type: application/json
{
"id": 99,
"name": "abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234567890"
}