概述
之前Jfinal转用Springboot后就特别不习惯springmvc不提供默认的请求日志打印。于是当时基于AOP仿照Jfinal的打印方式写了一个springboot版的。最近肖老板也写了一个基于Springmvc拦截器版的。于是都记录一下,供参考。
jfinal请求日志打印效果图:
这种请求日志无论是对于开发和生产中定位问题都是极为方便和重要的,我们python服务也是有这种日志打印,复杂的调用逻辑也应标识出下级调用的URL。
基于AOP实现
获取方法行数是借鉴肖老板的实现。
package cn.yr.clock.aop;
/**
* @title LogRecordAspect
* @description 切面 打印请求、返回参数信息
* @author Lang 1102076808@qq.com
* @date 2020/7/11 0:25
*/
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.NotFoundException;
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.beans.factory.annotation.Value;
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.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
/**
* 仿JFinal请求日志打印
*
* @author Lang 1102076808@qq.com
* @date 2020/7/20 22:59
*/
@Aspect
@Component
@Slf4j
public class LogRecordAspect {
private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
protected static boolean locateMethodLineNumber = true;
private static ClassPool pool = ClassPool.getDefault();
@Value("${spring.profiles.active}")
private String active;
@Pointcut("execution(* cn.yr..*.controller.*Controller.*(..))")
public void executionService() {
}
@Around("executionService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
/*if (!"dev".equals(active)) {
return pjp.proceed();
}*/
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.isNull(sra)) {
log.error("请求异常");
return null;
}
HttpServletRequest request = sra.getRequest();
String method = request.getMethod();
String uri = request.getRequestURI();
String paraString = JSONUtil.toJsonStr(request.getParameterMap());
Integer line = getLineNumber(pjp.getSignature().getName(), pjp.getTarget().getClass().getName());
StringBuffer buffer = new StringBuffer("\n南风怡人 request report ---------")
.append((sdf.get()).format(new Date())).append(" --------------------------\n");
buffer.append("Url : ").append(method).append(" ").append(uri).append("\n");
buffer.append("Controller : ").append(pjp.getSignature().getDeclaringTypeName())
.append(".(").append(pjp.getTarget().getClass().getSimpleName()).append(".java:").append(line).append(")\n");
buffer.append("Method : ").append(pjp.getSignature().getName()).append("\n");
buffer.append("UrlPara : ").append(paraString).append("\n");
buffer.append("--------------------------------------------------------------------------------\n");
System.out.println(buffer);
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
String sbResponse = "\n返回值: " +
pjp.getTarget().getClass().getSimpleName() + " --- " +
pjp.getSignature().getName() + ": " +
JSONUtil.toJsonStr(result) + "\n";
System.out.println(sbResponse);
return result;
}
private int getLineNumber(String methodName, String className) {
int lineNumber = 1;
if (locateMethodLineNumber) {
try {
lineNumber = pool.get(className).getDeclaredMethod(methodName).getMethodInfo().getLineNumber(0);
} catch (NotFoundException e) {
}
}
return lineNumber;
}
}
基于拦截器实现
package cn.yr.clock.interceptor;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.NotFoundException;
import org.springframework.boot.SpringBootVersion;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
/**
* <p>Title: ActionReportInterceptor</p>
* <p>Description: controller请求信息打印拦截器</p>
*
* @author sliver
* @email 18142611739@163.com
* @date 2021-11-28 11:10
*/
@Component
public class ActionReportInterceptor implements HandlerInterceptor {
protected static final String title = "\nSpringBoot-" + SpringBootVersion.getVersion() + " action report -------- ";
protected static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
protected static int maxOutputLengthOfParaValue = 512;
protected static boolean reportAfterInvocation = true;
/**
* 定位方法所在行数,依赖于java assist
*/
protected static boolean locateMethodLineNumber = true;
private static String[] DEFAULT_INCLUDE_PATH = new String[]{"/**"};
private static String[] DEFAULT_EXCLUDE_PATH = new String[]{"/**/*.css", "/**/*.js", "/**/*.html", "/**/*.jpg", "/**/*.png", "/**/*.ico,/**/*.woff2"};
private static ClassPool pool = ClassPool.getDefault();
public String[] getIncludePath() {
return DEFAULT_INCLUDE_PATH;
}
public String[] getExcludePath() {
return DEFAULT_EXCLUDE_PATH;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!reportAfterInvocation && handler instanceof HandlerMethod) {
report(request, response, (HandlerMethod) handler);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (reportAfterInvocation && handler instanceof HandlerMethod) {
report(request, response, (HandlerMethod) handler);
}
}
public void report(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" --------------------------\n");
sb.append("Url : ").append(request.getMethod()).append(" ").append(request.getRequestURI()).append("\n");
Class<?> cc = handler.getBeanType();
int lineNumber = getLineNumber(handler, cc);
sb.append("Controller : ").append(cc.getName()).append(".(").append(cc.getSimpleName()).append(".java:" + lineNumber + ")");
sb.append("\nMethod : ").append(handler.getMethod().getName()).append("\n");
Enumeration<String> e = request.getParameterNames();
if (e.hasMoreElements()) {
sb.append("Parameter : ");
while (e.hasMoreElements()) {
String name = e.nextElement();
String[] values = request.getParameterValues(name);
if (values.length == 1) {
sb.append(name).append("=");
if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) {
sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("...");
} else {
sb.append(values[0]);
}
} else {
sb.append(name).append("[]={");
for (int i = 0; i < values.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(values[i]);
}
sb.append("}");
}
sb.append(" ");
}
sb.append("\n");
}
sb.append("--------------------------------------------------------------------------------\n");
System.out.println(sb.toString());
}
private int getLineNumber(HandlerMethod handler, Class<?> cc) {
int lineNumber = 1;
if (locateMethodLineNumber) {
try {
lineNumber = pool.get(cc.getName()).getDeclaredMethod(handler.getMethod().getName()).getMethodInfo().getLineNumber(0);
} catch (NotFoundException e) {
}
}
return lineNumber;
}
}
最后
最后怎样实现怎么像Jfinal一样显示拦截器路径就交给方老爷实现了@方老爷。
评论