通常我们自定义一个Aspect用于记录请求日志,方便问题排查。主要记录了如下信息:
- 请求方法及请求地址
- 客户端IP地址
- 调用的控制器类及方法名
- 请求头信息
- 请求参数
- 处理耗时
代码实现
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@Slf4j
@Aspect
@Component
public class WebLogAspect {
private final String LOCALHOST_IPV4 = "127.0.0.1";
private final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
// 切入点
@Pointcut("execution(public * demo.controller..*(..)))")
public void pointCut() {
}
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 开始处理时间
long startTime = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 构建成一条长日志,避免并发下日志输出错乱
StringBuilder sb = new StringBuilder(300);
// 日志参数
List<Object> reqArgs = new ArrayList<>();
sb.append("\n================ Start ================\n");
// 打印路由
sb.append("===> {}: {}\n");
String requestMethod = request.getMethod();
reqArgs.add(requestMethod);
reqArgs.add(request.getRequestURL().toString());
// IP
sb.append("===> IP: {}\n");
reqArgs.add(getClientIp(request));
// 打印调用 controller 的全路径以及执行方法
sb.append("===> Class Method: {}.{}\n");
reqArgs.add(joinPoint.getSignature().getDeclaringTypeName());
reqArgs.add(joinPoint.getSignature().getName());
// 打印请求头
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
sb.append("=== Headers === {}: {}\n");
String headerName = headerNames.nextElement();
reqArgs.add(headerName);
reqArgs.add(StrUtil.join("", request.getHeader(headerName)));
}
// 打印请求入参
sb.append("===> request params: {}\n");
reqArgs.add(JSONUtil.toJsonStr(joinPoint.getArgs()));
// List<Object> args = Arrays.asList(joinPoint.getArgs());
// log.info("Request Args : {}", new Gson().toJson(args));
Object result = joinPoint.proceed();
// // 打印出参
// log.info("Response Args : {}", new Gson().toJson(result));
// // 打印处理耗时
sb.append(StrUtil.format("处理耗时: {} ms \n", System.currentTimeMillis() - startTime));
sb.append("================ End =================\n");
// 打印
log.info(sb.toString(), reqArgs.toArray());
return result;
}
// 获取客户端IP
public String getClientIp(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (StrUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (StrUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (StrUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (LOCALHOST_IPV4.equals(ipAddress) || LOCALHOST_IPV6.equals(ipAddress)) {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
ipAddress = inetAddress.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
if (!StrUtil.isEmpty(ipAddress)
&& ipAddress.length() > 15
&& ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
return ipAddress;
}
}