Log framework system sorting (basic)

0. Preface

  • The original intention of this blog is for specific people. The original text can be found on the Internet. Therefore: this blog is only guaranteed to be understood by specific people. At the same time, there are no detailed descriptions, no running screenshots, and no special remarks in this blog...

1,JUL

  • Refers to the Java Util Logging package, which is a native java logging framework. It is not necessary to refer to third-party class libraries when using it. Compared with other frameworks, it is easy to use and easy to learn. It is mainly used in small applications.


1.1. Composition of JUL

  • Logger: Known as a logger, the application publishes log information by obtaining the Logger object and using its API. Logger is usually considered as the entry program to access the log system.

  • Handler: Handler, each Logger will be associated with one or a group of Handlers, the Logger will hand over the log to the associated Handler for processing, and the Handler is responsible for recording the log. Handler specifically implements the output location of the log, such as outputting to the console or to a file, etc.

  • Filter: Filter, customize which information will be recorded and which information will be skipped according to your needs.

  • Formatter: The formatting component, which is responsible for converting and formatting the data and information in the log, so it determines the final form of our output log.

  • Level: The output level of the log, each log message has an associated level. We use the output level settings to display the final log information presented. According to different needs, to set different levels.



1.2, Getting Started

  • You can go directly to the JDK documentation to view the java.util.logging package


public class JULTest {

    @Test
    public void test01(){
        /*
            Log entry program java.util.logging.Logger
         */
        // Logger object creation method, not directly new object
        // To get the method parameters of the object, you need to introduce the full path string of the current class (currently we use this first, and then there will be a Logger parent-child relationship according to the package structure, which will be introduced in detail later)
        Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");

        /*
            For the output of the log, there are two ways

            The first way:
                Directly call the method related to the log level, and pass the log output information in the method
                Suppose now we want to output info-level log information
         */
        //logger.info("Output info 1");
        /*
            The second way:
                Call the general log method, and then use the Level type to define the level parameters of the log and the parameters that match the log output information.
         */
        //logger.log(Level.INFO,"output info information 2");


        /*
            output student information
                Name
                age
         */
        /*String name = "zs";
        int age = 23;
        logger.log(Level.INFO,"Student's name is: "+name+"; age is: "+age);*/

        /*
            For the output message, there are many disadvantages of string splicing
            1.trouble
            2.The program is inefficient
            3.not very readable
            4.High maintenance cost

            We should use a way of dynamically generating data, producing logs
            We use placeholders to operate
         */
        String name = "zs";
        int age = 23;
        logger.log(Level.INFO,"Student's name:{0},age:{1}",new Object[]{name,age});
    }
}


1.3, log level

    @Test
    public void test02(){

        /*
            The level of the log (viewed through the source code, very simple)

              SEVERE : error --- the highest log level
              WARNING : warn
              INFO : (default level) message
              			Source code: logger Getlogger() ---- > demandlogger ----- > getlogmanager() ---- > ensurelagmanagerinitialized() ---- > 350 lines or so has a defaultlevel attribute
              CONFIG : configure
              FINE : Details (less)
              FINER : Details (middle)
              FINEST : verbose (multiple) --- lowest log level

            two special levels
               OFF Can be used to turn off logging
               ALL Enable logging of all messages

            For the log level, the focus is on the second parameter of the new object, which is a value (in the source code)
            OFF Integer.MAX_VALUE Integer maximum value
            SEVERE 1000
            WARNING 900
            ...
            ...
            FINEST 300
            ALL Integer.MIN_VALUE Integer minimum value

            The significance of this value is that if the set log level is INFO -- 800
            Then the final displayed log information must be all log information with a value greater than 800
            The final show is
            SEVERE
            WARNING
            INFO
         */

        Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");

        /*
            By printing the results, we see that only the info level and log information higher than the info level are output
            Log information lower than info level is not output
            It proves the log information of the info level, which is the default log level of the system
            Based on the default log level info, print information with a higher level than it
         */

        /*
            If you just set the log level by the following form
            then it won't work
            In the future, it needs to be set together with the processor handler to take effect.
         */
        logger.setLevel(Level.CONFIG);

        logger.severe("severe information");
        logger.warning("warning information");
        logger.info("info information");
        logger.config("config information");
        logger.fine("fine information");
        logger.finer("finer information");
        logger.finest("finest information");

    }



1.4, custom log level

1.4.1. Output information on the console

    @Test
    public void test03(){

        /*
            custom log level
         */
        // logger
        Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");

        /*
            Turn off the default log printing method
            If the parameter is set to false, the method of printing the log will not operate according to the default method of the parent logger
        */
        logger.setUseParentHandlers(false);

        /*
            Handler
            The console log processor is used here, and the processor object is obtained
        */
        ConsoleHandler handler = new ConsoleHandler();
        // Create a log formatting component object
        SimpleFormatter formatter = new SimpleFormatter();

        // Set the output format in the processor
        handler.setFormatter(formatter);
        // Add a handler to the logger
        logger.addHandler(handler);

        /* 
            Set the print level of the log
            Here, the levels of the logger and processor must be set uniformly to achieve the effect of displaying the corresponding level of the log.
            logger.setLevel(Level.CONFIG);
            handler.setLevel(Level.CONFIG);
        */

        // After setting the following all level, all the output information below will be printed to the console, and you can change it to the corresponding log level when needed
        logger.setLevel(Level.ALL);
        handler.setLevel(Level.ALL);

        logger.severe("severe information");
        logger.warning("warning information");
        logger.info("info information");
        logger.config("config information");
        logger.fine("fine information");
        logger.finer("finer information");
        logger.finest("finest information");

    }



1.4.2, output information on disk

    @Test
    public void test04() throws IOException {

        /*
            Output the log to a specific disk file
            Doing so is equivalent to doing a persistent log operation
         */
        Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");
        logger.setUseParentHandlers(false);

        // file log handler
        FileHandler handler = new FileHandler("D:\\test\\" + this.getClass().getSimpleName() + ".log");
        SimpleFormatter formatter = new SimpleFormatter();
        handler.setFormatter(formatter);
        logger.addHandler(handler);

        // It is also possible to print to the console and to a file at the same time
        ConsoleHandler handler2 = new ConsoleHandler();
        handler2.setFormatter(formatter);
        // Multiple processors can be added to the logger at the same time, so that the log information of the corresponding level can be output in the disk and the console
        logger.addHandler(handler2);

        logger.setLevel(Level.ALL);
        handler.setLevel(Level.ALL);
        handler2.setLevel(Level.CONFIG);

        logger.severe("severe information");
        logger.warning("warning information");
        logger.info("info information");
        logger.config("config information");
        logger.fine("fine information");
        logger.finer("finer information");
        logger.finest("finest information");


        /*
            Summarize:
                Users use Logger to record logs, and Logger can hold multiple Handler s
                (Logger is used for log recording, and Handler is used for log output)
                Which handler objects are added, it is equivalent to needing to add the handler according to the
                Output the log to the specified location, such as console, file..
         */
    }



1.5. The parent-child relationship of logger s

    @Test
    public void test05(){

        /*
            Logger parent-child relationship
                JUL There is a "parent-child" relationship between the Logger s
                It is worth noting that this parent-child relationship is not the inheritance relationship between classes as we generally think
                Relationships are stored in a tree-like structure
         */



        /*
            From the two logger objects created below it seems
            We can consider logger1 to be the father of logger2
         */

        /* 
            The father is RootLogger, and the name defaults to an empty string
            RootLogger can be called the top-level logger of all logger objects
        */
        // this is the father
        Logger logger1 = Logger.getLogger("cn.zixieqing.jul.test");

        // This is the child, even cn.zixieqing.jul is the parent of this logger2
        Logger logger2 = Logger.getLogger("cn.zixieqing.jul.test.JULTest");

        //System.out.println(logger2.getParent()==logger1); //true

        System.out.println("logger1 father Logger quoted as:"
                +logger1.getParent()+"; name is"+logger1.getName()+"; father's name is"+logger1.getParent().getName());


        System.out.println("logger2 father Logger quoted as:"
                +logger2.getParent()+"; name is"+logger2.getName()+"; father's name is"+logger2.getParent().getName());


        /*
            The settings made by the father can also affect the son at the same time
            Make log printing related settings for logger1, and then we use logger2 to print the log
         */
        // father making settings
        logger1.setUseParentHandlers(false);
        ConsoleHandler handler = new ConsoleHandler();
        SimpleFormatter formatter = new SimpleFormatter();
        handler.setFormatter(formatter);
        logger1.addHandler(handler);
        handler.setLevel(Level.ALL);
        logger1.setLevel(Level.ALL);

        // The son prints - the result is: the son logger2 is not configured, but the father logger1 does, so the output level of the son logger2 is the father's level
        logger2.severe("severe information");
        logger2.warning("warning information");
        logger2.info("info information");
        logger2.config("config information");
        logger2.fine("fine information");
        logger2.finer("finer information");
        logger2.finest("finest information");
    }


Summary: look at the results of the source code

    // JUL will create a top-level RootLogger as the parent Logger of all Loggers during initialization, java.util.logging.LogManager$RootLogger, the default name is an empty string

    // View source code Logger.getLogger() --->demandLogger -----> getLogManager() -----> ensureLogManagerInitialized()--->350 Lines left and right:
    owner.rootLogger = owner.new RootLogger();
    RootLogger Yes LogManager inner class of

	/*
		The above RootLogger object exists as the root node of the tree structure
        In the future, the custom parent-child relationship will be associated through the path
        The parent-child relationship is also the mount relationship between nodes
	*/

    // About 350 rows
    owner.addLogger(owner.rootLogger);
    addLogger ----> LoggerContext cx = getUserContext(); 
    /* 
		LoggerContext One is used to save the node of Map relationship, weakhashmap<object, loggercontext > contextsmap
		Click weakhashmap<object, loggercontext>of loggercontext, and the structure is: hashtable<string, loggerweakref> namedloggers 
	*/

	// Click hashtable<string, LoggerWeakRef> LoggerWeakRef of namedloggers to get the following information like: 
    private String                name;       // for namedLoggers cleanup, this thing is the node, that is to say, the parent-child relationship is the node mount relationship
    private LogNode               node;       // for loggerRef cleanup
    private WeakReference<Logger> parentRef;  // for kids cleanup



1.6, using configuration files

1.6.1, the location of the default configuration file

  • Check source code
	Logger.getLogger() --->demandLogger -----> getLogManager() -----> ensureLogManagerInitialized()--->345 Line left and right: 

	There is this line of code: owner.readPrimordialConfiguration();

	click readPrimordialConfiguration(),There is a sentence around line 340 readConfiguration();

	click readConfiguration();Around line 1290, there is the following code
	    String fname = System.getProperty("java.util.logging.config.file");
        if (fname == null) {
            fname = System.getProperty("java.home");
            if (fname == null) {
                throw new Error("Can't find java.home ??");
            }
            File f = new File(fname, "lib");
            f = new File(f, "logging.properties");
            fname = f.getCanonicalPath();
        }


Conclusion: where the default configuration file is located

  • java. Home (i.e. the directory where JDK is installed) -- turn up JRE folder -- > lib -- > logging.properties

Analyze the logging.properties file in the above directory, find and open this file in this directory

# The default handler used by RootManager
# If you want to configure multiple processors, you can separate them with commas, such as: java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler

# RootManager's default log level, which is the global log level
# If you do not manually configure the level, then use INFO and higher levels for output by default
.level= INFO

# file processor settings
# path to log file
# 	  %h/java%u.log h refers to the user directory - regardless of window or linux
#	  java%u.log is the generated file name, where: u is equivalent to self-increment, starting from 0
#	  				Such as: java0.log, java1.log, java2.log... How many copies are generated here is determined by the configuration of java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.pattern = %h/java%u.log
# Log file limit - default 50000 bytes
java.util.logging.FileHandler.limit = 50000
# Number of log files - default 1
java.util.logging.FileHandler.count = 1
# The format of the log file, the default XML format, or SimpleFormatter
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Console processor settings
# Console default level
java.util.logging.ConsoleHandler.level = INFO
# Console default output format
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# This is an example given in this source document, which means that the log level can be set to a specific package like the following
# com.xyz.foo.level = SEVERE



1.6.2. Modify to a more friendly configuration file

# Custom Logger
cn.zixieqing.handlers=java.util.logging.FileHandler
# Custom Logger log level
cn.zixieqing.level=SERVER
# Block the log settings of the parent logger, which is equivalent to the previous play logger.setUseParentHandlers(false);
cn.zixieqing.useParentHandlers=false

# The default handler used by RootManager
# If you want to configure multiple processors, you can separate them with commas, such as: java.util.logging.ConsoleHandler,java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler

# RootManager's default log level, which is the global log level
.level= SERVER

# file processor settings
# path to log file
# 	  %h/java%u.log h refers to the user directory - regardless of window or linux
#	  java%u.log is the generated file name, where: u is equivalent to self-increment, starting from 0
#	  				Such as: java0.log, java1.log, java2.log... How many copies are generated here is determined by the configuration of java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.pattern = %h/java%u.log
# Log file limit - 50000 bytes
java.util.logging.FileHandler.limit = 50000
# number of log files
java.util.logging.FileHandler.count = 1
# Format of the log file
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

# Console processor settings
# Console default level
java.util.logging.ConsoleHandler.level = SERVER
# Console default output format
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# The next generated log file defaults to overwriting the previous one - append to the original log instead
java.util.logging.FileHandler.append=true



1.6.3. Use a custom configuration file

    @Test
    public void test06() throws Exception {

        // Load a custom configuration file
        InputStream input = new FileInputStream("D:\\test\\logging.properties");

        // Get the log manager object
        LogManager logManager = LogManager.getLogManager();

        // Read custom configuration files
        logManager.readConfiguration(input);

        Logger logger = Logger.getLogger("cn.zixieqing.jul.test.JULTest");

        logger.severe("severe information");
        logger.warning("warning information");
        logger.info("info information");
        logger.config("config information");
        logger.fine("fine information");
        logger.finer("finer information");
        logger.finest("finest information");
    }



1.7. Summary: JUL principle

  • 1. Initialize LogManager

    • LogManager loads logging.properties configuration file
    • Add Logger to LogManager
  • 2. Get the Logger from the LogManager of the singleton, namely: LogManager.getLogManager();

  • 3. Set the log level Level, and use the LogRecord class of log records in the printing process. The source code is as follows:

    • 1,click logger.severe("severe information");middle severe,Of course click other warning,info,config You can also go in, and you will see the following code later
      	    public void severe(String msg) {
              	log(Level.SEVERE, msg);
          	}
      
      2,click above log(),see the following code
      	    public void log(Level level, String msg) {
                  if (!isLoggable(level)) {
                      return;
                  }
      			// here is the destination
                  LogRecord lr = new LogRecord(level, msg);
                  doLog(lr);
              }
      
      
  • 4. Filter as a filter provides more fine-grained control beyond the log level

  • 5. Handler log processor, which determines the output location of the log, such as console, file...

  • 6. Formatter is used to format the output



2,LOG4J

  • Full name: log for java

2.1, the composition of LOG4J

  • Loggers: Control the output and output level of the log (JUL does the log level Level)
  • Appenders (output controller): Specify how the log is output (output to console, file, etc.)
  • Layout (log formatter): Controls the output format of log information


2.1.1, Loggers Logger

  • Responsible for collecting and processing log records, the name of the instance is the fully qualified name of the class, such as: cn.zixieqing. Test, and at the same time: the name of logger is case sensitive, and its naming has an inheritance mechanism (same as the parent child relationship in Jul, with the package path); In addition: root logger Yes the setting of the day log attribute made by the root parent of all loggers will directly affect the children
    // Obtaining the root logger
    Logger.getRootLogger();


log level

  • DEBUG
  • INFO
  • WARN
  • ERROR
  • ........

  • Size relationship: error > warn > info > debug

  • Output rules: The rules for outputting logs are: only output log information whose level is not lower than the set level
    • For example, if the Loggers level is set to INFO, the log information of INFO, WARN, and ERROR levels will be output, while DEBUG with a level lower than INFO will not be output.


2.1.2, Appenders output controller

  • Allows to output logs to different places, such as console (Console), files (Files), etc., can generate new files according to time or file size, can be sent to other places in the form of streams, etc.

  • Common Appenders output controller types:
    • ConsoleAppender outputs logs to the console
    • FileAppender outputs logs to a file
    • DailyRollingFileAppender output to a new file according to the specified time, output the log to a log file
    • According to the specified file size, RollingFileAppender will automatically rename the file when the file size reaches the specified size, generate a new file, and output the log information to a log file
    • JDBCAppender saves log information to the database


2.2. Play LOG4J

2.2.1. Getting Started

  • rely
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>


public class Log4jTest01 {

    @Test
    public void test01(){

        // Load initial configuration
        BasicConfigurator.configure();
        // Note: This logger is under apache, and the JUL in the previous play is java.  Under the util.logging package
        Logger logger = Logger.getLogger(Log4jTest01.class);

        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }
}
  • Pay attention to loading initialization information: basicconfigurator configure();, If you don't add this sentence of code, you will report an error - you don't add Appenders output controller, and the reason for not reporting an error is that there is such a code rootmanager in the source code addAppenders( XxxxAppender( PatternLayout layout ) ), the configure source code is as follows:
    public static void configure() {
        Logger root = Logger.getRootLogger();
        root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));
    }


LOG4J log level

  • Log4j provides 8 levels of log output, which are as follows:
    • ALL lowest level to turn on logging at all levels
    • The trace information under the advancement of the TRACE program. The log level of this trace information is very low and is generally not used.
    • DEBUG points out that fine-grained information events are very helpful for debugging applications, mainly in cooperation with development, printing some important running information during the development process, and in the case of no settings, the default log output level
    • Coarse-grained level operational information for INFO messages
    • WARN means a warning, a hidden error that may occur during the operation of the program
      • Note that some messages are not errors, but the purpose of this level of output is to give the programmer a hint
    • ERROR The error message of the system, the error does not affect the operation of the system
      • In general, if you don't want to output too many logs, you can use this level
    • FATAL stands for fatal error, it is the type of fatal error that makes it impossible for the system to continue operating once it occurs
      • If this level of error occurs, the program can stop running
    • OFF The highest level, the user turns off all logging


2.2.2, analyze the source code

        BasicConfigurator.configure();

        Logger logger = Logger.getLogger(Log4jTest01.class);

        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");



Analysis BasicConfigurator.configure(); click configure()

  • The resulting code is as follows
    public static void configure() {
        Logger root = Logger.getRootLogger();
        root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));
    }

  • Several pieces of information can be obtained from this:

    • 1. Create the root node object Logger root = Logger.getRootLogger();
    • 2. A ConsoleAppender object is added to the root node (representing the default print to the console, and the custom formatted output PatternLayout)
  • So what about custom configuration files to implement the functions of the above source code? Through the above source code analysis, we need to have the following conditions:

    • Our configuration file needs to provide the three component information of Logger, Appender, and Layout.


Analysis Logger logger = Logger.getLogger(Log4jTest01.class); click getLogger()

    public static Logger getLogger(Class clazz) {
        return LogManager.getLogger(clazz.getName());
    }

  • Found: LogManager.getLogger(clazz.getName());, where: LogManagerj is the log manager


View LogManager

  • First see the following information, a bunch of constants
    public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
    static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
    /** @deprecated */
    public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
    /** @deprecated */
    public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
    /** @deprecated */
    public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
    private static Object guard = null;
    private static RepositorySelector repositorySelector;

  • These things represent configuration files in different forms (different suffixes), of which the log4j.properties property is the most commonly used because it has a simple syntax and is easy to use


Load timing of log4j.properties
load - that's static
Observe the code in LogManager and find the static code block static in it

  • Found the following bunch of source code
    static {
        Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
        repositorySelector = new DefaultRepositorySelector(h);
        String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);
        if (override != null && !"false".equalsIgnoreCase(override)) {
            LogLog.debug("Default initialization of overridden by log4j.defaultInitOverrideproperty.");
        } else {
            String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);
            String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);
            URL url = null;
            if (configurationOptionStr == null) {
                url = Loader.getResource("log4j.xml");
                if (url == null) {
                    // The front is a bunch of judgments about the file format, here is what the log4j.propertie format does
                    url = Loader.getResource("log4j.properties");
                }
            } else {
                try {
                    url = new URL(configurationOptionStr);
                } catch (MalformedURLException var7) {
                    url = Loader.getResource(configurationOptionStr);
                }
            }

            if (url != null) {
                LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");

                try {
                    // Here is another piece of information: selectAndConfigure() translates to selecting a configuration file
                    OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
                } catch (NoClassDefFoundError var6) {
                    LogLog.warn("Error during default initialization", var6);
                }
            } else {
                LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
            }
        }
    }

  • From the source code, found url = Loader.getResource("log4j.properties");

    • The information obtained from this code: the system defaults to find log4j.properties from the current classpath, and if it is a maven project, then: it should be found in the resources path
  • Also found in the above source code OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());



View selectAndConfigure()

  • Found the following source code
    public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
        Configurator configurator = null;
        String filename = url.getFile();
        if (clazz == null && filename != null && filename.endsWith(".xml")) {
            clazz = "org.apache.log4j.xml.DOMConfigurator";
        }

        if (clazz != null) {
            LogLog.debug("Preferred configurator class: " + clazz);
            configurator = (Configurator)instantiateByClassName(clazz, Configurator.class, (Object)null);
            if (configurator == null) {
                LogLog.error("Could not instantiate configurator [" + clazz + "].");
                return;
            }
        } else {
            // Useful information is here, i.e. new PropertyConfigurator(); creates a properties configuration object
            configurator = new PropertyConfigurator();
        }

        ((Configurator)configurator).doConfigure(url, hierarchy);
    }



View PropertyConfigurator class

  • The first thing you see is the following constant information
    static final String CATEGORY_PREFIX = "log4j.category.";
    static final String LOGGER_PREFIX = "log4j.logger.";
    static final String FACTORY_PREFIX = "log4j.factory";
    static final String ADDITIVITY_PREFIX = "log4j.additivity.";
    static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
	// This is an important message
    static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
	// This is also an important message
    static final String APPENDER_PREFIX = "log4j.appender.";
    static final String RENDERER_PREFIX = "log4j.renderer.";
    static final String THRESHOLD_PREFIX = "log4j.threshold";
    private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
    private static final String LOGGER_REF = "logger-ref";
    private static final String ROOT_REF = "root-ref";
    private static final String APPENDER_REF_TAG = "appender-ref";
    public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
    private static final String RESET_KEY = "log4j.reset";
    private static final String INTERNAL_ROOT_NAME = "root";

  • Through the previous foundation, from this source code, it is found that there are two pieces of information to be configured
static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
static final String APPENDER_PREFIX = "log4j.appender.";

  • So how are these two configured?


Look for the appender configuration method in static final String APPENDER_PREFIX = "log4j.appender."
Search for appender s directly on the current source page

  • Found the following source code
    Appender parseAppender(Properties props, String appenderName) {
        Appender appender = this.registryGet(appenderName);
        if (appender != null) {
            LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
            return appender;
        } else {
            // The important information is here, here is one thing: the log4j.appender. found above is configured in the following way
            String prefix = "log4j.appender." + appenderName;
            // This is also important information, how the layout log format is configured
            String layoutPrefix = prefix + ".layout";
            appender = (Appender)OptionConverter.instantiateByKey(props, prefix, Appender.class, (Object)null);
            if (appender == null) {
                LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
                return null;
            } else {
                appender.setName(appenderName);
                if (appender instanceof OptionHandler) {
                    if (appender.requiresLayout()) {
                        Layout layout = (Layout)OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, (Object)null);
                        if (layout != null) {
                            appender.setLayout(layout);
                            LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
                            PropertySetter.setProperties(layout, props, layoutPrefix + ".");
                            LogLog.debug("End of parsing for \"" + appenderName + "\".");
                        }
                    }

                    String errorHandlerPrefix = prefix + ".errorhandler";
                    String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
                    if (errorHandlerClass != null) {
                        ErrorHandler eh = (ErrorHandler)OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, (Object)null);
                        if (eh != null) {
                            appender.setErrorHandler(eh);
                            LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
                            this.parseErrorHandler(eh, errorHandlerPrefix, props, this.repository);
                            Properties edited = new Properties();
                            String[] keys = new String[]{errorHandlerPrefix + "." + "root-ref", errorHandlerPrefix + "." + "logger-ref", errorHandlerPrefix + "." + "appender-ref"};
                            Iterator iter = props.entrySet().iterator();

                            while(true) {
                                if (!iter.hasNext()) {
                                    PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
                                    LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
                                    break;
                                }

                                Entry entry = (Entry)iter.next();

                                int i;
                                for(i = 0; i < keys.length && !keys[i].equals(entry.getKey()); ++i) {
                                }

                                if (i == keys.length) {
                                    edited.put(entry.getKey(), entry.getValue());
                                }
                            }
                        }
                    }

                    PropertySetter.setProperties(appender, props, prefix + ".");
                    LogLog.debug("Parsed \"" + appenderName + "\" options.");
                }

                this.parseAppenderFilters(props, appenderName, appender);
                this.registryPut(appender);
                return appender;
            }
        }
    }

  • Through the above source code, find the way to configure log4j.appender.: log4j.appender.+appenderName

    • Among them: appenderName is the output controller name
  • Then: deduce a configuration item appender output method in the log4j.properties configuration file as: log4j.appender.+appenderName=a certain output controller name

    • Among them: the output controller name has been touched at the beginning

  • Therefore, an example of the appender output configuration method of Log4j.properties is log4j.appender.console=org.apache.log4j.ConsoleAppender
  • In the same way, through the second sentence of code String layoutPrefix = prefix + ".layout";, we also know how to configure the layout output format

  • Example of layout log output format configuration: log4j.appender.console.layout=org.log4j.SimpleLayout


A small summary: the configuration method of appender output controller and layout log output format in log4j.properties configuration file

    log4j.appender,console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout=org.log4j.SimpleLayout



Continue to find the rootLogger configuration method in the second configuration static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger"
Search via log4j.rootLogge

  • find the following method
    void configureRootCategory(Properties props, LoggerRepository hierarchy) {
        String effectiveFrefix = "log4j.rootLogger";
        String value = OptionConverter.findAndSubst("log4j.rootLogger", props);
        if (value == null) {
            value = OptionConverter.findAndSubst("log4j.rootCategory", props);
            effectiveFrefix = "log4j.rootCategory";
        }

        if (value == null) {
            LogLog.debug("Could not find root logger information. Is this OK?");
        } else {
            Logger root = hierarchy.getRootLogger();
            synchronized(root) {
                // This is implemented here
                this.parseCategory(props, root, effectiveFrefix, "root", value);
            }
        }

    }



see parseCategory()

  • Found the desired configuration here
    void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
        LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
        // The configuration method is here. The meaning of this operation is: it means to cut the string with commas, which proves that the value of log4j.rootLogger can have multiple values, which are separated by commas.
        StringTokenizer st = new StringTokenizer(value, ",");
        if (!value.startsWith(",") && !value.equals("")) {
            if (!st.hasMoreTokens()) {
                return;
            }

            // After the string is cut by commas, the purpose of the first value is here - levelStr, level, that is: the first value after cutting is the level of the log
            String levelStr = st.nextToken();
            LogLog.debug("Level token is [" + levelStr + "].");
            if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {
                logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));
            } else if (loggerName.equals("root")) {
                LogLog.warn("The root logger cannot be set to null.");
            } else {
                logger.setLevel((Level)null);
            }

            LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
        }

        logger.removeAllAppenders();

        // The first value after the string is cut is the level log level, and the purpose of the remaining values ​​is here
        while(st.hasMoreTokens()) {
            // Through this code, we know: the 2nd - nth value is the other information we configure, this information is appenderName
            String appenderName = st.nextToken().trim();
            if (appenderName != null && !appenderName.equals(",")) {
                LogLog.debug("Parsing appender named \"" + appenderName + "\".");
                Appender appender = this.parseAppender(props, appenderName);
                if (appender != null) {
                    logger.addAppender(appender);
                }
            }
        }

    }

  • Through the above code analysis, it is known that the configuration method of log4j.rootLogger is: log4j.rootLogger=log level, appenderName1,appenderName2,appenderName3....
    • Indicates a way to configure multiple log outputs on the root node at the same time


2,2,3, the most basic log4j.properties configuration

  • After the previous source code analysis, it is concluded that BasicConfigurator.configure(); The properties of the alternative are configured as follows:
# rootLogger root configuration for all logger s - log4j.rootLogger=loglevel, appenderName1,appenderName2,appenderName3....
# The example here does not use the log output path, this log output path will be added later
log4j.rootLogger=debug,console
# appender output controller configuration log4j.appender.+appenderName=a certain output type - an example using the Console console
log4j.appender.console=org.apache.log4j.ConsoleAppender
# The output format configuration log4j.appender.+appenderName+layout=a layout format type
log4j.appender.console.layout=org.apache.log4j.SimpleLayout


test

        // Just comment out this sentence, the configuration of this code is replaced by the above custom configuration
        //BasicConfigurator.configure();

        Logger logger = Logger.getLogger(Log4jTest01.class);

        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");



2.2.3. Open log output details

    @Test
    public void test03(){

        /*
            Via switch in Logger
                Turn on log output for details
                View the method getLoggerRepository() in the LogManager class
                Find the code LogLog.debug(msg, ex);
                LogLog Will use debug level output to show us the log output details
                Logger Is the log of the recording system, then LogLog is the log used to record the Logger

                Enter the LogLog.debug(msg, ex); method
                Through code: if (debuginabled &! Quietmode) 
                Observe that both switches in the if judgment must be turned on.
                !quietMode It is the state that has been started, and we do not need to worry about it
                debugEnabled Default is off
                So we just need to set debugEnabled to true
         */
        // enable debugEnabled
        LogLog.setInternalDebugging(true);

        Logger logger = Logger.getLogger(Log4jTest01.class);

        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }

  • If debugEnabled is turned on, the output information is as follows (the following is just an example)
0 [main] FATAL cn.zixieqing.HotelJavaApplicationTests  - fatal information
1 [main] ERROR cn.zixieqing.HotelJavaApplicationTests  - error information
1 [main] WARN cn.zixieqing.HotelJavaApplicationTests  - warn information
1 [main] INFO cn.zixieqing.HotelJavaApplicationTests  - info information
1 [main] DEBUG cn.zixieqing.HotelJavaApplicationTests  - debug information

  • After opening, the information will be more complete
log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2.
log4j: Trying to find [log4j.properties] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader.
log4j: Trying to find [log4j.properties] using ClassLoader.getSystemResource().
log4j: Could not find resource: [null].
0 [main] FATAL cn.zixieqing.HotelJavaApplicationTests  - fatal information
0 [main] ERROR cn.zixieqing.HotelJavaApplicationTests  - error information
1 [main] WARN cn.zixieqing.HotelJavaApplicationTests  - warn information
1 [main] INFO cn.zixieqing.HotelJavaApplicationTests  - info information
1 [main] DEBUG cn.zixieqing.HotelJavaApplicationTests  - debug information



2.2.4, custom output format patternLayout

  • The custom configuration is just Layout, and the custom is the PatternLayout to play. This class has a setConversionPattern() method, see the source code of this method
    public void setConversionPattern(String conversionPattern) {
        this.pattern = conversionPattern;
        this.head = this.createPatternParser(conversionPattern).parse();
    }

  • It is found from this that the String conversionPattern needs to be configured. Therefore, add the conversionPattern property configuration to the log4j.properties configuration file. Of course, this property configuration follows a certain writing method, which is written as follows:
    %m Output the log information specified in the code
    %p output priority, and DEBUG,INFO Wait
    %n newline ( Windows The newline for the platform is "\n",Unix Platform is "\n")
    %r output from the application startup to the output of the log The number of milliseconds spent by the message
    %c Print the full name of the class to which the print statement belongs
    %t Output the full name of the thread that generated the log
    %d The current time of the output server, the default is ISO8601,Formats can also be specified, such as:%d{yyyy year MM moon dd day HH:mm:ss}
    %l Output where the log time occurred, including the class name, thread, and number of lines in the code. like: Test.main(Test.java:10)
    %F The name of the file where the output log message was generated
    %L line number in output code
    %% output a "%" character
    allowable % Add modifiers between characters to control min-width, max-width, and how the text is aligned
        [%10p]: []There must be 10 characters in it, filled with spaces, and the information is right-aligned
        [%-10p]: []There must be 10 characters in it, which are filled with spaces, the information is left-aligned, and it is widely used


	The above example:[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

  • The configuration of log4j.properties is modified as follows (the modifications made are the last two configuration items)
# rootLogger root configuration for all logger s - log4j.rootLogger=loglevel, appenderName1,appenderName2,appenderName3....
log4j.rootLogger=debug,console
# appender output controller configuration log4j.appender.+appenderName=a certain output type - an example using the Console console
log4j.appender.console=org.apache.log4j.ConsoleAppender
# The output format configuration log4j.appender.+appenderName+layout=a layout format type - Note: The type here has been changed, it is PatternLayout, that is, custom
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# Write a custom output format (take the example above directly) - Note: Added the conversionPattern attribute just mentioned
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

  • test code
    public void test04(){

        LogLog.setInternalDebugging(true);
        Logger logger = Logger.getLogger(Log4jTest01.class);

        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }



2.2.5. Output the log to a file

2.2.5.1. Modify the log4j.properties file

# rootLogger root configuration of all logger s - add another file here
log4j.rootLogger=debug,console,file
# Console appender output controller configuration
log4j.appender.console=org.apache.log4j.ConsoleAppender
# Format for custom console output
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# Writing console custom output formats
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

# One more copy, change to file configuration - just change console to file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n



2.2.5.2. Specify the file output location and character encoding settings

  • Looking at the FileAppender source code, the first thing you see is four properties
	// Indicates whether the log file adopts the method of appending content - there is a constructor in the source code, the default value of this is true
    protected boolean fileAppend;
    protected String fileName;
    protected boolean bufferedIO;
	// Size of log file - default value in source constructor is 8192
    protected int bufferSize;

	// Constructor source code

    public FileAppender() {
        this.fileAppend = true;
        this.fileName = null;
        this.bufferedIO = false;
        this.bufferSize = 8192;
    }

  • There is also a setFile() method in FlieAppender. It is known that this is the method of setting the file, which is the location of the file log file storage path.
    public void setFile(String file) {
        String val = file.trim();
        this.fileName = val;
    }

  • A file parameter needs to be passed in here, so: through the knowledge of MyBatis, you know that the corresponding property in the log4j.properties configuration is the file (cut off the set, get the property name)
# rootLogger root configuration of all logger s - add another file here
log4j.rootLogger=debug,console,file
# Console appender output controller configuration
log4j.appender.console=org.apache.log4j.ConsoleAppender
# Format for custom console output
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# Writing console custom output formats
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

# One more copy, change to file configuration - just change console to file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
# Log file saving path - Note: The former file is a custom appenderName; the latter file is the attribute of the log file output path. If you are afraid of misunderstanding, you can change the latter to File capitalization.
log4j.appender.file.file=D:\log4j\zixieqing\log4j.log
  • Now look at the parent class WriterAppender of FileAppender and see the character encoding settings
    protected boolean immediateFlush;
	// This property is the encoding setting
    protected String encoding;
    protected QuietWriter qw;

  • Therefore: the log4.properties configuration after adding the log file output path and character encoding is:
# Console log output settings
log4j.rootLogger=debug,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n

# Log file output settings
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.file.file=D:\log4j\zixieqing\log4j.log
log4j.appender.file.encoding=UTF-8



2.2.5.3, split log files

  • A log cannot be used to record all the time, in that case, the log file size will be too large

  • Continue to view the FileAppender source code and see the implementation class


2.2.5.3.1, RollingFileAppender implementation class
  • This thing is to use the file size to split the log
  • There are two properties in the source code that need attention
	// Log splitting when large file size is reached
    protected long maxFileSize = 10485760L;
	// How many log files can be split in total
    protected int maxBackupIndex = 1;

  • Therefore: Now modify the log4j.properties configuration file to achieve log splitting according to file size
# Note: Remember to add the following file size in the sentence log4j.rootLoger=debug,console,file to split the configuration
# Such as: log4j.rootLoger=debug,console,rollingFile
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file=D:\log4j\zixieqing\log4j.log
# Log splitting when file size is large
log4j.appender.rollingFile.maxFileSize=10MB
# How many parts can be split
log4j.appender.rollingFile.maxBackupIndex=50


2.2.5.3.2, DailyRollingFileAppender implementation class - recommended
  • This thing is to split the log according to time. Looking at the source code, there is such an attribute
	// Time format, the default value is the following days
    private String datePattern = "'.'yyyy-MM-dd";

  • Modify the log4j.properties configuration file to the following configuration to use
# In the same way, use this method: remember to add the following file size in the log4j.rootLogger=debug,console,file to split the configuration
# Such as: log4j.rootLogger=debug,console,dateRollingFile
log4j.appender.dateRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dateRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dateRollingFile.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
log4j.appender.dateRollingFile.file=D:\log4j\zixieqing\log4j.log
# Add the time format - the following values ​​can be based on the actual situation
log4j.appender.dateRollingFile.datePattern='.'yyyy-MM-dd



2.2.6, log persistence to the database

table base field

CREATE TABLE tbl_log(
    id int(11) NOT NULL AUTO_INCREMENT,
    name varchar(100) DEFAULT NULL COMMENT 'project name',
    createTime varchar(100) DEFAULT NULL COMMENT 'creation time',
    level varchar(10) DEFAULT NULL COMMENT 'log level',
    category varchar(100) DEFAULT NULL COMMENT 'full path of the class',
    fileName varchar(100) DEFAULT NULL COMMENT 'file name',
    message varchar(255) DEFAULT NULL COMMENT 'log message',
    PRIMARY KEY(id)
)


Project MySQL Driver

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
            <scope>runtime</scope>
        </dependency>


log4.properties configuration

# Configure appender output mode Output to database table - Note: add logDB to rootManager, such as: log4j.rootLogger=debug,console,logDB
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=072413
log4j.appender.logDB.Sql=INSERT INTO tbl_log(name,createTime,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')


test code

    @Test
    public void test07(){
        Logger logger = Logger.getLogger(Log4jTest01.class);
        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }



2.2.7, custom logger

  • All the loggers used in the previous configuration files are rootManager's loggers. Next, we will customize a logger. Look at the source code of the PropertyConfigurator class. It has a property in it.
	// How to write custom logger configuration - this is followed by the custom logger name
    static final String LOGGER_PREFIX = "log4j.logger.";

  • Among them: the above-mentioned custom logger name follows the parent-child relationship, that is, the package relationship
like: cn.zixieqing.log4j.test.Log4jTest01
    its parent logger It is the upper-level path or the upper-level path
    E.g:
        cn.zixieqing.log4j.test
        cn.zixieqing.log4j
        ...
        cn


Modify log4j.properties

# Root logger, the output level is trace, output in the console console
log4j.rootLogger=trace,console
# Custom logger, level is info, output in file file
log4j.logger.cn.zixieqing.log4j.test=info,file
# Custom logger, is apache, the level is error
log4j.logger.org.apache=error


Notes on custom logger s

  • If the logge of the root node (ie: rootManager) and the output location of the custom logger configuration are different, take the union of the two, and the output operation will be performed at the configured location.
  • If the log levels configured by the two are different, the output level of our custom logger is the main one


2.2.8, a simple log4j.properties configuration

	log4j.rootLogger=DEBUG,console,file
	#Related settings for console output
	log4j.appender.console = org.apache.log4j.ConsoleAppender
	log4j.appender.console.Target = System.out
	log4j.appender.console.Threshold=DEBUG
	log4j.appender.console.layout = org.apache.log4j.PatternLayout
	log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
	#File output related settings
	log4j.appender.file = org.apache.log4j.RollingFileAppender
	log4j.appender.file.File=./1og/zixieqing.log
	log4j.appender.file.MaxFileSize=10mb
	log4j.appender.file.Threshold=DEBUG
	log4j.appender.file.layout=org.apache.log4j.PatternLayout
	log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]‰m%n
	#log output level
	log4j.logger.org.mybatis=DEBUG
	log4j.logger.java.sq1=DEBUG
	log4j.logger.java.sql.Statement=DEBUG
	log4j.logger.java.sql.ResultSet=DEBUG
	log4j.logger.java.sql.PreparedStatement=DEBUG
	


3,JCL

  • The full name is Jakarta Commons Logging, which is a general log AP provided by Apache

  • Note: This thing itself does not have the function of logging, but is equivalent to a facade. We can freely choose third-party log components (log4j, JUL) as the specific implementation, common-logging will automatically find the log library that is actually used when the program is running through the dynamic search mechanism


Composition of JCL

  • JCL has two basic abstract classes
    • Log: Logger
    • LogFactory: log factory (responsible for creating Log instances)


3.1, play JCL

rely

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

test

    @Test
    void jclQuickStartTest() {

        // LogFactory is org.apache.commons.logging.LogFactory
        Log log = LogFactory.getLog(JCLTest01.class);
        log.info("info information");
    }

  • After running, look at the effect and you will find that the output format is JUL format
  • That is: if there is no third-party logging framework, JUL is used by default


3.2, the introduction of log4j

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

  • Then use the log4j.properties configuration file
  • When the test is performed again, it will become the output configuration of log4j


JCL source code analysis

Log 4 implementation classes of the interface
    JDk13
    JDK14 normal java.util.logging
    Log4j our integrated log4j
    Simple JCL own implementation class

    (1)check Look Jdk14Logger prove that the used JUL logging framework - Look import imported to know
    (2)Check Log4JLogger prove that the used Log4j logging framework - see import imported to know

    (3)Observed LogFactory,see how it loads Logger object
        This is an abstract class and cannot be instantiated
        Need to observe its implementation class LogFactoryImpl

    (4)Observed LogFactoryImpl
    	The real load log implementation uses this implementation class LogFactoryImpl

    (5)Enter getLog - use breakpoints debug be able to see more clearly

        Enter getInstance

        turn up instance = this.newInstance(name);,continue to enter

        find instance = this.discoverLogImplementation(name); Indicates that a log implementation was found

        for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
            result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
        }
    Traverse the log implementation framework we have
        Traversing is an array, this array is according to
        log4j
        jdk14
        jdk13
        SimpleLogger
        traverse in order
        It means that the first thing to traverse is log4j,If there is log4j then execute the logging framework
        If not, traverse out the second one, use jdk14 of JUL logging framework
        and so on

        result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
    to help us create Logger object
        In this method, we see
        c = Class.forName(logAdapterClassName, true, currentCL);
    is to obtain the reflection type object of this type

        Use reflection to help us create logger object
        constructor = c.getConstructor(this.logConstructorSignature);



4,SLF4J

  • This is also a log facade ( Facade Mode / Facade Mode ), the core of which is: external communication with a subsystem must be carried out through a unified facade object, making the subsystem easier to use

Common log facades and log implementations

  • Common log implementations: JUL, log4j, logback, log4j2

  • Common log facades: JCL, slf4j

  • Order of occurrence: log4j -- > Jul -- > jcl--> slf4j -- > logback -- > log4j2


Understanding SLF4J

  • Full name: Simple Logging Facade For Java, simple log facade
  • The main purpose is to provide a standard and standardized API framework for Java log access. Its main significance is to provide an interface. The specific implementation can be handed over to other log frameworks, such as log4j and logback, etc.
  • The two most important functions of SLF4J are the binding of the logging framework and the bridging of the logging framework


4.1. Play SLF4J

4.1.1. Quick start

prepare knowledge

  • SLF4J's level division of logs
trace,debug,info,warn,error five levels

    trace: log tracking information
    debug: log details
    info: Log key information Default print level
    warn: log warning message
    error: log error message


  • Without any other log implementation framework integration, slf4j uses the built-in framework slf4j-simple
    • Note: slf4j-simple must also be imported as a separate dependency
	<!--slf4j Built-in simple log implementation -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
    </dependency>


getting Started

  • rely
    <!--slf4j core dependencies-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>

    <!--slf4j Built-in simple log implementation -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
    </dependency>


  • test code
	@Test
    public void test01(){

        // It is under the slf4j package
        Logger logger = LoggerFactory.getLogger(SLF4JTest01.class);
        logger.trace("trace information");
        logger.debug("debug information");
        logger.info("info information");
        logger.warn("warn information");
        logger.error("error information");

    }



4.1.2. Dynamic information output

  • Essential: use placeholders
  • Sometimes the output log information requires us to match dynamic data. These data may be information or data in database tables.
    @Test
    public void test02(){

        Logger logger = LoggerFactory.getLogger(SLF4JTest01.class);
        String name = "zs";
        int age = 23;
        /* 
            The form of string concatenation: logger.info("student information-name: "+name+"; age: "+age);
            Use the form of JCL: logger.info("Student Information - Name: {}, Age: {}",new Object[]{name,age});

            Although both of the above can achieve the corresponding output, but: trouble
        */
        // Use the SLF4J form
        logger.info("student information-Name:{},age:{}",name,age);

    }

  • Note: If the string spliced ​​later is an object, then {} cannot act as a placeholder for string splicing


4.2.3. Output abnormal information

    @Test
    public void test03(){

        /*

            Log processing of abnormal information

                Under normal circumstances, the exception information we have in development is recorded on the console (a log printing method of our development environment)
                We will extract useful clues based on exception information to de bug bugs

                But in a real production environment (project online), for server or system-related problems
                In fact, the output of the corresponding exception or error information will also be provided on the console.
                But this error output method (output time, location, format...) is the default of the server system

                We can use the logging technology to choose to print the exception as a log to view the output.
                The time, location (console, file), and format of the output are completely defined by ourselves

         */

        Logger logger = LoggerFactory.getLogger(SLF4JTest01.class);

        try {
            Class.forName("aaa");
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            logger.info("XXX in class XXX The method is abnormal, please pay attention to the information in time");
            // e is a reference type object, and cannot do valid string concatenation with the preceding {}
            // logger.info("The specific error is: {}",e);
            // We don't need to add {}, just add the exception object e directly after it - this is using the overloaded method info(String message, Throwable throwable )
            logger.info("The specific error is:",e);
        }
    }



4.2.4, SLF4J and log binding

  • The figure is divided into three parts

    • 1. On the basis of not binding any log implementation, the log cannot be bound to achieve any function
      • slf4j-simple is officially provided by slf4j
      • When using it, you also need to import dependencies and automatically bind them to the slf4j facade
      • If not imported, slf4j core dependencies do not provide any implementation
    • 2. logback and simple (including nop)
    • They are all log implementations provided behind the slf4j facade timeline, so the API fully follows the design of slf4j
    • You only need to import the log implementation dependencies you want to use, and you can seamlessly connect with slf4j
    • Note: Although nop is also divided into implementation, it means that logging is not implemented
    • 3. log4j and JUL
    • They are all log implementations in front of the slf4j facade timeline, so the API does not follow the design of slf4j
    • By adapting the bridging technology, the connection with the log facade is completed


4.2.5. Binding logback

rely

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>


test

public class SLF4JTest {

    @Test
    public void bingingLogTest() {

        Logger logger = LoggerFactory.getLogger(SLF4JTest.class);

        try {
            Class.forName("aaa");
        } catch (ClassNotFoundException e) {
            logger.info("The specific error is:",e);
        }
    }
}


result

10:50:15.391 [main] INFO com.zixieqing.SLF4JTest - The specific error is:
java.lang.ClassNotFoundException: aaa
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	.......
  • This looks very refreshing. If you add slf4j-simple, the output is another matter, and the test is skipped.
  • The above is that you don't need to care which log implementation is used at the bottom layer, but the source code above has not changed at all, and it is written as usual. This is the benefit of the log facade.


4.2.6, slf4j-nop prohibits log printing

rely

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!--
            Note: There are pits, which are related to the import order of dependencies
                as·: If you put this dependency in logback , then it's useless
                But: put this dependency in logback The front of the log can be implemented to prohibit printing

                Reason: in slf4j In the environment, if multiple log implementations appear at the same time, the log implementation imported first is used by default.
        -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>


test

    @Test
    public void slf4jNopTest() {

        Logger logger = LoggerFactory.getLogger(SLF4JTest.class);

        try {
            Class.forName("aaa");
        } catch (ClassNotFoundException e) {
            logger.info("The specific error is:",e);
        }
    }


result

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/install/maven/apache-maven-3.6.1/maven-repo/org/slf4j/slf4j-nop/1.7.30/slf4j-nop-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/install/maven/apache-maven-3.6.1/maven-repo/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.helpers.NOPLoggerFactory]

  • The relevant printing of the log content is gone.
  • Note: If you want nop to work and disable the printing of all logs, you must place the slf4j-nop dependencies above all log implementation dependencies


4.2.7. Binding log4j

  • To play this, you need to pay attention to the problem of the log frame timeline.
    • Order of occurrence: log4j -- > Jul -- > jcl--> slf4j -- > logback -- > log4j2

  • That is to say, it appeared after slf4j (such as: logback, log4j2), these all allow the specification of slf4j, so it can be used after directly importing the corresponding dependencies, but before slf4j, I did not expect these subsequent specifications to appear. If slf4j wants to bind log4j, it needs a slf4j-log4j12 adapter

play with binding log4j

rely

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--slf4j and log4j adapter - want to slf4j able to bind log4j just need this-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>


test

    @Test
    public void slf4jBindingLog4jTest() {
        Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
        logger.info("info information");
    }


result

# Although there is no output message, it is enough to have the following words No appenders could be found for logger
# This means that there is no appender, and the log4j.properties configuration file can be used
log4j:WARN No appenders could be found for logger (com.zixieqing.SLF4JTest).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


The binding of other log frameworks depends on the picture on the official website. In that picture, there are notes on which dependencies are imported when binding.



4.2.8, slf4j source code analysis process

Enter getLogger
            See Logger logger = getLogger(clazz.getName());

            into the overloaded getLogger
            ILoggerFactory iLoggerFactory = getILoggerFactory(); used to obtain Logger The method implemented by the factory

            Enter getILoggerFactory()
            see double-checked locks to make judgments
            implement performInitialization(); Factory initialization method

            Enter performInitialization()
            bind()It is the method used to bind the specific log implementation

            Enter bind()
            See Set gather Set<URL> staticLoggerBinderPathSet = null;
            because there may be N Implementation of multiple logging frameworks
            See staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();

            Enter findPossibleStaticLoggerBinderPathSet()
            See that an ordered and non-repeatable collection object is created
            LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();
            The path to the enumeration class is declared, after if else Judgment to get what log implementations are in the system
            See Enumeration paths;
            if (loggerFactoryClassLoader == null) {
                paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
            } else {
                paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
            }

            We mainly observe constants STATIC_LOGGER_BINDER_PATH
            Through the constant we will find the class StaticLoggerBinder
            This class is statically bound Logger Implemented class
            from slf4j-JDK14 adapter

            Enter StaticLoggerBinder
            See new JDK14LoggerFactory();
            Enter JDK14LoggerFactory class no-argument constructor
            See java.util.logging.Logger.getLogger("");
            is using jul of Logger

            Then observe findPossibleStaticLoggerBinderPathSet
            See the following code, indicating if there are other log implementations
            while(paths.hasMoreElements()) {
                URL path = (URL)paths.nextElement();
                add the path to
                staticLoggerBinderPathSet.add(path);
            }

            back bind method
            Indicates the handling of binding multiple implementations
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            In the event of a multi-log implementation
            will print
            Util.report("Class path contains multiple SLF4J bindings.");


Summary from the source code:
In a real production environment, slf4j can only bind a log implementation framework
Bind multiple, default to use the first imported dependency, and will generate unnecessary warnings



4.2.9, slf4j log reconstruction

There is such a situation: the project originally used log4j logs, but with the iteration of technology, it is necessary to use another logging framework, such as: slf4j+logback, therefore: it is impossible to say that the source code is used at this time, and all the places where log4j is used All changed to slf4j

  • In this case, the corresponding solution is provided on the slf4j official website, which is to add a dependency, and the added thing is called a bridge



4.2.9.1. Play with the bridge

Demonstration bug: first ensure that the project does not have any other interference, such as: additional dependencies, additional log code

rely

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>


log4j.properties configuration file

log4j.rootLogger=debug,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.lo4j.SimpleLayout


test code

package com.zixieqing;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.Test;



    @Test
    public void bridgingTest() {

        // Suppose the project uses log4j
        Logger logger = LogManager.getLogger(SLF4JTest.class);

        logger.info("inf information");
    }


At this point, the project is upgraded and slf4j+logback is used instead as the log

  • 1. Remove the dependency of log4j

  • 2. Add the bridge component of slf4j, so the current dependency becomes as follows

    • 		<dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.13.2</version>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>slf4j-api</artifactId>
                  <version>1.7.25</version>
              </dependency>
      		<!--this is log4j and slf4j bridging component-->
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>log4j-over-slf4j</artifactId>
                  <version>1.7.25</version>
              </dependency>
              <dependency>
                  <groupId>ch.qos.logback</groupId>
                  <artifactId>logback-classic</artifactId>
                  <version>1.2.11</version>
              </dependency>
      
      
    • What bridging component to introduce depends on how to refactor it, and then follow the instructions on the official website to get it


test

  • The source code does not need to be touched directly
package com.zixieqing;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.Test;


    @Test
    public void bridgingTest() {

        // Suppose the project uses log4j
        Logger logger = LogManager.getLogger(SLF4JTest.class);

        logger.info("inf information");
    }


result

14:45:09.901 [main] INFO com.zixieqing.SLF4JTest - inf information



5,LOGBACK

  • This is another logging framework developed by the author of log4j after quitting, which is much better than log4j
  • Logback is currently divided into three modules: logback-core, logback-classic and logback-access
    • logback-core is the base module of the other two modules
    • logback-classic is an improved version of log4j. In addition logback-classic fully implements the SLF4J API. Can be easily replaced with other logging systems such as log4j or JDK14 Logging
    • logback-access access module with Servlet Container integration provides the ability to access logs through Http

Composition of logback

  • Logger: Log logger, mainly used to store log objects, can also define log type and level
  • Appender: used to specify the destination of log output, the destination can be console, file, database, etc.
  • Layout: Responsible for converting events into strings and outputting formatted log information
    • Note: In Logback, the Layout object is encapsulated in the encoder, that is, the encoder used in the future is actually the Layout

Types of logback configuration files

  • Logback provides 3 configuration files
    • logback.groovy
    • logback-test.xml
    • logback.xml
  • We generally use the xml format

logback log output format

Log output format:
    %-10level  Level case is set to 10 characters, left-aligned
    %d{yyyy-MM-dd HH:mm:ss.SSS} date
    %c  Current class fully qualified name
    %M  The current method of executing the log
    %L  line number
    %thread thread name
    %m or%msg    information
    %n  newline



5.1, play logback

5.1, the log level of logback

	error > warn > info > debug > trace

	in: logback The default log level is debug


5.2, get started quickly

rely

        <!-- ogback-classic base template included ogback-core-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
        <!--logback It is a log implementation, and it also needs to be integrated through the log facade-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>


test

public class LogBackTest {

    @Test
    public void quickStartTest() {

        // in this log4j
        Logger logger = LoggerFactory.getLogger(LogBackTest.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }
}


result

15:44:27.032 [main] ERROR com.zixieqing.LogBackTest - error information
15:44:27.033 [main] WARN com.zixieqing.LogBackTest - warn information
15:44:27.033 [main] INFO com.zixieqing.LogBackTest - info information
15:44:27.033 [main] DEBUG com.zixieqing.LogBackTest - debug information

  • Get the information, the default log level of logback is debug


5.3. Simple understanding of logback configuration files

<?xml version="1.0" encoding="utf-8" ?>
<!--logback All configuration needs to be in configuration The movement in the label-->
<configuration>

    <!--Common property configuration - Purpose: Use where required by the following configuration ${name}form, easy to obtain value value
        name You can name it by knowing the meaning
        value value
    -->
    <!--Configure the log output format-->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"/>

    <!--Configure log output type
        name Take it indiscriminately, see the name and know the meaning
        class type full classpath
    -->
    <appender name = "consoleAppender" class = "ch.qos.logback.core.ConsoleAppender">
        <!--Configure the color of the log-->
        <target>
            <!--can also be used system.out Just the usual black and white-->
            system.err
        </target>

        <!--Configure the log output format, directly refer to the previous configuration property Common properties-->
        <encoder class = "ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--configure root Logger-->
    <root level="DEBUG">
        <appender-ref ref="consoleAppender"/>
    </root>

</configuration>
  • Where: <property name= "" value = "" the parameter configure reference of vlaue in ""
Log output format:
    %-10level  Level case is set to 10 characters, left-aligned
    %d{yyyy-MM-dd HH:mm:ss.SSS} date
    %c  Current class fully qualified name
    %M  The current method of executing the log
    %L  line number
    %thread thread name
    %m or%msg    information
    %n  newline


test

public class LogBackTest {

    @Test
    public void quickStartTest() {

        // in this log4j
        Logger logger = LoggerFactory.getLogger(LogBackTest.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }
}



5.4. Output the log to a file

Add the following configuration to logback.xml

    <!--Configure the global file output path-->
    <property name="filePath" value="D:\test"/>

    <!--definition file appender output type-->
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <!--file path and file name -->
        <file>${filePath}/logback.log</file>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--Format of file output-->
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--remember in root logger cite it in fileAppender Configuration-->
    <!--configure root Logger-->
    <root level="DEBUG">
        <appender-ref ref="consoleAppender"/>
        <!--file referencing configuration appender output configuration-->
        <appender-ref ref="fileAppender"/>
    </root>


test code

    @Test
    public void fileAppenderTest() {
        // in this log4j
        Logger logger = LoggerFactory.getLogger(LogBackTest.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }

Through the observation of the results, it will be found that the log file output of logback is to add new log content in the form of appending by default.



5.5. Record log files in HTML format

  • When there are not many log files, this method can be used, because the styles and formats in this HTML can be generated by logback, and the content inside is only added by us. Therefore, we can see at a glance that HTML The file takes up a lot of memory

logback.xml configuration file writing

    <!--definition HTML The file output format is essentially a file, so class yes FileAppender-->
    <appender name="HTMLFileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${filePath}\logback.log</file>

        <!--Note: here is the LayoutWrappingEncoder-->
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <!--Note: using layout Label package, format type is HTMLLayout-->
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
            </layout>
        </encoder>
    </appender>

    <!--configure root Logger-->
    <root level="DEBUG">
        <!--<appender-ref ref="consoleAppender"/>-->
        <!--<appender-ref ref="fileAppender"/>-->
        <appender-ref ref="HTMLFileAppender"/>
    </root>


test

    @Test
    public void htmlFileAppenderTest() {
        // in this log4j
        Logger logger = LoggerFactory.getLogger(LogBackTest.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }

  • Run the program to generate the HTML file


5.6, split log and archive compression

    <!-- config file appender Splittable archived files -->
    <appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">

        <!-- input format -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!-- import file location -->
        <file>${logDir}/roll_logback.log</file>

        <!-- Specify splitting rules -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">

            <!-- Declare filenames in terms of time and compression format Compression format gz -->
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>

            <!-- Split by file size -->
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>
    </appender>

    <!--configure root Logger-->
    <root level="DEBUG">
        <!--<appender-ref ref="consoleAppender"/>-->
        <!--<appender-ref ref="fileAppender"/>-->
        <!--<appender-ref ref="HTMLFileAppender"/>-->
        <appender-ref ref="roll"/>
    </root>

  • Note: as long as the split log is used, the label <filenamepattern>< /filenamepattern> must be included, and the source code explains it
exist TimeBasedRollingPolicy There is a constant in the class
 static final String FNP_NOT_SET ="The FileNamePattern option must be set before using TimeBasedRollingPolicy. ";

The result informed by this constant value is the point of note above

  • In addition: what other attributes are there, and what tags can be used corresponding to each class, that is the attribute name in the specified value of the class, that is, the tag name, just look at the source code

    @Test
    public void rollFileAppenderTest() {
        for (int i = 0; i < 1000; i++) {
            // in this log4j
            Logger logger = LoggerFactory.getLogger(LogBackTest.class);
            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");
        }
    }



5.7. Filter

    <!-- configuration console appender use filter -->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">

        <target>
            System.out
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <!-- Configure filters -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">

            <!-- Set the output level of the log -->
            <level>ERROR</level>

            <!-- higher than level the level set in, the log is printed -->
            <onMatch>ACCEPT</onMatch>

            <!-- lower than level the level set in, the log is blocked -->
            <onMismatch>DENY</onMismatch>

        </filter>

    </appender>

	
    <!--configure root Logger-->
    <root level="DEBUG">
        <!--<appender-ref ref="consoleAppender"/>-->
        <!--<appender-ref ref="fileAppender"/>-->
        <!--<appender-ref ref="HTMLFileAppender"/>-->
        <!--<appender-ref ref="roll"/>-->
        <!--unravel console log print logger-->
		<appender-ref ref="consoleAppender"/>
    </root>
  • The test code is used as usual, and then the content output in the console console will be filtered according to the corresponding content of the filter filter configured above, so it will not be printed out


5.8. Asynchronous log

    <!-- 1,Configure asynchronous logging -->
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
         <!-- quote appender Configuration, that is: what output type of logs to perform asynchronous operations, you can choose consoleAppender,fileAppender.... -->
        <appender-ref ref="fileAppender"/>
    </appender>

    <!--configure root Logger-->
    <root level="DEBUG">
        <!--<appender-ref ref="consoleAppender"/>-->
        <!--<appender-ref ref="fileAppender"/>-->
        <!--<appender-ref ref="HTMLFileAppender"/>-->
        <!--<appender-ref ref="roll"/>-->
        <!--unravel console log print logger-->
		<!--<appender-ref ref="consoleAppender"/>-->
        <!--2,exist root logger Refer to the configured asynchronous log in-->
        <appender-ref ref="asyncAppender"/>
    </root>


In addition: There are two configuration properties in the above 1. Configure asynchronous log

    The following configuration is a threshold, when the remaining capacity of the queue is less than this threshold, the current log level trace,debug,info These 3 levels of logs will be discarded
    Set to 0, indicating that it will never be discarded trace,debug,info The 3 levels of logs
    <discardingThreshold>0</discardingThreshold>

    Configure the depth of the queue, this value will affect the performance of logging, the default value is 256
    <queueSize>256</queueSize>

    Regarding these two properties, in general, the default value can be used.
    Do not configure these two properties indiscriminately, it will affect the system performance, just understand their functions


    @Test
    public void asyncAppenderTest(){

        Logger logger = LoggerFactory.getLogger(LOGBACKTest01.class);

        // log print operation
        for (int i = 0; i < 100; i++) {

            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");

        }

        // Other operations related to the business of the system itself
        System.out.println("Operations related to the business of the system itself 1");
        System.out.println("Operations related to the business of the system itself 2");
        System.out.println("Operations related to the business of the system itself 3");
        System.out.println("Operations related to the business of the system itself 4");
    }



5.9, custom logger

  • Still the same, custom logger is to replace the following bunch
    <!--configure root Logger-->
    <root level="DEBUG">
        <!--<appender-ref ref="consoleAppender"/>-->
        <!--<appender-ref ref="fileAppender"/>-->
        <!--<appender-ref ref="HTMLFileAppender"/>-->
        <!--<appender-ref ref="roll"/>-->
        <!--unravel console log print logger-->
		<!--<appender-ref ref="consoleAppender"/>-->
        <!--2,exist root logger Refer to the configured asynchronous log in-->
        <appender-ref ref="asyncAppender"/>
    </root>


Custom logger configuration

    <!--
        additivity="false" means no inheritance rootlogger
    -->
    <logger name="com.bjpowernode" level="info" additivity="false">
        <!-- in custom logger Medium configuration appender -->
        <appender-ref ref="consoleAppender"/>
    </logger>

  • The test code is the same as before, skip


6,LOG4J2

  • Although this thing is called log4j2, which is an enhancement of log4j, it is actually more optimized for the lack of logback.

Features of log4j2

performance boost

  • Log4j2 contains a next-generation asynchronous logger based on the LMAX Disruptor library. In multi-threaded scenarios, asynchronous loggers have 18x higher throughput and lower latency than Log4j 1.x and Logback

Automatically reload configuration

  • Like Logback, Log4j2 can automatically reload its configuration when it is modified. Unlike Logback, it will not lose log events when reconfiguration occurs

Advanced filtering

  • Like Logback, Log4j2 supports filtering based on contextual data, tags, regular expressions and other components in Log events
  • Additionally, filters can also be associated with loggers. Unlike Logback, Log4j2 can use the generic Filter class in any of these cases

Plugin Architecture

  • Log4j uses the plugin pattern to configure components. So there is no need to write code to create and configure Appender s, Layout s, Pattern Converter s, etc. If configured, Log4j automatically recognizes plugins and uses them

no garbage mechanism

  • During steady state logging, Log4j2 is garbage free in standalone applications and low garbage in Web applications. This reduces the pressure on the garbage collector and can provide better responsiveness

Best match for log4j2

  • Using log4j2 + slf4j way


6.1, play log4j2

6.1.1. Quick start

rely

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>


test

public class Log4j2Test {

    @Test
    public void quickStartTest() {

        // It is under the org.apache.logging.log4j package
        Logger logger = LogManager.getLogger(Log4j2Test.class);
        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
    }
}


result

21:18:43.909 [main] FATAL com.zixieqing.Log4j2Test - fatal information
21:18:43.911 [main] ERROR com.zixieqing.Log4j2Test - error information

  • Conclusion: The default level of log4j2 is the error level


6.1.2, a brief understanding of the configuration file of log4j2

  • log4j2 is created with reference to logback, so the configuration file also uses xml
  • log4j2 is also the configuration in the log4j2.xml file under the default loading classpath (resources)
  • However: log4j2.xml is different from logback.xml. The first point is that the label names in log4j2.xml are capitalized; the second point is that multiple words use camel case nomenclature, and there are other differences.

Simple log4j2.xml configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds

	Note: Do not add this tag xmlns Attributes, and then there are table space constraints, which may cause problems
-->
<Configuration>
    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>


test code

    @Test
    public void log4j2XmlTest() {

        // It is under the org.apache.logging.log4j package
        Logger logger = LogManager.getLogger(Log4j2Test.class);
        logger.fatal("fatal information");
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
    }


result

Directly output the following:

    fatal information
    error information
    warn information
    info information



6.1.3, SLF4J + LOG4J2 - focus from now on

  • The facade of slf4j calls the facade of log4j2, and then the facade of log4j2 calls the implementation of log4j2

  • step
    • 1. Import the log facade of slf4j
    • 2. Import the adapter of log4j2
    • 3. Import the log facade of log4j2
    • 4. Import the log implementation of log4j2
    • 5. Write log4j2.xml

rely

        <!--slf4j facade-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!--log4j-slf4j adapter-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.17.1</version>
        </dependency>
        <!--log4j facade-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.1</version>
        </dependency>
        <!--logj log implementation-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>


log4j2.xml configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds

	Note: Do not add this tag xmlns Attributes, and then there are table space constraints, which may cause problems
-->
<Configuration>
    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>


test

  • Make sure there is no other test code or make sure that the import is the org.slf4j facade
    @Test
    public void log4j2AndSlf4jTest() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }


result

error information
warn information
info information
debug information



6.1.4. Output the log to a file

  • The current code is based on the previously configured log4j2 + slf4j form

The configuration file of log4j2.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    Except in this tag xmlns,It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds
-->
<Configuration>

    <!--Configure global common properties-->
    <properties>
        <property name="logPath">D:\test</property>
    </properties>

    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>

        <!--config file output-->
        <File name="fileAppender" fileName="${logPath}/log4j2.log">
            <!-- The configuration file output format follows the same logback format in -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </File>
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
        </Root>
    </Loggers>
</Configuration>


    @Test
    public void fileAppenderTest() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
        logger.error("error information");
        logger.warn("warn information");
        logger.info("info information");
        logger.debug("debug information");
        logger.trace("trace information");
    }



6.1.5. Split log

log4j2.xml configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    Except in this tag xmlns,It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds
-->
<Configuration>

    <!--Configure global common properties-->
    <properties>
        <property name="logPath">D:\test</property>
    </properties>

    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>

        <!--config file output-->
        <File name="fileAppender" fileName="${logPath}/log4j2.log">
            <!-- The configuration file output format follows the same logback format in -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </File>

        <!--
           Split log files according to specified rules

           fileName:log file name
           filePattern: Naming rules for files after log file splitting
                       $${date:yyyy-MM-dd}: Create a folder based on the date
                             Example: 2021-01-01 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                                  2021-01-02 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                        rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
                             Rules for naming files:%i Indicates the serial number, starting from 0, so that the name of each file will not be repeated
       -->
        <RollingFile name="rollingFile" 
                     fileName="${logPath}/rollingLog.log"
                     filePattern="${logPath}/$${date:yyyy-MM-dd}/rollingLog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <!-- log message format -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>

            <Policies>
                <!-- At system startup, the split rule is triggered and a log file is generated -->
                <OnStartupTriggeringPolicy/>

                <!-- Split by file size Note: Although the limit here is 10 KB,However, the actual generated files are generally 1 larger than this value. KB-->
                <SizeBasedTriggeringPolicy size="10KB"/>

                <!-- Splitting according to time nodes The splitting rule is filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>

            <!-- In the same directory, the number of files is limited. If it exceeds the set value, the old one will be overwritten with the new one according to the time.-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
            <AppenderRef ref="rollingFile"/>
        </Root>
    </Loggers>
</Configuration>


test

    @Test
    public void rollingLogTest() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);

        for (int i = 0; i < 1000; i++) {
            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");
        }
    }



6.1.6. Asynchronous log

  • This technology is the most annoying point of log4j2
  • Log4j2 provides two ways to implement logs, one is through AsyncAppender, the other is through AsyncLogger, which correspond to the Appender component and Logger component we mentioned earlier.
    • Note that these are two different implementations, both in terms of design and source code.


6.1.6.1, AsyncAppender method - understand

  • It is achieved by referencing other Appender s
  • This method uses the <async>< /async> tag
When log events arrive, another thread is started to process them.

It should be noted that if the Appender When an exception occurs, it is imperceptible to the application.

AsyncAppender should be referenced in it Appender After configuration, the default is to use java.util.concurrent.ArrayBlockingQueue Implementation without the need for other external class libraries.  when using this Appender When it comes to multi-threaded environments, it's important to note that blocking queues are susceptible to lock contention, which can have a performance impact. At this point, we should consider using a lock-free asynchronous logger ( AsyncLogger)


step

  • 1. Add asynchronous log dependencies

  •         <!--asyncAppender Asynchronous log dependencies-->
            <dependency>
                <groupId>com.lmax</groupId>
                <artifactId>disruptor</artifactId>
                <version>3.4.2</version>
            </dependency>
    
    
  • 2. In the Appenders tab, configure asynchronous

    • Use the Async tag

      •         <!-- Configure asynchronous logging -->
                <Async name="asyncAppender">
                    <!-- Referencing the previously configured appender -->
                    <AppenderRef ref="fileAppender"/>
                </Async>
        
        
  • 3. The rootlogger refers to Async

    •     <!--configure logger-->
          <Loggers>
              <!--configure root logger-->
              <Root level="DEBUG">
                  <AppenderRef ref="asyncAppender"/>
              </Root>
          </Loggers>
      

Complete log4j2.xml configuration

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    Except in this tag xmlns,It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds
-->
<Configuration>

    <!--Configure global common properties-->
    <properties>
        <property name="logPath">D:\test</property>
    </properties>

    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>

        <!--config file output-->
        <File name="fileAppender" fileName="${logPath}/log4j2.log">
            <!-- The configuration file output format follows the same logback format in -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </File>

        <!--
           Split log files according to specified rules

           fileName:log file name
           filePattern: Naming rules for files after log file splitting
                       $${date:yyyy-MM-dd}: Create a folder based on the date
                             Example: 2021-01-01 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                                  2021-01-02 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                        rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
                             Rules for naming files:%i Indicates the serial number, starting from 0, so that the name of each file will not be repeated
       -->
        <RollingFile name="rollingFile"
                     fileName="${logPath}/rollingLog.log"
                     filePattern="${logPath}/$${date:yyyy-MM-dd}/rollingLog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <!-- log message format -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>

            <Policies>
                <!-- At system startup, the split rule is triggered and a log file is generated -->
                <OnStartupTriggeringPolicy/>

                <!-- Split by file size Note: Although the limit here is 10 KB,However, the actual generated files are generally 1 larger than this value. KB-->
                <SizeBasedTriggeringPolicy size="10KB"/>

                <!-- Splitting according to time nodes The splitting rule is filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>

            <!-- In the same directory, the number of files is limited. If it exceeds the set value, the old one will be overwritten with the new one according to the time.-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!-- Configure asynchronous logging -->
        <Async name="asyncAppender">
            <!-- Referencing the previously configured appender -->
            <AppenderRef ref="fileAppender"/>
        </Async>
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
            <AppenderRef ref="rollingFile"/>
            <AppenderRef ref="asyncAppender"/>
        </Root>
    </Loggers>
</Configuration>


test

    @Test
    public void asyncAppenderTest() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);

        for (int i = 0; i < 1000; i++) {
            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");
        }

        System.out.println("Other asynchronous tasks to be executed 1");
        System.out.println("Other asynchronous tasks to execute 2");
        System.out.println("Other asynchronous tasks to be executed 3");
        System.out.println("Other asynchronous tasks to execute 4");
        System.out.println("Other asynchronous tasks to be executed 5");
    }



6.1.6.2, AsyncLogger method - must be able to

  • AsyncLogger is the most important function of log4j2 to achieve asynchronous, and it is also the officially recommended asynchronous method.
  • It can make calls to Logger.log return faster. You have two options: global async and hybrid async


6.1.6.2.1, Global Async - Understanding
  • All logs are recorded asynchronously, no need to make any changes to the configuration file, just add a parameter when the jvm starts.

  • How to set

    • You only need to add a properties property file under the classpath resources and do one-step configuration. **The file name requirement must be: log4j2.component.properties**, the content of this properties configuration file is as follows:

      • Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
        
        

operate a wave

Create a new log4j2.component.properties file in the resources directory and configure the following:

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector


log4j2.xml file configuration (do not configure any async asynchronous settings)

<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    Except in this tag xmlns,It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds
-->
<Configuration>

    <!--Configure global common properties-->
    <properties>
        <property name="logPath">D:\test</property>
    </properties>

    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>

        <!--config file output-->
        <File name="fileAppender" fileName="${logPath}/log4j2.log">
            <!-- The configuration file output format follows the same logback format in -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </File>

        <!--
           Split log files according to specified rules

           fileName:log file name
           filePattern: Naming rules for files after log file splitting
                       $${date:yyyy-MM-dd}: Create a folder based on the date
                             Example: 2021-01-01 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                                  2021-01-02 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                        rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
                             Rules for naming files:%i Indicates the serial number, starting from 0, so that the name of each file will not be repeated
       -->
        <RollingFile name="rollingFile"
                     fileName="${logPath}/rollingLog.log"
                     filePattern="${logPath}/$${date:yyyy-MM-dd}/rollingLog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <!-- log message format -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>

            <Policies>
                <!-- At system startup, the split rule is triggered and a log file is generated -->
                <OnStartupTriggeringPolicy/>

                <!-- Split by file size Note: Although the limit here is 10 KB,However, the actual generated files are generally 1 larger than this value. KB-->
                <SizeBasedTriggeringPolicy size="10KB"/>

                <!-- Splitting according to time nodes The splitting rule is filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>

            <!-- In the same directory, the number of files is limited. If it exceeds the set value, the old one will be overwritten with the new one according to the time.-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!-- Configure asynchronous logging -->
        <!--<Async name="asyncAppender">-->
        <!--    &lt;!&ndash; Referencing the previously configured appender &ndash;&gt;-->
        <!--    <AppenderRef ref="fileAppender"/>-->
        <!--</Async>-->
    </Appenders>

    <!--configure logger-->
    <Loggers>
        <!--configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
            <AppenderRef ref="rollingFile"/>
            <!--<AppenderRef ref="asyncAppender"/>-->
        </Root>
    </Loggers>
</Configuration>


test

    @Test
    public void globalAsync() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);

        for (int i = 0; i < 1000; i++) {
            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");
        }

        System.out.println("Other asynchronous tasks to be executed 1");
        System.out.println("Other asynchronous tasks to execute 2");
        System.out.println("Other asynchronous tasks to be executed 3");
        System.out.println("Other asynchronous tasks to execute 4");
        System.out.println("Other asynchronous tasks to be executed 5");
    }



6.1.6.2.2, Hybrid async - must be
  • Both synchronous and asynchronous logs can be used in the application, which makes the configuration of the log more flexible
  • The hybrid asynchronous method needs to be implemented by modifying the configuration file, using the AsyncLogger tag to configure

log4j2.xml configuration

  • Note: Remember to comment out the properties of global asynchrony, global asynchrony and hybrid asynchrony cannot appear at the same time
  • How to write: Oil the ``AsyncLogger` tag in the appender tag, and then refer to the appender to be asynchronously output in it.
  • The requirements for the following configuration are: the log under the cn.zixieqing package is output to a file for asynchronous operation, while the root logger log is a synchronous operation
<?xml version="1.0" encoding="UTF-8" ?>
<!--The same, all logging configuration needs to be in Configuration in the label

    Except in this tag xmlns,It can also be configured with two properties
        status="level" The log output level of the log framework itself generally does not need to be configured, because after adding it, there will be more output information that is not very useful.
            like:<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" status="DEBUG"/>
        monitorInterval="Numerical value" The interval for automatically loading configuration files such as: monitorInterval="5" 5 seconds
-->
<Configuration>

    <!--Configure global common properties-->
    <properties>
        <property name="logPath">D:\test</property>
    </properties>

    <!--configure Appender output type-->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT"/>

        <!--config file output-->
        <File name="fileAppender" fileName="${logPath}/log4j2.log">
            <!-- The configuration file output format follows the same logback format in -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </File>

        <!--
           Split log files according to specified rules

           fileName:log file name
           filePattern: Naming rules for files after log file splitting
                       $${date:yyyy-MM-dd}: Create a folder based on the date
                             Example: 2021-01-01 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                                  2021-01-02 In this folder, all log information of the day is recorded (the split logs are placed in this folder)
                        rollog-%d{yyyy-MM-dd-HH-mm}-%i.log
                             Rules for naming files:%i Indicates the serial number, starting from 0, so that the name of each file will not be repeated
       -->
        <RollingFile name="rollingFile"
                     fileName="${logPath}/rollingLog.log"
                     filePattern="${logPath}/$${date:yyyy-MM-dd}/rollingLog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <!-- log message format -->
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>

            <Policies>
                <!-- At system startup, the split rule is triggered and a log file is generated -->
                <OnStartupTriggeringPolicy/>

                <!-- Split by file size Note: Although the limit here is 10 KB,However, the actual generated files are generally 1 larger than this value. KB-->
                <SizeBasedTriggeringPolicy size="10KB"/>

                <!-- Splitting according to time nodes The splitting rule is filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>

            <!-- In the same directory, the number of files is limited. If it exceeds the set value, the old one will be overwritten with the new one according to the time.-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!-- Configure asynchronous logging -->
        <!--<Async name="asyncAppender">-->
        <!--    &lt;!&ndash; Referencing the previously configured appender &ndash;&gt;-->
        <!--    <AppenderRef ref="fileAppender"/>-->
        <!--</Async>-->

        <!-- customize logger,make custom logger for asynchronous logger -->
        <!--
            name It is the package path. Note: this asynchronous configuration is limited to this name The logs under the specified package have asynchronous operations, if there is another package cn.xiegongzi
                    So: cn.xiegongzi The logs under the package are still synchronous, not asynchronous

            includeLocation="false"
                Indicates that the line number information in the log record is removed. This line number information greatly affects the efficiency of the log record (this line number is not added in production)
                In severe cases, the efficiency of logging may be lower than that of synchronized logs

            additivity="false" means no inheritance rootlogger

        -->
        <AsyncLogger name="cn.zixieqing"
                     level="debug"
                     includeLocation="false"
                     additivity="false">

            <!-- file output fileAppender,set to print asynchronously -->
            <AppenderRef ref="fileAppender"/>

        </AsyncLogger>
    </Appenders>

    <!--configure logger-->
    <!--now this root logger The logs in are synchronized-->
    <Loggers>
        <!--to configure root logger-->
        <Root level="DEBUG">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
            <AppenderRef ref="rollingFile"/>
            <!--<AppenderRef ref="asyncAppender"/>-->
        </Root>
    </Loggers>
</Configuration>


test

    @Test
    public void blendAsync() {
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);

        for (int i = 0; i < 1000; i++) {
            logger.error("error information");
            logger.warn("warn information");
            logger.info("info information");
            logger.debug("debug information");
            logger.trace("trace information");
        }

        System.out.println("Other asynchronous tasks to be executed 1");
        System.out.println("Other asynchronous tasks to execute 2");
        System.out.println("Other asynchronous tasks to be executed 3");
        System.out.println("Other asynchronous tasks to execute 4");
        System.out.println("Other asynchronous tasks to be executed 5");
    }



6.1.6.2.3, AsyncAppender, AsyncLogge two suggestions
  • If asynchronous logs are used, AsyncAppender and AsyncLogger should not appear at the same time, there will be no such strange demand, and the effects will not be superimposed. If they appear at the same time, the efficiency will be dominated by AsyncAppender
  • Similarly, the global asynchronous and hybrid asynchronous of AsyncLogger should not appear at the same time, and there will be no such strangeness, and the effects will not be superimposed.


7. Follow-up


  • The content of this blog is too much, the load is too large, this spam blog, the old man really doubts: TMD, it updates all the content of the blog post online every time it is updated, TNND, it will not collect all the data when clicking confirm, it reaches 3000 After more than one line of blog post content, as soon as the old man wrote the content, he found that it was reloading, and then the fan of my laptop started to spin rapidly, and the memory quickly rose to more than 60%.

  • Then I found that the browser popped up the following page of its ancestor's grave, causing Lao Na to re-copy the content in markdown again, making Lao Na want to just use my Zhihu.

Tags: Spring Boot

Posted by capitala on Wed, 27 Jul 2022 01:58:25 +0930