本文是蚯蚓在自己搭建一个通用的springboot后台管理系统框架,且边学习springboot边做的,若有不足之处,希望大家不吝留言,非常感谢。
1.使用全局异常处理,即控制层、业务层、持久层等分层中的方法均不使用try……catch对异常进行捕获处理,所有异常抛出,最终在请求结束后,统一处理。
2.日志需要记录完整的一次请求,包括请求参数:url、请求方式(如get、post等)、类路径、方法、方法参数名、方法入参值等,还有响应结果集、处理的时间等。当然还有一些请求的时间,响应的时间,异常的堆栈信息等等根据具体业务需要定制。
springboot2.x
aop(当要一次性记录整个请求时可用,也可以单独处理请求参数处理前、请求方法执行完之后等等,详见aop知识)
slf4j+logback(日志写入文件,可单独做全局异常记录,功能不如aop+slf4j+logback强大,但是简单的用于日常维护的异常排查还是可以的,但是一定要根据数据量进行存档,不然一个文件太大,打都打不开那就没意思了)
持久层JPA(与本文内容无关)
本文中的aop和slf4j+logback两个是可以自行组合使用的
maven的pom.xml中加入
org.springframework.boot
spring-boot-starter-aop
maven的pom.xml中加入
ch.qos.logback
logback-classic
org.slf4j
jcl-over-slf4j
在src/main/resources/下添加一个logback.xml的文件
这里就贴出来我的配置:信息、警告、异常按日期分离文件
default
%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
${LOG_HOME}/error-%d{yyyy-MM-dd}.log
60
%d{HH:mm:ss.SSS} [%thread] %-5level %logger-%line - %msg%n
ERROR
ACCEPT
DENY
${LOG_HOME}/info-%d{yyyy-MM-dd}.log
60
%d{HH:mm:ss.SSS} [%thread] %-5level %logger-%line - %msg%n
INFO
ACCEPT
DENY
${LOG_HOME}/warn-%d{yyyy-MM-dd}.log
60
%d{HH:mm:ss.SSS} [%thread] %-5level %logger-%line - %msg%n
WARN
ACCEPT
DENY
./my_log.log
./my_log%d{yyyyMMdd}.log.zip
60
UTF-8
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
10MB
这里描述的是简单的单独的全局异常处理(使用logback写入日志文件)
因为这个不是当前要用的,所以没有深入编码。
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 全局异常日志处理
*
*/
@RestControllerAdvice
public class WebExceptionHandler
{
private final static Logger logger = LoggerFactory.getLogger(WebExceptionHandler.class);
@ExceptionHandler
public RestResult defaultException(Exception e) {
// 记录异常堆栈信息
String strackTrace = ExceptionUtil.getStrackTrace(e);
logger.error("发生了未知异常", strackTrace);
printLog(e);
// 发送邮件通知技术人员
return RestResult.failure(ResultCode.SYSTEM_INNER_EXCEPTION);
}
private void printLog(Exception e)
{
//记录http请求
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
LoggerVo log = new LoggerVo();
log.setHttpMethod(request.getMethod());
log.setUrl(request.getRequestURL().toString());
log.setIp(request.getRemoteAddr());
// log.setLogType(AppConstants.LOG_TYPE_HTTP);
// log.setReqParams(JsonUtil.objectToJson(request.getParameterMap()));
// log.setRespParams(resp);
//logger.error(">>>"+JsonUtil.objectToJson(logEntity));
}
}
这里直接指定切点为controller包下的所有类。当然也可以自己写一个注解,然后在方法上添加这个注解,指定这个注解来处理。
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import 。。。.comm.util.ExceptionUtil;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
@Aspect
@Component
public class WebControllerAop
{
/**
* 指定切点
* 匹配 。。。.system.controller包及其子包下的所有类的所有方法
*/
@Pointcut("execution(public * 。。。.system.controller.*.*(..))")
public void webLog()
{
}
@Before(value = "webLog()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("我是前置通知!!!");
}
/**
* 处理完请求返回内容
* @param ret
* @throws Throwable
*/
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
System.out.println("方法的返回值 : " + ret);
}
/**
* 后置异常通知
* @param jp
*/
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("方法异常时执行.....");
}
@Around("webLog()")
public Object logHandler(ProceedingJoinPoint joinPoint) throws Throwable
{
long startTime=System.currentTimeMillis();
Object result= null;
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
// 记录下请求内容
String url = req.getRequestURL().toString();
String httpMethod = req.getMethod();
String ip = req.getRemoteAddr();
System.out.println("请求URL : " + url);
System.out.println("HTTP_METHOD : " + httpMethod);
System.out.println("IP : " + ip);
//获取目标方法的参数信息
Object[] obj = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
//AOP代理类的名字
String classPath = signature.getDeclaringTypeName();
System.out.println("类: "+classPath);
//代理的是哪一个方法
String methodName = signature.getName();
System.out.println("方法:"+ methodName);
String suffixM = classPath.replace("cn.com.startdima.", "");
String module = suffixM.substring(0, suffixM.indexOf("."));
System.out.println("模块: " + module);
MethodSignature methodSignature = (MethodSignature) signature;
// 入参
StringBuilder params = new StringBuilder();
// 获取方法参数名称
String[] paramNames = methodSignature.getParameterNames();
Object[] paramValues = joinPoint.getArgs();
int length = paramNames.length;
for(int i=0; i");
}
long costTime;
try
{
// 执行controller方法
result = joinPoint.proceed();
}
catch (Throwable throwable)
{
String strackTrace = ExceptionUtil.getStrackTrace(throwable);
String exception = throwable.getClass()+":"+throwable.getMessage();
costTime=System.currentTimeMillis()-startTime;
//log.error("请求时间:{},请求耗时:{},请求类名:{},请求方法:{},请求参数:{},请求结果:{}",startTime,costTime,className,methodName,params.toString(),exception);
System.out.println("出现异常");
}
costTime=System.currentTimeMillis()-startTime;
// 存入数据库或日志文件
return result;
}
}
最后附带一个异常堆栈记录的工具类:
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 异常工具类
* @author qiuyin
*
*/
public class ExceptionUtil
{
/**
* 获取异常堆栈信息
* @param throwable
* @return
*/
public static String getStrackTrace(Throwable throwable)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try
{
throwable.printStackTrace(pw);
return sw.toString();
} finally
{
pw.close();
}
}
}