For the normal operation of the project, exception capture and record are also very important, which is convenient for us to troubleshoot and locate problems

Define abnormal

To facilitate locating exceptions, several user-defined exception classes are provided to facilitate locating exceptions.

The base class

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

Copy the code

ParameterException

public class ParameterException extends HttpException {
    public ParameterException(String code){
        this.code = code;
        this.httpStatusCode = 400; }}Copy the code

ServerErrorException

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

UnAuthenticatedException

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

ForbiddenException

public class ForbiddenException extends HttpException {
    public ForbiddenException(String code) {
        this.code = code;
        this.httpStatusCode = 403; }}Copy the code

NotFoundException

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

There are several exceptions defined that I use in my projects, and I can also define my own exceptions as needed.

Catch exceptions

Catching exceptions requires a annotation @ControllerAdvice, which is explained in detail in the documentation.

To do so, define an exception catching class

@ControllerAdvice
public class GlobalExceptionAdvice {}Copy the code

This class has already implemented the ability to catch global exceptions, adding the exceptions defined above below

@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); }}Copy the code

The @ExceptionHandler annotation indicates the type of exception caught by the method, which allows different handling in different exceptions.

Record abnormal

After we capture the exception, we should record it, which is convenient for us to track and solve the bug.

There are many different ways to record, such as to a database or log file. I used the second method.

Join the rely on

   <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>
Copy the code

Add a log configuration file

logback.xml


      
<configuration>

  <! Console appender, almost default configuration -->
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
      <encoder charset="UTF-8">
          <! -- Output log text format, same as other appenders -->
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>

  <! -- Info level appender -->
  <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <! -- Log write file name, can be relative directory, can be absolute directory, if the upper directory does not exist automatically created -->
      <file>./logs/info/log-stack.log</file>
      <! If true, the log is appended to the end of the file; If false, empty the existing file. The default is true -->
      <append>true</append>
      <! -- Log level filter, type only INFO level logs -->
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>INFO</level>
          <! -- The following two attributes indicate: accept print if level matches, reject print if level does not match -->
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
      </filter>
      <! -- The most commonly used scroll policy, it is based on the time to determine the scroll policy, is responsible for both the scroll and trigger scroll -->
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <! Select '%d' from 'yyyY-MM-dd';
          <fileNamePattern>./logs/info/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
          <! Keep logs for 14 days -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <! -- Define log output format -->
      <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>
          <! -- Keep logs for 7 days -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <! -- Define log output format -->
      <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>
          <! -- Keep logs for 7 days -->
          <maxHistory>30</maxHistory>
      </rollingPolicy>
      <! -- Define log output format -->
      <encoder charset="UTF-8">
          <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
          <charset>UTF-8</charset>
      </encoder>
  </appender>
  <! 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>
Copy the code

Written to the log

@ControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
    @ExceptionHandler(ParameterException.class)
    public ResponseEntity handleParameterException(ParameterException e) {
        log.error(e.getLocalizedMessage()); 
        returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getCode()); }}Copy the code

Perfect exception information

Exceptions in this article only define code, and specific exception information can be written in the configuration file or stored in the database. After an exception is captured, the corresponding description information can be found and returned to the caller to increase friendliness.

Perfect journaling

If any of the above exceptions occur, this is recorded in the log file

10:19:32. 024 [HTTP - nio - 8080 - exec - 2] ERROR C.G.E.D.A dvice. 41 - / GlobalExceptionAdvice by zeroCopy the code

It was discovered that the line number recorded was in the GlobalExceptionAdvice class and was not the actual location of the code.

You can do this if you want to record the actual location of the code

 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();
    }
Copy the code
 log.error(getExceptionDetail(e));
Copy the code

Choose your own way according to the actual situation

The complete code

Github

Gitee