springboot全局异常与日志

日志

在resources文件夹中创建logback-spring.xml文件这个会在你项目的平级目录创建一个Logs文件夹,根据时间进行区分,并包含错误日志和控制台打印日志




    
    
    

    
    
        
            
             [host:%ip]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
        
    
    
    
        
            ${log.path}/%d{yyyy-MM-dd}/info.%d{yyyy-MM-dd}.%i.log
            
            1GB
            
            10
            
            
            
            true
        
        
             [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
            
        
        
    
    
    
        
            ${log.path}/%d{yyyy-MM-dd}/error.%d{yyyy-MM-dd}.%i.log
            
            1GB
            
            10
            
            
            
            true
        
        
             [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 
        
        
            ERROR
        
    

    
    
        
    
    
        
        
        
    
    
    
    

    
    
        true
    

日志中ip

import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class LogIpConfig extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent event) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return "127.0.0.1";
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String remoteAddr = "";
        if (request != null) {
            remoteAddr = request.getHeader("X-Forwarded-For");
            if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
                remoteAddr = request.getHeader("X-Real-IP");
            }
            if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
                remoteAddr = request.getRemoteAddr();
            }
        }
        return remoteAddr;
    }
}

在application.yml文件中引入

logging:
  config: classpath:logback-spring.xml

全局捕获异常

springboot中通过GlobalExceptionHandler类来实现。通过全局捕获异常可以减少trycatch的使用并且可以通过枚举创建状态码和信息。

通过下方几个文件实现

BaseErrorInfoInterface.java文件

public interface BaseErrorInfoInterface {
    /**
     * 错误码
     */
    int getResultCode();

    /**
     * 错误描述
     */
    String getResultMsg();
}

 BusinessException.java文件

public class BusinessException extends RuntimeException  {
    private static final long serialVersionUID = -4879677283847539655L;

    private int errorCode;

    private String errorMessage;

    private String exceptionMessage;

    private Exception exception;

    public BusinessException(String errorMessage) {
        super();
        this.errorMessage = errorMessage;
    }

    public BusinessException(int errorCode, String errorMessage) {
        super();
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    public BusinessException(int errorCode, String errorMessage, Exception exception) {
        super();
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.exception = exception;
    }

    public BusinessException(String errorMessage, String exceptionMessage) {
        super();
        this.exceptionMessage = exceptionMessage;
        this.errorMessage = errorMessage;
    }

    public String getExceptionMessage() {
        return exceptionMessage;
    }

    public void setExceptionMessage(String exceptionMessage) {
        this.exceptionMessage = exceptionMessage;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public Exception getException() {
        return exception;
    }

    public void setException(Exception exception) {
        this.exception = exception;
    }

    public BusinessException(int errorCode, String errorMessage, String exceptionMessage) {
        super();
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.exceptionMessage = exceptionMessage;
    }
}

 CommonEnum.java文件

public enum CommonEnum implements BaseErrorInfoInterface {
    // 数据操作错误定义
    SUCCESS(200, "成功!"),
    BODY_NOT_MATCH(400,"请求的数据格式不符!"),
    SIGNATURE_NOT_MATCH(401,"请求的数字签名不匹配!"),
    NOT_FOUND(404, "未找到该资源!"),
    INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
    SERVER_BUSY(503,"服务器正忙,请稍后再试!"),
    CLIENT_ABORT_ERROR(600, "客户端中断连接")
    ;

    /** 错误码 */
    private int resultCode;

    /** 错误描述 */
    private String resultMsg;

    CommonEnum(int resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }

    @Override
    public int getResultCode() {
        return resultCode;
    }

    @Override
    public String getResultMsg() {
        return resultMsg;
    }

}

 ErrController.java文件

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ErrController implements ErrorController {

    public String getErrorPath() {
        // TODO Auto-generated method stub
        return "/error";
    }

    @RequestMapping("/error")
    public boolean handlerError() {
        throw new BusinessException(-1,"页面不存在");
    }

}

 GlobalExceptionHandler.java文件

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理自定义的业务异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public Result bizExceptionHandler(HttpServletRequest req, BusinessException e){
        StackTraceElement[] stackTrace = e.getStackTrace();
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement stackTraceElement : stackTrace) {
            sb.append(stackTraceElement.toString()).append("\n");
        }
        logger.error("未知异常:"+e.getErrorMessage()+"!原因是:"+e);
        logger.error("未知异常!错误信息是:"+sb.toString());
        return Result.error(e.getErrorCode(),e.getErrorMessage());
    }

    /**
     * 处理空指针的异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public Result exceptionHandler(HttpServletRequest req, NullPointerException e){
        StackTraceElement[] stackTrace = e.getStackTrace();
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement stackTraceElement : stackTrace) {
            sb.append(stackTraceElement.toString()).append("\n");
        }
        logger.error("未知异常!原因是:"+e);
        logger.error("未知异常!错误信息是:"+sb.toString());
        return Result.error(CommonEnum.BODY_NOT_MATCH);
    }

    /**
     * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public Result exceptionHandler(HttpServletRequest req, Exception e){
        StackTraceElement[] stackTrace = e.getStackTrace();
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement stackTraceElement : stackTrace) {
            sb.append(stackTraceElement.toString()).append("\n");
        }
        logger.error("未知异常!原因是:"+e);
        logger.error("未知异常!错误信息是:"+sb.toString());
        return Result.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }

    /**
     * 处理 ClientAbortException 异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = ClientAbortException.class)
    @ResponseBody
    public Result clientAbortExceptionHandler(HttpServletRequest req, ClientAbortException e){
        logger.warn("客户端中断连接!异常信息是:" + e.getMessage());
        return Result.error(CommonEnum.CLIENT_ABORT_ERROR);
    }

}

注意:clientAbortExceptionHandler 用于处理 ClientAbortException 异常。该方法记录了警告级别的日志,而不是错误级别,因为这种异常通常表示客户端主动断开连接,这种情况不一定是服务器端的错误

 MyCustomErrorAttributes.java文件

import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.util.Map;

@Component
public class MyCustomErrorAttributes extends DefaultErrorAttributes  {
    @Override
    public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map errorAttributes = super.getErrorAttributes(webRequest, options);
        return errorAttributes;
    }
}

Result.java文件

import com.alibaba.fastjson.JSONObject;

public class Result {
    /**
     * 响应代码
     */
    private int code;

    /**
     * 响应消息
     */
    private String message;

    /**
     * 响应结果
     */
    private Object result;

    public Result() {
    }

    public Result(BaseErrorInfoInterface errorInfo) {
        this.code = errorInfo.getResultCode();
        this.message = errorInfo.getResultMsg();
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    /**
     * 成功
     *
     * @return
     */
    public static Result success() {
        return success(null);
    }

    /**
     * 成功
     * @param data
     * @return
     */
    public static Result success(Object data) {
        Result rb = new Result();
        rb.setCode(CommonEnum.SUCCESS.getResultCode());
        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        rb.setResult(data);
        return rb;
    }

    /**
     * 失败
     */
    public static Result error(BaseErrorInfoInterface errorInfo) {
        Result rb = new Result();
        rb.setCode(errorInfo.getResultCode());
        rb.setMessage(errorInfo.getResultMsg());
        rb.setResult(null);
        return rb;
    }

    /**
     * 失败
     */
    public static Result error(int code, String message) {
        Result rb = new Result();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    /**
     * 失败
     */
    public static Result error(String message) {
        Result rb = new Result();
        rb.setCode(-1);
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }

}

注意:如果使用了全局捕获异常但是你的代码中还是有异常警告

解决办法1:在全局捕获异常中在使用try-catch在包一次这个代码,但是当try-catch捕获异常之后如果不做处理的话全局异常并不会识别到有异常就不会打印错误日志。所以可以将异常上抛或在catch中进行日志打印操作。

public class ExceptionExample {

    private static final Logger logger = Logger.getLogger(ExceptionExample.class.getName());

    public String test() throws Exception {
        try{
        //代码块
        }
        catch(catch (Exception e){
            throw new Exception(e);//上抛错误
            logger.error("错误信息");//直接打印日志
        }
    }
}

解决办法2:使用@SneakyThrows注解。这个注解是 Lombok 库中的一个注解,它可以帮助简化在 Java 中处理受检查异常(checked exception)的代码。在使用 @SneakyThrows 注解后,编译器会自动为标记的方法中的受检查异常生成 try-catch 代码,将异常转换为未经检查的异常(unchecked exception)抛出,但是可能会出现一些隐藏的异常处理问题,所以谨慎使用。

public class ExceptionExample {
    @SneakyThrows
    public String test(){
        
        //代码块
       
    }
}

你可能感兴趣的:(spring,boot,java,后端)