This article is a sub-chapter of the personal development framework for SpringBoot2.1. Please read the personal development framework for SpringBoot2.1

Back-end project address: personal application development framework for SpringBoot2.1

Front-end project address: yWH-vuE-admin

Log Customization

When we tested in the previous chapter, we found that the log output of the console was default, and many logs were not printed, and we could not customize the information we wanted to output. For an application program, logging is an essential part. Online problem tracking, log-based statistical analysis of business logic, and so on are all without logging.

There are a lot of reference materials for logs on the Internet. A more detailed introduction can be easily obtained. Here are a few references:

  • Description of logback configuration
  • Slf4j log component introduction and configuration details
  • This section describes common Java logging frameworks

Java has many common Logging frameworks, such as Log4j, Log4j 2, Commons Logging, Slf4j, Logback, and more.

Commons Logging and Slf4j are Logging surfaces that provide a unified high-level interface for a variety of Loging apis. Log4j and Logback are concrete logging implementations. It can be simply understood as interface and interface implementation. Callers only need to pay attention to the interface without paying attention to the specific implementation, so as to achieve decoupling.

Slf4j and Logback are commonly used in combination. Commons Logging and Log4j are used in combination. Based on the following advantages, Slf4j+Logback Logging framework is used:

For faster execution, Logback rewrote its internal implementation, improving performance by more than 10 times on some critical execution paths. Not only is logBack performing better, but the initial memory load is also smaller

Automatically clear old log archives, and you can control the maximum number of log archives by setting the maxHistory property of TimeBasedRollingPolicy or SizeAndTimeBasedFNATP

Logback has much richer filtering capabilities than Log4J and can record logs at a lower log level without lowering the log level.

Logback must be used with Slf4j. Since Logback and Slf4j are the same author, the compatibility is self-evident.

By default, Spring Boot logs logs using Logback and outputs them to the console at the INFO level.

To configure the log

We can set the format of the log by setting the yML file. We can also set the log management by setting the logback. XML file.

  • Here are some examples of all the YML file configurations

Yml: This method is simpler than XML because springBoot has default Settings without configuration. You can add the following configuration to the application-dev.yml development environment to take effect. When you deploy to liunx server, you need to use the application-prod production environment configuration file, and the configuration path in the file is liunx.

Logging: file: # Maximum number of days for storing logs max-history: 15 # Maximum size for storing logs max-size: 100MB # Path: E:\logs pattern: Output to console format console: "YWH -% d{YYYY-MM-DD HH: MM: SS} -%-4r [%t] %-5level %logger{36} -% MSG %n" root: info com.ywh.core: debug *************************************************<! -- %d{HH: mm: ss.sss} --
<! -- %thread [%t] -- the name of the process that prints logs. This is useful in Web applications and asynchronous task processing.
<! -- %-4r -- "-" represents left align the output from the time the program started to the time the log was created with a minimum width of 4 -->
<! -- %-5level -- log level, left-aligned with 5 characters -->
<! -- %logger{36} -- name of the logger -->
<! -- % MSG -- log message -->
<! -- %n -- platform newline -->

<! - more details may refer to: https://aub.iteye.com/blog/1103685 explanation of the bottom of this blogCopy the code

The following images from: aub.iteye.com/blog/110368…

XML: This method requires multiple tags to be configured, which is a bit more cumbersome than YML method. Create logback-spring. XML file in resources. You need to configure logging.config= to specify the location in the YML file

<?xml version="1.0" encoding="UTF-8"? >
<! -- Log levels are TRACE < DEBUG < INFO < WARN < ERROR < FATAL. If set to WARN, messages lower than WARN will not be printed -->
<! -- scan: When this property is set to true, the configuration document will be reloaded if it changes. The default value is true -->
<! -- scanPeriod: sets the interval for monitoring whether the configuration document has been modified. If no time unit is given, the default unit is milliseconds. This attribute takes effect when scan is true. The default interval is 1 minute. -->
<! -- debug:When the value is set to true, internal logback logs are displayed to view the running status of logback in real time. The default value is false. -->
<configuration  scan="true" scanPeriod="60 seconds">
    <contextName>Y-W-H</contextName>
    <! The value of name is the name of the variable, and the value of value is the value defined by the variable. The defined values are inserted into the Logger context. After definition, you can make "${}" to use variables. -->
    <property name="log.path" value="E:/logs/" />
 
    <! --0. Log format and color rendering -->
    <! -- Color log dependent render class -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <! -- Color log format -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName ) [%thread] %clr(${LOG_LEVEL_PATTERN:-%5p}) % the CLR (${PID: -}) {magenta} % CLR (-) {abbreviation} % CLR ([% 15.15 t]) {abbreviation} % CLR (% 40.40 logger {39}) CLR (:) {abbreviation} {cyan} % %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
 
 
    <! --1. Output to console -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <! The log appender is configured for development purposes only. The console outputs logs with a log level greater than or equal to this level.
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <! -- Set character set -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>. Omit the code, the specific code can be viewed at Github</configuration>
Copy the code

After either of these two methods are configured and the project is started, we will find that we have used our custom output format to output the log file in the path we specified.

Custom exceptions and global exception classes

When the log level is set to INFO, only logs above INFO, such as INFO, WARN, and ERROR, will be output. The problem is that the exception stack thrown by the program (runtime exceptions) will not be printed, which is not convenient for troubleshooting.

Also, in some cases, we want to throw an exception directly to the Controller in the Service without doing anything about it, but we can’t output the exception directly to the client, which is very unfriendly, and we want to precisely locate the error, which is up to us to define the output of the exception. And return the error exception to the front end in the same format as Result that we encapsulated earlier, so we need to define the custom exception and define the global exception class. We define the custom exception class first and then the global exception class.

According to the classification of exception information in the rookie tutorial, there are three types of exceptions

Checking exceptions: The most representative checking exceptions are those caused by user errors or problems that the programmer could not have foreseen. For example, when trying to open a nonexistent file, an exception occurs that cannot simply be ignored at compile time.

Runtime exceptions: Runtime exceptions are exceptions that can be avoided by programmers. In contrast to checking exceptions, runtime exceptions can be ignored at compile time.

Errors: Errors are not exceptions, but problems outside the programmer’s control. Errors are generally ignored in code. For example, when a stack overflow occurs, an error occurs that is not detected at compile time.

All we need to do is inherit run-time exceptions, customize such exceptions, and create MyException classes in the Exception package under Common that inherit RuntimeException.

package com.ywh.common.exception;
 
/** * CreateTime: 2018-11-21 19:07 * ClassName: MyXiyiException * Package: com.ywh.common.exception * Describe: * Custom exception, can throw their own exception class * *@author YWH
 */
public class MyException extends RuntimeException {
 
    public MyException(String msg) {
        super(msg);
    }
 
    public MyException(String message, Throwable throwable) {
        super(message, throwable);
    }
 
    public MyException(Throwable throwable) {
        super(throwable); }}Copy the code

Create MyExceptionUtil utility class in the Utils package under Common to quickly create exception classes

package com.ywh.common.utils;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ywh.common.exception.MyException;

/** * CreateTime: 2018-12-18 22:32 * ClassName: MyExceptionUtil * Package: com.ywh.common.utils * Describe: * exception tool class **@author YWH
 */
public class MyExceptionUtil {

    public MyExceptionUtil(a) {}public static MyException mxe(String msg, Throwable t, Object... params){
        return new MyException(StringUtils.format(msg, params),t);
    }

    public static MyException mxe(String msg, Object... params){
        return new MyException(StringUtils.format(msg, params));
    }

    public static MyException mxe(Throwable t){
        return newMyException(t); }}Copy the code

After creating the custom exception, we need to catch and handle the custom exception. This requires us to define the global exception class to catch and handle the custom exception. Add the GlobalExceptionHandler class to exception under Common.

package com.ywh.common.exception;
 
/ * * *@Author: YWH
 * @Description: global exception handling class, intercepting Controller RestControllerAdvice This annotation is ResponseBody and ControllerAdvice hybrid *@Date: Create in 17:16 2018/11/17
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
 
    /** ** Exceptions defined in the global exception class can be intercepted, but the trigger conditions are different, such as IO exceptions must be thrown to the * controller to be intercepted, or in the class try.. * Most of them can be intercepted without throwing an exception up, and return front-end JSON data, such as a number of subscripts out of bounds, 404, 500, 400, etc. * If you want to write, According to the following format to increase the exceptions * HttpMessageNotReadableException * /
 
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /** * after starting the application, be@ExceptionHandler,@InitBinder,@ModelAttributeAnnotation method, * will be used by@RequestMappingAnnotation method. *@param binder
     */
    @InitBinder
    public void initWebBinder(WebDataBinder binder){}/** * System error, unknown error tested *@paramEx Exception information *@returnReturn front-end exception information */
    @ExceptionHandler({Exception.class})
    public Result exception(Exception ex){
        log.error("Error details:" + ex.getMessage(),ex);
        returnResult.errorJson(BaseEnum.SYSTEM_ERROR.getMsg(),BaseEnum.SYSTEM_ERROR.getIndex()); }... Omit the code, please go to Github to see the specific code/** * Custom exception message interception *@paramEx Exception information *@returnReturn front-end exception information */
    @ExceptionHandler(MyException.class)
    public Result myCustomizeException(MyException ex){
        log.warn("Error details:" + ex);
        returnResult.errorJson(BaseEnum.CUSTOMIZE_EXCEPTION.getMsg(),BaseEnum.CUSTOMIZE_EXCEPTION.getIndex()); }}Copy the code

In GlobalExceptionHandler we intercept a lot of exceptions after custom processing, and put our above custom runtime exceptions to intercept, I have written comments on the method in the class, and according to the online information should be well understood, I have done tests on most exceptions, can intercept success.

Test the sample

We use postman to request a GET via POST, which returns our custom JSON format and tells us that the error is due to the interface type, so we can quickly locate the error and fix it.

All the above errors are caught by the system for us and intercepted by the global exception class, and then returned the custom JSON format. How to use our custom exception? We need to manually catch the exception and throw the exception, so that our global exception class can intercept the exception.

We define a method in ExampleServiceImpl and call this method in the Controller layer, calling this interface with Postman

    /** * Test custom exception *@returnReturns the string */
    @Override
    public String myException(a) {
        int i = 0;
        int a = 10;
        if( i > a){
            System.out.println("Test!!");
        }else{
            throw MyExceptionUtil.mxe("Mistake, smaller than him!!");
        }
        return "There was no interception. It failed.";
    }
Copy the code
    @Autowired
    private ExampleService exampleService;
    
    @GetMapping("myExceptionTest")
    public Result myExceptionTest(a){
        return Result.successJson(exampleService.myException());
    }
Copy the code

You can see that our custom exception is intercepted and the desired information is printed in the console.