美文网首页
12- JavaLogs

12- JavaLogs

作者: XAbo | 来源:发表于2021-03-02 14:53 被阅读0次

    日志门面:类似与JDBC,使用一套接口,操作不同实现。

    日志图谱

    日志框架出现的顺序:log4j>>jul>>jcl>>slf4j>>logback>>log4j2
    log4j2:即使日志门面也是日志实现

    一、JUL

    全称Java Util Logging 是JAVA原生的日志框架,使用时不需要引用三方Jar类库。

    • Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来发布日志信息。

    • Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志。

    • Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。

    • Level:每条日志消息都有一个关联的日志级别。我们可以将Level和Loggers、Appenders做关联以便于我们过滤消息。

    • JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层;RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过路径来关联。所有日志记录器对象的顶级父元素 class为java.util.logging.LogManager$RootLogger

      流程图

    1.1 JUL入门

    public class JULTest {
        public void test01() {
            Logger logger = Logger.getLogger("com.examples.logs");
            logger.info("你好");
            logger.warning("警告!!!!");
           //通用的日志方法
            logger.log(Level.INFO,"HELLO WORLD !!");
            String name ="z";
            String age = "0";
            //使用占位符的通用方法
            logger.log(Level.INFO,"{0},{1}",new Object[]{name,age});
            
            //日志级别
            //logger.setLevel(Level.ALL); 
            //logger.setLevel(Level.OFF);
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            logger.severe("severe");
            logger.warning("warning");
            //默认级别
            logger.info("info");
            logger.config("config");
            logger.fine("fine");
            logger.finer("finer");
            logger.finest("finest");
        }
        public static void main(String[] args) throws  Exception {
            JULTest jul = new JULTest();
            jul.test01();
    
        }
    }
    

    1.2 自定义日志

    import java.util.logging.*;
    
    public class JULTest {
        public void test02() throws Exception{
            SimpleFormatter simpleFormatter = new SimpleFormatter();
            Logger logger = Logger.getLogger("com.examples.logs");//儿子
            Logger logger1 = Logger.getLogger("com.examples");//爸爸
            Logger logger0 = logger1.getParent();//爷爷  无名氏
            System.out.println(logger1==logger.getParent());
            System.out.println(logger0+"--->"+logger0.getName());
    
            // 0. 爷爷的默认
            // 默认INFO,需要使用consoleHandler0修改默认级别才能输出全部级别日志
            ConsoleHandler consoleHandler0 = new ConsoleHandler();
            logger0.addHandler(consoleHandler0);
            consoleHandler0.setLevel(Level.ALL);
            logger0.setLevel(Level.ALL);
            logger0.severe("爷爷的severe");
            logger0.warning("爷爷的warning");
            logger0.info("爷爷的info");
            logger0.config("爷爷的config");
            logger0.fine("爷爷的fine");
            logger0.finer("爷爷的finer");
            logger0.finest("爷爷的finest");
    
            // 1.爸爸在控制台:输出所有日志,使用自定义的日志级别
            // 爸爸关闭爷爷日志级别配置
            logger1.setUseParentHandlers(false);
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setFormatter(simpleFormatter);
            logger1.addHandler(consoleHandler);
            consoleHandler.setLevel(Level.WARNING);
            logger1.setLevel(Level.ALL);
            logger1.severe("爸爸的severe");
            logger1.warning("爸爸的warning");
            logger1.info("爸爸的info");
            logger1.config("爸爸的config");
            logger1.fine("爸爸的fine");
            logger1.finer("爸爸的finer");
            logger1.finest("爸爸的finest");
    
            // 2.儿子在文件中输出日志:输出错误日志,使用自定义的日志级别
            logger.setUseParentHandlers(false);
            FileHandler fileHandler = new FileHandler("C:\\Users\\Administrator\\Desktop\\logs.txt");
            fileHandler.setFormatter(simpleFormatter);
            logger.addHandler(fileHandler);
            fileHandler.setLevel(Level.SEVERE);
            logger.setLevel(Level.ALL);
            logger.severe("severe");
            logger.warning("warning");
            logger.info("info");
            logger.config("config");
            logger.fine("fine");
            logger.finer("finer");
            logger.finest("finest");
        }
        public static void main(String[] args) throws  Exception {
            JULTest jul = new JULTest();
            jul.test02();
        }
    }
    

    结果:

    控制台 文件

    1.3 使用配置文件

    JDK默认的日志文件在

     $JAVA_HOME/jre/lib
    

    实例:

    #指定顶级父元素RootLogger默认处理器
    handlers= java.util.logging.ConsoleHandler
    .level= ALL
    
    java.util.logging.FileHandler.pattern = %h/java%u.log
    
    java.util.logging.FileHandler.limit = 50000
    java.util.logging.FileHandler.count = 1
    java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
    
    java.util.logging.ConsoleHandler.level = ALL
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    
    #自定义Logger
    com.simple.handlers =  java.util.logging.ConsoleHandler
    com.simple.level=ALL
    #关闭RootLogger配置
    com.simple.ueParentHandlers = false
    

    客户端:

    package com.examples.logs;
    import java.io.InputStream;
    import java.util.logging.*;
    public class JULTest {
        public void test02() throws Exception{
            InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
            LogManager logManager = LogManager.getLogManager();
            logManager.readConfiguration(ins);
            Logger logger1 = Logger.getLogger("com.examples");//爸爸
            Logger logger0 = logger1.getParent();//爷爷  无名氏
            logger0.severe("爷爷的severe");
            logger0.warning("爷爷的warning");
            logger0.info("爷爷的info");
            logger0.config("爷爷的config");
            logger0.fine("爷爷的fine");
            logger0.finer("爷爷的finer");
            logger0.finest("爷爷的finest");
    
        }
        public static void main(String[] args) throws  Exception {
            JULTest jul = new JULTest();
            jul.test02();
        }
    }
    
    结果

    二、Log4J

    是Apache的一款开源日志框架,通过在项目中使用log4j,我们可以控制日志信息输出到控制台、文件甚至是数据库中。

    Log4j主要有Loggers(日志记录器,是category的别名)、Appenders(输出端)和Layout(日志格式化器)组成。其中Loggers控制日志的输出级别与日志是否输出;Appenders指定日志的输出方式;控制日志的输出格式。

    自定义日志使用场景:第三方日志。

    Loggers

    Loggers层级
    Appenders
    • ConsoleAppender:将日志输出到控制台。
    • FileAppender:将日志输出到文件中。
    • DailyRollingFileAppender:将日志输出到一个日志文件,并且每天输出到一个新的文件。
    • RollingFileAppender:将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件。
    • JDBCAppenders:把日志信息保存到数据库中。

    Layout

    • HTMLLayout:格式化日志输出为HTML表格形式。
    • SimpleLayout:简单的日志输出格式化,打印的日志格式为(info-message)。
    • PatternLayout:最强大的格式化器,可以根据自定义格式输出日志,如果没有指定转换格式,就是默认的转换格式。

    2.1 入门案例

    pom.xml

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

    客户端:

    package com.examples.logs;
    import org.apache.log4j.BasicConfigurator;
    import org.apache.log4j.Logger;
    public class Log4jTest {
    
       public void test1() throws  Exception{
           //不适应配置文件进行初始化
           BasicConfigurator.configure();
           //获取日志记录
           Logger logger = Logger.getLogger(Log4jTest.class);
           //默认日志级别是debug
           logger.fatal("fatal");//验证错误,一般会造成系统崩溃并终止运行
           logger.error("error");//错误信息,不会影响系统运行
           logger.warn("warn");//警告信息,可能会发生的问题
           logger.info("info");//运行信息
           logger.debug("debug");//调试信息
           logger.trace("trace");//追踪信息,记录程序所有流程信息
        }
        public static void main(String[] args) throws  Exception{
            Log4jTest log4jTest = new Log4jTest();
                log4jTest.test1();
        }
    }
    
    结果

    2.2 使用配置文件

    log4j.properties

    log4j.rootLogger=info,console,fileAppender,rollingFile 
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
    
    log4j.appender.fileAppender = org.apache.log4j.FileAppender
    log4j.appender.fileAppender.File = D:/xzv.log
    log4j.appender.fileAppender.Append = true
    log4j.appender.fileAppender.Threshold = DEBUG
    log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
    log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m\n
    
    log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
    log4j.appender.rollingFile.Threshold=ERROR
    log4j.appender.rollingFile.File=D:/xzb.log
    log4j.appender.rollingFile.MaxFileSize=10KB
    log4j.appender.rollingFile.Append = true
    log4j.appender.rollingFile.MaxBackupIndex=10
    log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
    log4j.appender.rollingFile.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss.SSS}  [ %t:%r ] - [ %p ]  %m\n
    # 配置文件中layout的pattern的格式:
    # %m:输出代码中指定的日志信息。
    # %p:输出优先级及DEBUG、INFO等。
    # %n:换行符Windows:"\n";Unix:"%n"。
    # %r:输出自应用启动到输出该LOG信息耗费的毫秒数
    # %c:输出打印语句所属的类的全名。
    # %t:输出产生该日志的线程全名。
    # %d:输出服务器当前时间,默认ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}。
    # %l:输出日志时间发生的位置,包括类名、线程和代码中的行数。
    # %F:输出日志消息产生时所在的文件名称。
    # %L:输出代码中的行号
    # %%输出一个“%”字符。
    
    # %5c:输出category名称,最小宽度是5,category<5,默认的情况下右对齐。
    # %-5c:输出category名称,最小宽度是5,“-”号指定左对齐,会有空格。
    # .5c:输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格。
    # %20.30c category名称<20补空格,并且右对齐,>30字符,就从左边将多出的字符截掉。
    

    Log4jTest.java

    public class Log4jTest {
    
       public void test1() throws  Exception{
           //开启LOG4J内置日志
           LogLog.setInternalDebugging(false);
           Logger logger = Logger.getLogger(Log4jTest.class);
            for (int i = 0; i < 100; i++) {
                logger.fatal("fatal");//验证错误,一般会造成系统崩溃并终止运行
                logger.error("error");//错误信息,不会影响系统运行
                logger.warn("warn");//警告信息,可能会发生的问题
                logger.info("info");//运行信息
                logger.debug("debug");//调试信息
                logger.trace("trace");//追踪信息,记录程序所有流程信息
            }
        }
        public static void main(String[] args) throws  Exception{
            Log4jTest log4jTest = new Log4jTest();
            log4jTest.test1();
        }
    }
    

    结果:

    结果

    三、 JCL日志门面

    当项目从JUL转变为Log4J的时候,由于接口不同,代价是巨大的。JCL就是为解决此问题的,所有日志的实现将提供统一的接口,改变日志的实现而不需要更改程序。


    JCL
    JCL原理

    日志门面支持的日志实现数组

    private static final String[] classesToDiscover = {
        "org.apache.commons.logging.impl.Log4JLogger",
        "org.apache.commons.logging.impl.Jdk14Logger",
        "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
        "org.apache.commons.logging.impl.SimpleLog"
    }
    

    获取具体的日志实现:

    for(int i = 0; i < classesToDiscover.length && result == null; ++i)
     {
            result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
    }
    

    3.1 入门案例

    客户端:

    package com.examples.logs;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    public class JCLTest {
        public void test1() {
            Log log = LogFactory.getLog(JCLTest.class);
            log.info("大家好!!!");
        }
        public static void main(String[] args) {
            JCLTest  jcl  = new JCLTest();
            jcl.test1();
        }
    }
    

    3.1.1 只引入commons-logging包

        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
    
    只引入commons-logging

    3.1.2 引入log4j不引入配置文件

     <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
       <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
             <version>1.2.17</version>
       </dependency>
    
    引入log4j不引入配置文件

    3.1.3 引入log4j和配置文件

    引入log4j和配置文件

    四、SLF4J日志门面

    原理:

    • SLF4J通过LoggerFactory加载日志具体实现对象。
    • LoggerFactory在初始化的过程中,会通过performInitialization()绑定具体的日志实现。
    • 在绑定具体实现的时候,通过类加载器,加载org.slf4j.impl.StaticLoggerBinder.class
    • 所以只要是一个日志实现框架,在org.slf4j.impl包中提供一个自己的StaticLoggerBinder类,在其中提供具体日志实现的LoggerFactory就可以被SLF4J所加载。

    客户端:

    package examples.logs;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class SLF4JTest {
        public static  final Logger LOGGER = LoggerFactory.getLogger(SLF4JTest.class);
        public void test1() {
            LOGGER.error("错误");
            LOGGER.warn("警告");
            LOGGER.info("信息");
            LOGGER.debug("BUG");
            //使用占位符
            String name ="xzv";
            int age = 30;
            LOGGER.info("name:{},{}",name,age);
            //将系统异常信息输出
            try {
                int i =1/0;
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.warn("警告",e);
            }
        }
        public static void main(String[] args) {
            SLF4JTest jcl  = new SLF4JTest();
            jcl.test1();
        }
    }
    

    4.1 入门案例

    pom.xml: 自带的日志实现

     <dependencies>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.21</version>
            </dependency>
        </dependencies>
    
    自带的日志实现

    4.2 日志绑定

    image.png
     - 添加slf4j-api的依赖。
     - 使用slf4j的API在项目中进行统一的日志记录。
     - 绑定具体的日志实现框架
       - 1 绑定已经实现了slf4j的日志框架,直接添加对应的依赖。
       - 2 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
    - slf4j有且仅有一个日志实现框架的绑定。(如果出现多个默认使用第一个依赖日志实现)
    

    4.2.1 绑定logback

    pom.xml

        <dependencies>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
        </dependencies>
    
    绑定logback

    4.2.2 绑定nop

    pom.xml

        <dependencies>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-nop</artifactId>
                <version>1.7.25</version>
            </dependency>
        </dependencies>
    

    结果:没有输出日志框架的日志。

    绑定nop

    4.2.3 绑定 log4j

    pom.xml

         <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
           <!--绑定log4j 日志实现,导入适配器-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
    绑定 log4j-未引入配置文件
    绑定 log4j-引入配置文件

    4.2.4 绑定 JUL

    pom.xml

           <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
           <!--绑定JUL日志实现,导入适配器,JUL是JDK内置的,不需要添加实现-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-jdk14</artifactId>
               <version>1.7.31</version>
            </dependency>
    
    绑定 JUL

    4.2.5 slf4j桥接器

    应用场景:当旧项目的日志框架log4j需要更换为slf4j+logback的时候,又不想修改源代码。使用桥接可以处理
    原项目的pom.xml

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

    新项目的pom.xml

            <!---1. 先导入日志门面--->
           <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
           <!---2. 导入需要更换的logback;仅仅导入logback源代码是会报错的--->
           <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
          <!---3. 导入桥接器--->
           <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>log4j-over-slf4j</artifactId>
                <version>1.7.25</version>
            </dependency>
    

    桥接器注意事项:
    1.jcl-over-slf4j.jar和slf4j-jcl.jar不能同时部署。
    2.log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现。
    3.jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现。


    log4j-over-slf4j.jar和slf4j-log4j12.jar

    五、Logback

    Logback是由log4j创始人设计的另一个开源日志组件。
    Logback主要分为三个模块:

    • logback-core:其他两个模块的基础模块。
    • logback-classic:是log4j的一个改良版本,同时它完整实现了slf4j API。
    • logback-access:访问模块与Servlet容器集成提供通过http来访问日志的功能。

    5.1 logback入门

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>Theme</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
        <artifactId>Logback</artifactId>
        <dependencies>
           <!--   maven传递依赖,不需要导入slf4j-api
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
           -->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
        </dependencies>
    </project>
    

    客户端:

    package com.theme.logback;
    import org.slf4j.LoggerFactory;
    import org.slf4j.Logger;
    public class LogbackTest {
        public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);
        public void test1() {
            LOGGER.error("错误");
            LOGGER.warn("警告");
            LOGGER.info("信息");
            LOGGER.debug("BUG");
        }
        public static void main(String[] args) {
            LogbackTest logbackTest = new LogbackTest();
            logbackTest.test1();
        }
    }
    

    结果:

    image.png

    5.2. logback配置

    logback会依次读取以下类型的配置文件logback.groovylogback-test.xmllogback.xml 如果均不存在会采用默认配置。

    logback组件之间的关系:
    Logger:日志的记录器,把它关联到应用对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
    Appender:用于指定日志输出的目的地。
    Layout:负责把事件转换成字符串,格式化的日志信息的输出。在logback对象被封装在encoder中。

    配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false">
    
        <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
        <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
        <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
        <property name="LOG_HOME" value="/logggs"/>
    
        <!--控制台日志, 控制台输出 -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <!--控制输出对象 默认System.out 修改为System.err-->
            <target>System.err</target>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!--日志文件-->
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>${LOG_HOME}/logback.log</file>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!--HTML日志文件-->
        <appender name="HTMLFILE" class="ch.qos.logback.core.FileAppender">
            <file>${LOG_HOME}/logback.html</file>
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
                </layout>
            </encoder>
        </appender>
    
    
        <!--日志文件 拆分-->
        <appender name="ROLLFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_HOME}/roll_logback.log</file>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!--按照时间和压缩格式声明拆分的文件名-->
            <fileNamePattern>${LOG_HOME}/roll.%d{yyyy-MM-dd HH:mm:ss.SSS}log%i.gz</fileNamePattern>
              <!--按照文件大小拆分-->
                <maxFileSize>1MB</maxFileSize>
            </rollingPolicy>
    
            <!--日志级别过滤器-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!--日志过滤级别-->
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMisMatch>DENY</onMisMatch>
            </filter>
        </appender>
        <!--异步日志-->
        <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
         <!--指定某个具体的appender-->
            <appender-ref ref="ROLLFILE"/>
        </appender>
    
    
        <root level="DEBUG">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="FILE"/>
            <appender-ref ref="HTMLFILE"/>
            <appender-ref ref="ASYNC"/>
        </root>
    
        <!--自定义日志:表示com.theme.logback包下的日志走自定义的配置-->
        <logger name="com.theme.logback" level="info" additivity="false">
            <appender-ref ref="STDOUT"/>
        </logger>
    </configuration>
    

    5.3.logback-access

    logback-access模块与Servlet容器集成,以提供HTTP访问日志的功能。我们可以使用logback-access模块来替代tomcat的访问日志。

    • 1 将logback-access.jar与logback-core.jar复制到$TOMCAT_HOME/lib/目录下

    • 2 修改$TOMCAT_HOME/conf/server.xml中的Host元素,添加

    <value className="ch.qos.logback.access.tomcat.LogbackValue"/>
    
    • 3 logback默认会在$TOMCAT_HOME/conf下查找logback-access.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    <statusLister class="ch.qos.logback.core.status.OnConsolesListener"/>
    <property name="LOG_DIR" value="${catalina.base}/logs/">
      <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_DIR}/access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/roll.%d{yyyy-MM-dd HH:mm:ss.SSS}log%i.gz</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
            </rollingPolicy>
            <encoder>
                <pattern>combined</pattern>
            </encoder>
        </appender>
    <appender-ref ref="FILE">
    </configuration>
    

    六、Log4j2

    Log4j2是对Log4j的升级,参考了logback的优点;

    • 异常处理,在logback中Appender中的异常不会被应用感知到,但是在Log4j2中提供一些异常处理机制。
    • 自动重载配置,参考logback的设计,生产环境上可以动态的修改日志的级别而不需要重启应用。
    • 无垃圾机制,Log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的JVM GC。

    6.1 Log4j2入门

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
        <dependencies>
    
           <!--第一种门面-->
           <!--log4j2日志门面-->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.12.1</version>
            </dependency>
     
           <!--第二种门面-->
            <!--slf4j日志门面-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.26</version>
            </dependency>
            <!--使用log4j2适配器-->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>2.9.1</version>
            </dependency>
    
    
            <!--log4j2日志实现 -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.12.1</version>
            </dependency>
        </dependencies>
    

    客户端:

    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    public class Log4j2Test {
        public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
        public void test1() {
            LOGGER.fatal("fatal");
            LOGGER.error("error");
            LOGGER.warn("warn");
            LOGGER.debug("debug");
            LOGGER.trace("trace");
        }
    
        public static void main(String[] args) {
            Log4j2Test logbackTest = new Log4j2Test();
            logbackTest.test1();
        }
    }
    

    log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!--
    status:日志框架本身日志的级别
    monitorInterval:自动加载配置文件的间隔时间,不低于5S
    -->
    <configuration status="OFF" monitorInterval="5">
    
        <properties>
            <property name="LOG_HOME">/logs</property>
        </properties>
    
    
        <appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <!--只接受程序中DEBUG级别的日志进行处理-->
                <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            </Console>
    
            <file name="File" fileName="${LOG_HOME}/mylog.log">
                <PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            </file>
    
            <!--随机读写流-->
            <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAccessFile.log">
                <PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            </RandomAccessFile>
    
    
            <!--处理INFO级别的日志,并把该日志放到logs/info.log文件中-->
            <RollingFile name="RollingFileInfo" fileName="${LOG_HOME}/logs/info.log"
                         filePattern="logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <!--只接受INFO级别的日志,其余的全部拒绝处理-->
                    <ThresholdFilter level="INFO"/>
                    <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                    <OnStartupTriggeringPolicy/>
                </Policies>
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
    
            <!--druid的日志记录追加器-->
            <RollingFile name="druidSqlRollingFile" fileName="./logs/druid-sql.log"
                         filePattern="logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
        </appenders>
        <!--     然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
        <loggers>
            <!--默认的root的logger-->
            <root level="DEBUG">
                <appender-ref ref="Console"/>
                <appender-ref ref="RollingFileInfo"/>
            </root>
            <!--额外配置的logger-->
            <!--记录druid-sql的记录-->
            <logger name="druid.sql.Statement" level="debug" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            </logger>
            <!--log4j2 自带过滤日志-->
            <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
            <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
            <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
            <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
            <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
            <Logger name="org.crsh.plugin" level="warn" />
            <logger name="org.crsh.ssh" level="warn"/>
            <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
            <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
            <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
            <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
            <logger name="org.thymeleaf" level="warn"/>
        </loggers>
    </configuration>
    

    6.2 Log4j2异步日志

    摘自:https://blog.csdn.net/jek123456/article/details/100123570
    pom.xml

    <dependency>
         <groupId>com.lmax</groupId>
         <artifactId>disruptor</artifactId>
         <version>3.4.3</version>
     </dependency>
    

    6.2.1 AsyncAppender方式

    AsyncAppender是通过引用别的Appender来实现的,当有日志事件到达时,会开启另外一个线程来处理它们。需要注意的是,如果在Appender的时候出现异常,对应用来说是无法感知的。 AsyncAppender应该在它引用的Appender之后配置,默认使用 java.util.concurrent.ArrayBlockingQueue实现而不需要其它外部的类库。 当使用此Appender的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。这时候,我们应该考虑使用无所的异步记录器(AsyncLogger)。

    log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" name="MyApp" packages="">
     <Appenders>
       <File name="MyFile" fileName="logs/app.log">
         <PatternLayout>
           <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
         </PatternLayout>
       </File>
       <Async name="Async">
         <AppenderRef ref="MyFile"/>
       </Async>
     </Appenders>
     <Loggers>
       <Root level="error">
         <AppenderRef ref="Async"/>
       </Root>
     </Loggers>
    </Configuration>
    

    6.2.2 AsyncLogger方式

    AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。
    你可以有两种选择:全局异步和混合异步。

    • 全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要在jvm启动的时候增加一个参数;
    • 混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。因为Log4j文档中也说了,虽然Log4j2提供以一套异常处理机制,可以覆盖大部分的状态,但是还是会有一小部分的特殊情况是无法完全处理的,比如我们如果是记录审计日志,那么官方就推荐使用同步日志的方式,而对于其他的一些仅仅是记录一个程序日志的地方,使用异步日志将大幅提升性能,减少对应用本身的影响。混合异步的方式需要通过修改配置文件来实现,使用AsyncLogger标记配置。

    全局异步
    不修改配置文件,在系统初始化的时候,增加全局参数配置:

    System.setProperty("log4j2.contextSelector, "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
    

    你可以在你第一次获取Logger之前设置,也可以加载JVM启动参数里,类似

    java -Dog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
    

    混合异步

    log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN">
      <Appenders>
        <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
        <RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"
                  immediateFlush="false" append="false">
          <PatternLayout>
            <Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern>
          </PatternLayout>
        </RandomAccessFile>
      </Appenders>
      <Loggers>
        <!-- pattern layout actually uses location, so we need to include it -->
        <AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
          <AppenderRef ref="RandomAccessFile"/>
        </AsyncLogger>
        <Root level="info" includeLocation="true">
          <AppenderRef ref="RandomAccessFile"/>
        </Root>
      </Loggers>
    </Configuration>
    

    在使用异步日志的时候需要注意一些事项,如下:

    • 不要同时使用AsyncAppender和AsyncLogger,也就是在配置中不要在配置Appender的时候,使用Async标识的同时,又配置AsyncLogger,这不会报错,但是对于性能提升没有任何好处。
    • 不要在开启了全局同步的情况下,仍然使用AsyncAppender和AsyncLogger。这和上一条是同一个意思,也就是说,如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。
    • 如果不是十分必须,不管是同步异步,都设置immediateFlush为false,这会对性能提升有很大帮助。
    • 如果不是确实需要,不要打印location信息,比如HTML的location,或者pattern模式里的%C or $class, %F or %file, %l or %location, %L or %line, %M or %method, 等,因为Log4j需要在打印日志的时候做一次栈的快照才能获取这些信息,这对于性能来说是个极大的损耗。

    相关文章

      网友评论

          本文标题:12- JavaLogs

          本文链接:https://www.haomeiwen.com/subject/eatxqltx.html