SpringCloud统一异常及日志处理(一)

  • 前言
  • 一、异常处理
  • 二、日志处理
  • 1.引入依赖
  • 2.配置log4j2
  • 总结


前言


  Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,简单来说,一个SpringCloud包含了N个SpringBoot。在各项服务之间相互分离为不同的jar包的时候,各服务内部异常和日志的统一处理将会显得尤为重要和困难,在这里笔者将利用自己的实际项目举例,记录当前项目中是如何进行以上所述操作的。   笔者在阅读较多类似文章后,总结出一套较为适合本项目的处理机制,各位看官酌情参考。如果各位有更好的解决思路,还请不吝赐教。


一、异常处理

由于我在系统中单独做了数据接口,所以以下将会由该服务举例,其他服务类似。

  1. 将每个服务中涉及到的所有的状态码单独划分为一个枚举体,BaseStatus。
public enum BaseStatus {
    SYSTEM_ERROR(5100, "系统错误"),
    PARAM_ERROR(5101, "参数错误"),
    INSERT_FAIL(5102,"新增失败"),
    DELETE_FAIL(5103,"删除失败"),
    UPDATE_FAIL(5104,"更新失败"),
    SELECT_FAIL(5105,"查询失败")
    ;

    private final Integer status;
    private final String message;

    public Integer getStatus() {
        return this.status;
    }

    public String getMessage() {
        return this.message;
    }

    BaseStatus(Integer status, String message) {
        this.status = status;
        this.message = message;
    }
}
  1. 对服务进行异常分类,这里我只展示基类,其他异常类extends BaseException
public class BaseException extends RuntimeException{
    private static final long serialVersionUID = -3872700496475775551L;
    
    private final BaseStatus baseStatus;
    private final String extMsg;

    public BaseException(BaseStatus baseStatus, String extMsg) {
        super(baseStatus.getMessage() + (extMsg == null ? "" : ('@' + extMsg)));
        this.baseStatus = baseStatus;
        this.extMsg = extMsg;
    }

    public BaseStatus getBaseStatus() {
        return this.baseStatus;
    }

    public String getExtMsg() {
        return extMsg;
    }
}
  1. 单独建立一个异常的处理类,针对不同的异常进行不同的处理。这里要添加@RestControllerAdvice的注解,所有控制器中的抛出异常将会由该类进行处理。 @ExceptionHandler注解将会用来选择哪些异常进入到哪些方法中,这里进行简单举例。
@ResponseBody
@RestControllerAdvice
public class ExceptionsHandler {

    @ExceptionHandler({UserException.class})
    public <T extends BaseException> ResponseEntity<JSONObject> warnException(T exception) {
        log.warn(exception);
        return new ResponseEntity<JSONObject>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler({Exception.class, Error.class})
    public <T extends Throwable> ResponseEntity<JSONObject> errorException(T exception) {
        log.error(exception);
        return new ResponseEntity<JSONObject>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

二、日志处理

1.引入依赖

maven依赖如下,这里使用了log4j2,使用log4j2时需要在其他依赖中排除:

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-log4j2</artifactId>
		<version>2.4.1</version>
</dependency>

2.配置log4j2

log4j2的具体配置请参考官方文档,这里需要重点提的是,我的日志选择了异步处理的方式,当系统抛出异常后,首先将会交由log4j2进行收集,然后将会将异常信息通过Socket发送到logstash,进行第二步的收集、解析、转换,之后将会通过管道发送到Elasticsearch,以达到日志的统一处理的目的。ELK stack将会在下一章进行详细说明,这里暂时先不赘述了:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Properties>
        <property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:-} [%15.15t] %-30.30C{1.} : %m%n</property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${LOG_PATTERN}" />
        </Console>
        <Socket name="logstash-tcp" host="10.159.xxx.xxx" port="8501" protocol="TCP">
            <PatternLayout pattern="${LOG_PATTERN}" />
        </Socket>
    </Appenders>
    <Loggers>
        <!-- 如果使用<asyncRoot> 或 <asyncLogger>,includeLocation="true"是必须要设置才会有类路径等一些信息打印出来 -->
        <AsyncLogger name="com.MultiTec.HikAc.OracleService" level="warn" includeLocation="true" >
            <appender-ref ref="logstash-tcp" />
        </AsyncLogger>

        <Root level="INFO">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

总结


以上就是今天要分享的内容,好的系统一定是多种优秀的工具相互搭配,互相成就,发挥出最好的效果来,作为程序员,不仅要对代码有足够高的敏感性,更要对不同的实现方式、工具的组合使用永远抱有充分的好奇心,如果您喜欢我的文章,请点赞收藏,如果您有更好的实现思路,请不吝赐教。