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