Springboot2.x 全局异常+日志

前言

本文是蚯蚓在自己搭建一个通用的springboot后台管理系统框架,且边学习springboot边做的,若有不足之处,希望大家不吝留言,非常感谢。

架构要求:

1.使用全局异常处理,即控制层、业务层、持久层等分层中的方法均不使用try……catch对异常进行捕获处理,所有异常抛出,最终在请求结束后,统一处理。

2.日志需要记录完整的一次请求,包括请求参数:url、请求方式(如get、post等)、类路径、方法、方法参数名、方法入参值等,还有响应结果集、处理的时间等。当然还有一些请求的时间,响应的时间,异常的堆栈信息等等根据具体业务需要定制。

框架基于:

springboot2.x

aop(当要一次性记录整个请求时可用,也可以单独处理请求参数处理前、请求方法执行完之后等等,详见aop知识)

slf4j+logback(日志写入文件,可单独做全局异常记录,功能不如aop+slf4j+logback强大,但是简单的用于日常维护的异常排查还是可以的,但是一定要根据数据量进行存档,不然一个文件太大,打都打不开那就没意思了)

持久层JPA(与本文内容无关)

正文:

1.说明

本文中的aop和slf4j+logback两个是可以自行组合使用的

2.挂载

2.1AOP

maven的pom.xml中加入


    org.springframework.boot
    spring-boot-starter-aop

 

2.2slf4j+logback

maven的pom.xml中加入


    ch.qos.logback
    logback-classic


    org.slf4j
    jcl-over-slf4j

3.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
        
    
    
    
    
    
    	
    	
        
        
        
        
    

	
    
    
        
    

4.全局异常处理

这里描述的是简单的单独的全局异常处理(使用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));
	}
	
}

5.AOP全局异常处理和日志(重点)

这里直接指定切点为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();
        }
	}
}

 

你可能感兴趣的:(java,springboot)