当前位置:   article > 正文

Java全局异常捕获处理

java全局异常捕获

为了项目的正常运行中,异常捕获,记录也是非常重要的,方便我们排查问题,定位问题

定义异常

为了方便定位异常,自定义了几种异常类,方便我们快速定位异常。

基类

public class HttpException extends RuntimeException {
    protected String code;
    protected Integer httpStatusCode = 500;
}

  • 1
  • 2
  • 3
  • 4
  • 5

ParameterException

public class ParameterException extends HttpException {
    public ParameterException(String code){
        this.code = code;
        this.httpStatusCode = 400;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

ServerErrorException

public class ServerErrorException extends HttpException {
    public ServerErrorException(String code) {
        this.code = code;
        this.httpStatusCode = 500;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

UnAuthenticatedException

public class UnAuthenticatedException extends HttpException{
    public UnAuthenticatedException(String code){
        this.code = code;
        this.httpStatusCode = 401;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ForbiddenException

public class ForbiddenException extends HttpException {
    public ForbiddenException(String code) {
        this.code = code;
        this.httpStatusCode = 403;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

NotFoundException

public class NotFoundException extends HttpException {
    public NotFoundException(String code){
        this.httpStatusCode = 404;
        this.code = code;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里定义了我在项目中常用的几种异常,也可根据实际情况定义自己所需的异常。

捕获异常

捕获异常需要用到一个注解@ControllerAdvice,关于它的详细解释可查看文档

使用方法如下,定义一个异常捕获类

@ControllerAdvice
public class GlobalExceptionAdvice {

}
  • 1
  • 2
  • 3
  • 4

这个类就已经实现了捕获全局异常的功能,下面在加上上面定义的几种异常

@ControllerAdvice
public class GlobalExceptionAdvice {
  @ExceptionHandler(UnAuthenticatedException.class)
  public ResponseEntity unAuthenticatedException(UnAuthenticatedException e) {
      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getCode());
  }


  @ExceptionHandler(ParameterException.class)
  public ResponseEntity handleParameterException(ParameterException e) {
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getCode());
  }

  @ExceptionHandler(ForbiddenException.class)
  public ResponseEntity handleForbiddenException(ForbiddenException e) {
      return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getCode());
  }

  @ExceptionHandler(NotFoundException.class)
  public ResponseEntity handleNotFoundException(NotFoundException e) {
      return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getCode());
  }

  @ExceptionHandler(RuntimeException.class)
  public ResponseEntity handleRunTimeException(RuntimeException e) {

      return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(500);
  }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

@ExceptionHandler注解表示该方法捕获的异常类型,就可以在不同的异常中进行不同的处理方式。

记录异常

捕获到异常之后我们要记录下来,方便我们对bug的追踪解决。

记录方法有多种多样的,比如记录到数据库或者log文件中。我使用了第二种方式。

加入依赖

   <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <optional>true</optional>
      </dependency>
     <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-api</artifactId>
          <version>2.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-core</artifactId>
          <version>2.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-jcl</artifactId>
          <version>2.2</version>
      </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

增加日志配置文件

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <!-- 控制台 appender, 几乎是默认的配置 -->
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
      <encoder charset="UTF-8">
          <!-- 输出的日志文本格式, 其他的 appender 与之相同 -->
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>

  <!-- info 级别的 appender -->
  <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <!-- 日志写入的文件名, 可以是相对目录, 也可以是绝对目录, 如果上级目录不存在会自动创建 -->
      <file>./logs/info/log-stack.log</file>
      <!-- 如果是 true, 日志被追加到文件结尾; 如果是 false, 清空现存文件. 默认是true -->
      <append>true</append>
      <!-- 日志级别过滤器, 只打 INFO 级别的日志-->
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>INFO</level>
          <!-- 下面2个属性表示: 匹配 level 的接受打印, 不匹配的拒绝打印 -->
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
      </filter>
      <!-- 最常用的滚动策略, 它根据时间来制定滚动策略, 既负责滚动也负责触发滚动 -->
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- 设置滚动文件规则, 如果直接使用 %d, 默认格式是 yyyy-MM-dd -->
          <fileNamePattern>./logs/info/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
          <!-- 保留14天的日志 -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <!-- 定义日志输出格式 -->
      <encoder charset="UTF-8">
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>

  <!-- error 级别的 appender -->
  <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>./logs/error/log-stack.log</file>
      <append>true</append>
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>ERROR</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
      </filter>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <fileNamePattern>./logs/error/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
          <!-- 保留7天的日志 -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <!-- 定义日志输出格式 -->
      <encoder charset="UTF-8">
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>
  <!-- error 级别的 appender -->
  <appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>./logs/debug/log-stack.log</file>
      <append>true</append>
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>DEBUG</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
      </filter>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <fileNamePattern>./logs/debug/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
          <!-- 保留7天的日志 -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <!-- 定义日志输出格式 -->
      <encoder charset="UTF-8">
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>
  <!-- 指定 com.github 下的日志打印级别, appender -->
  <logger name="com.github" level="debug" additivity="false">
      <appender-ref ref="stdout"/>
      <appender-ref ref="info"/>
      <appender-ref ref="error"/>
      <appender-ref ref="debug"/>

  </logger>


  <root level="info">
      <appender-ref ref="stdout"/>
      <appender-ref ref="info"/>
      <appender-ref ref="error"/>
  </root>

</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

写入日志

@ControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
    @ExceptionHandler(ParameterException.class)
    public ResponseEntity handleParameterException(ParameterException e) {
        log.error(e.getLocalizedMessage()); 
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getCode());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

完善异常信息

文章中的异常只定义了code,具体的异常信息可以写在配置文件中或者保存在数据库中,在捕获到异常之后,找到对应的描述信息返回调用者,增加友好度。

完善记录日志

以上如果发生了异常,在日志文件中是这样记录的

10:19:32.024 [http-nio-8080-exec-2] ERROR c.g.e.d.advice.GlobalExceptionAdvice 41 - / by zero
  • 1

发现记录的行号是在GlobalExceptionAdvice类中,并非是代码真实的位置。

如果要记录到代码的真实位置可以这样实现

 public  String getExceptionDetail(Exception e) {

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(e.getClass() + System.getProperty("line.separator"));
        stringBuilder.append(e.getLocalizedMessage() + System.getProperty("line.separator"));
        StackTraceElement[] arr = e.getStackTrace();
        for (int i = 0; i < arr.length; i++) {
            stringBuilder.append(arr[i].toString() + System.getProperty("line.separator"));

        }
        return stringBuilder.toString();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
 log.error(getExceptionDetail(e));
  • 1

根据实际情况选择适合自己的方式

完整代码

Github

Gitee

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/372854
推荐阅读
相关标签
  

闽ICP备14008679号