Java日志框架详解

日志框架

日志基本概念

日志:在计算机领域,日志文件(logfile)是一个记录了发生在运行中的操作系统或其他软件中的事件的文件,或者记录了在网络聊天软件的用户之间发送的消息。

日志记录(Logging):是指保存日志的行为。最简单的做法是将日志写入单个存放日志的文件。

日志作用
日志记录了系统行为的时间、地点、状态等相关信息,能够帮助我们了解并监控系统状态,在发生错误或者接近某种危险状态时能够及时提醒我们处理,同时在系统产生问题时,能够帮助我们快速的定位、诊断并解决问题。

Java中常用日志框架概述

在Java程序中常用日志框架可以分为两类

  • 无具体实现的抽象门面框架,如:Commons Logging、SLF4J
  • 具体实现的框架,如:Log4j,Log4j 2,Logback,JUL

Java常用日志框架历史
1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。
期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4j到java的标准库中,但Sun拒绝了。
2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。
接着,Apache推出了Jakarta Commons Logging,JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是log4j,也可以是Java Util Logging。
后来(2006年),Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。
现今,Java日志领域被划分为两大阵营:Commons Logging阵营和SLF4J阵营。
Commons Logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出slf4j的发展趋势更好:
Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2。Log4j 2具有logback的所有特性。

Java常用日志框架之间的关系
Log4j2与Log4j1发生了很大的变化,log4j2不兼容log4j1。
Commons Logging和Slf4j是日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)。log4j和Logback则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用这只需要关注接口而无需关注具体的实现,做到解耦。
比较常用的组合使用方式是Slf4j与Logback组合使用,Commons Logging与Log4j组合使用。
Logback必须配合Slf4j使用。由于Logback和Slf4j是同一个作者,其兼容性不言而喻。

日志门面框架

日志门面与日志实现的关系:

用户
日志门面(接口)
日志实现
Commons-logging
Slf4j
Log4j
JUL
Slf4j-nop
Slf4j-simple
Logback

日志门面是外观模式的一个典型的应用。日志门面框架就使一套提供了日志相关功能的接口而无具体实现的框架,其调用具体的实现框架来进行日志记录。也就是说日志门面天然的兼容日志实现框架。典型的日志门面就是Commons Logging、SLF4J。

  • Commons Logging:
    Apache Commons Logging是一个基于Java的日志记录实用程序,是用于日志记录和其他工具包的编程模型。它通过其他一些工具提供API,日志实现和包装器实现。

  • SLF4J:
    Java简易日志门面(Simple Logging Facade for Java,缩写SLF4J),是一套包装Logging 框架的界面程式,以外观模式实现。可以在软件部署的时候决定要使用的 Logging 框架,目前主要支援的有Java Logging API、Log4j及logback等框架。以MIT 授权方式发布。SLF4J 的作者就是 Log4j和Logback 的作者 Ceki Gülcü.

  • Commons Logging和SLF4J实现机制:

    • Commons logging实现机制:
      Commons logging是通过动态查找机制,在程序运行时,使用自己的ClassLoader寻找和载入本地具体的实现。详细策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。由于OSGi不同的插件使用独立的ClassLoader,OSGI的这种机制保证了插件互相独立, 其机制限制了commons logging在OSGi中的正常使用。
    • Slf4j实现机制:
      Slf4j在编译期间,静态绑定本地的LOG库,因此可以在OSGi中正常使用。它是通过查找类路径下org.slf4j.impl.StaticLoggerBinder,然后绑定工作都在这类里面进。

日志门面的优点
日志门面是介于具体的日志框架与系统之间的桥梁,通过日志门面框架的应用实现了系统与具体实现日志框架的解耦。无论具体实现的日志框架如何变化,都不会影响系统日志的记录功能,更无须更改系统代码,符合开闭原则。

日志实现框架

  • JUL:Java Util Logging,自Java1.4以来的官方日志实现。

  • Log4j:Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。

  • Log4j2:Apache Log4j 2是apache开发的一款Log4j的升级产品,并且不兼容Log4j。

  • Logback:Logback是一个日志框架,与Log4j是同一作者,都出自Ceki Gülcü之手。

Java 日志框架的选择

  • 成本考虑:Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档。
  • 资源开销:Commons Logging相比较与SLF4J开销更高.
  • 性能:Logback相比Log4j、Log4j2拥有更好的性能。Logback声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在Logback中需要3纳秒,而在Log4J中则需要30纳秒。LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的。

JUL日志快速入门(不常用,了解即可)

  • JUL全称Java Util Logging,是Java原生的日志框架。使用时不需要引用第三方类库,相对于其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。

  • JUL架构:

    Application
    Logger
    Handler
    Output
    Level
    Filter
    Formatter
    • Logger:被称为日志记录器,应用程序通过获取Logger对象调用其API来发布日志信息。Logger通常是应用程序访问日志系统的入口程序
    • Handler:每个Logger都会关联一组Handler,Logger会将日志交给关联的Handler处理,由Handler负责将日志做记录。Handler在此是一个抽象,其具体的实现决定了日志记录的位置,可以是控制台、文件、网络上的其他日志服务或操作系统日志等
    • Formatter:也被称为Layout,它负责对日志事件中的数据进行转换和格式化。Formatter决定了数据在一条日志记录中的最终形式
    • Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫程度
    • Filter:过滤器,根据需要定制那些信息会被记录,哪些信息会被放过
  • JUL快速入门:

    package com.xukai;
    
    import org.junit.Test;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class JULTest {
        @Test
        public void quickstart() throws Exception {
            // 1.获取日志记录器对象
            Logger logger = Logger.getLogger("com.xukai.JULTest");
            // 2.输出日志记录
            logger.info("Hello, JUL");
    
            // 使用通用log方法进行日志记录
            logger.log(Level.INFO, "INFO Level message");
    
            // 通过占位符方式输出变量值
            String name = "Xukai";
            int age = 21;
            logger.log(Level.INFO, "用户信息:{0}, {1}", new Object[]{name, age});
        }
    }
    

    输出:

    八月 23, 2021 11:39:22 上午 com.xukai.JULTest quickstart
    信息: Hello, JUL
    八月 23, 2021 11:39:22 上午 com.xukai.JULTest quickstart
    信息: INFO Level message
    八月 23, 2021 11:39:22 上午 com.xukai.JULTest quickstart
    信息: 用户信息:Xukai, 21

  • JUL日志级别:

    /**
     * java.util.logging.Level 部分源码展示
     */
    
    // 日志开关
    public static final Level OFF = new Level("OFF", Integer.MAX_VALUE, defaultBundle);
    // 错误级别
    public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
    // 警告级别
    public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
    // 消息级别
    public static final Level INFO = new Level("INFO", 800, defaultBundle);
    // 配置信息级别
    public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
    // debug记录级别(三个不同粒度)
    public static final Level FINE = new Level("FINE", 500, defaultBundle);
    public static final Level FINER = new Level("FINER", 400, defaultBundle);
    public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
    // 日志开关
    public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
    

    可以看出JUL的日志级别有九级,分别是OFF,SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST,ALL

  • 通过配置文件方式对JUL进行配置:

    /**
       * 加载resources下的自定义配置文件
       * @throws Exception
       */
    @Test
    public void testSelfDefinedConfig() throws Exception {
        // 1.读取配置文件
        InputStream resource = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
        // 2.创建LogManager
        LogManager logManager = LogManager.getLogManager();
        // 3.通过LogManager对象加载配置文件
        logManager.readConfiguration(resource);
        // 4.创建日志记录器
        Logger logger = Logger.getLogger("com.xukai");
        // 5.记录日志
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
    

    配置文件内容:

    # RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler
    handlers = java.util.logging.ConsoleHandler
    # RootLogger 顶级父元素默认的日志级别为INFO,这里设为ALL
    .level = ALL
    
    # 对FileHandler的配置
    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
    
    # 对ConsoleHandler的配置
    java.util.logging.ConsoleHandler.level = ALL
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    

Log4j日志快速入门(几乎淘汰,了解即可)

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog 守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  • 快速入门代码示例:

    /**
       * 快速入门案例
       * @throws Exception
       */
    @Test
    public void testQuickStart() throws Exception {
        // 初始化配置信息,quickStart中暂时直接配置
        BasicConfigurator.configure();
    
        // 1. 获取日志记录器对象
        Logger logger = Logger.getLogger(Logger.class);
        // 2. 日志记录输出
        logger.info("Hello, log4j");
    }
    

    输出:

    0 [main] INFO org.apache.log4j.Logger - Hello, log4j

  • Log4j 日志级别

    • FATAL:表示需要立即被处理的系统级错误。当该错误发生时,表示服务已经出现了某种程度的不可用,系统管理员需要立即介入

    • ERROR:该级别的错误也需要马上被处理,但是紧急程度要低于FATAL级别。当ERROR错误发生时,已经影响了用户的正常访问

    • WARN:该日志表示系统可能出现问题,也可能没有,这种情况如网络的波动等。对于那些目前还不是错误,然而不及时处理也会变为错误的情况,也可以记为WARN日志,例如一个存储系统的磁盘使用量超过阈值,或者系统中某个用户的存储配额快用完等等

    • INFO:该种日志记录系统的正常运行状态,例如某个子系统的初始化,某个请求的成功执行等等

    • DEBUG or TRACE:这两种日志具体的规范应该由项目组自己定义,该级别日志的主要作用是对系统每一步的运行状态进行精确的记录。通过该种日志,可以查看某一个操作每一步的执 行过程,可以准确定位是何种操作,何种参数,何种顺序导致了某种错误的发生。可以保证在不重现错误的情况下,也可以通过DEBUG(或TRACE)级别的日志对问题进行诊断。需要注意的是,DEBUG日志也需要规范日志格式,应该保证除了记录日志的开发人员自己外,其他的如运维,测试人员等也可以通过 DEBUG(或TRACE)日志来定位问题

    日志级别优先级
    ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

    代码示例:

    /**
       * 日志级别
       * @throws Exception
       */
    @Test
    public void testLevel() throws Exception {
        BasicConfigurator.configure();
    
        Logger logger = Logger.getLogger(Logger.class);
    
        // 日志级别
        logger.fatal("fatal");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");
    }
    

    输出:

    0 [main] FATAL org.apache.log4j.Logger - fatal
    1 [main] WARN org.apache.log4j.Logger - warn
    1 [main] INFO org.apache.log4j.Logger - info
    1 [main] DEBUG org.apache.log4j.Logger - debug

    显然默认的日志级别为debug级别

  • Log4j组件

    • Appender
      Appender用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j常用的输出目的地有以下几种:

      输出端类型作用
      ConsoleAppender将日志输出到控制台
      FileAppender将日志输出到文件中
      DailyRollingFileAppender将日志输出到一个日志文件,并且每天输出到一个新的文件
      RollingFileAppender将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件
      JDBCAppender把日志信息保存到数据库中
    • Layout

      布局器 Layout 用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。Log4j常用的Layout有:

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

      Layout的格式:

      log4j 采用类似C语言的 printf 函数的打印格式格式化日志信息,在具体的占位符及含义如下:

      格式字符含义
      %m输出代码中指定的日志信息
      %p输出优先级
      %n输出换行符
      %r输出从应用启动到输出该条日志信息耗费的毫秒数
      %c输出打印语句所属的类的全限定名
      %t输出产生该日志的线程全名
      %d输出服务器当前时间,默认为ISO8601,也可自定义格式
      %l输出日志时间发生的位置,包括类名、线程及在代码中的行数,如:Test.main(Test.java:10)
      %F输出日志消息产生时所在的文件名称
      %L输出代码中的行号
      %%输出一个”%“字符

      还可以在 % 与 字符之间加上修饰符来控制最小宽度、最大宽度和文本的对齐方式:

      格式字符串含义
      %5c输出所属类的全名,最小宽度为5,不够长时右对齐
      %-5c输出所属类的全名,最小宽度为5,不够长时加空格后左对齐
      %.5c输出所属类的全名,最大宽度为5,超长时超长的部分将被截掉
      %20.30c长度小于20时补空格右对齐,长度大于30时截掉超长部分
  • 通过配置文件方式对Log4j进行配置

    /**
       * 自定义配置文件
       * @throws Exception
       */
    @Test
    public void testSelfDefinedConfig() throws Exception {
        // 默认读取了resource下的log4j.properties配置文件
        // BasicConfigurator.configure();
        Logger logger = Logger.getLogger(Logger.class);
    
        logger.fatal("fatal");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");
    }
    

    配置文件内容:

    # 配置Logger级别与类型(级别,类型)
    log4j.rootLogger = trace, console
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout = org.apache.log4j.SimpleLayout
    

commons-logging日志门面(现已淘汰,了解即可)

全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。它是为”所有的Java日志实现“提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常弱(SimpleLog),所以一般不会单独使用它。它允许开发人员使用不同的具体日志实现工具,如Log4j,JUL等。

  • 架构图

    JavaAPP
    commons-logging
    Log4j
    JUL
    SimpleLog
  • 使用日志门面框架的意义

    • 符合依赖倒置原则,面向接口开发,不再依赖具体实现,降低对日志实现框架的耦合度
    • 项目通过导入不同的日志实现类,可以灵活地切换日志框架
    • 统一API,方便开发者学习和使用
    • 统一配置便于项目日志的管理
  • commons-logging原理

    commons-logging中有两大组成部分:Log接口和LogFactory工厂类。commons-logging通过LogFactory动态加载Log实现类,可以是JULLogger、Log4jLogger等。

    源码中日志实现框架的优先级:Log4j > JUL > SimpleLog

Slf4j 日志门面(当下主流,需掌握)

  • Slf4j全称为Simple Logging Facade For Java,即Java简单日志门面。其诞生主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交给其他实现框架,如Log4j或Logback等。Slf4j自身也提供了功能较为简单的实现,但一般很少用到。对于一般的Java项目而言,日志框架会选择Slf4j-api作为门面,配上具体的实现框架(Log4j、Logback等),中间使用桥接器完成桥接。

    官网:https://www.slf4j.com

    Slf4j是当下主流的日志门面,现在的项目中基本上都是使用Slf4j作为我们的日志系统。Slf4j主要提供两大功能:

    • 日志框架的绑定
    • 日志框架的桥接
  • Slf4j入门

    • 导入Maven坐标:

      <!--    Slf4j门面    -->
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.7.5</version>
      </dependency>
      <!--    Slf4j实现    -->
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>1.7.5</version>
      </dependency>
      
    • 编写Demo:

      public class Slf4jTest {
      
          // 静态Logger对象
          public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
      
          /**
           * 快速入门
           * @throws Exception
           */
          @Test
          public void testQuickStart() throws Exception {
              LOGGER.error("error");
              LOGGER.warn("warn");
              LOGGER.info("info");
              LOGGER.debug("debug");
              LOGGER.trace("trace");
      
              // 使用占位符输出日志信息
              String name = "xukai";
              int age = 21;
              LOGGER.info("用户:{},{}", name, age);
      
              // 将系统日常信息输出
              try {
                  int i = 1/0;
              } catch (Exception e) {
                  LOGGER.error("出现异常:{}", e.getMessage());
              }
          }
      }
      
  • Slf4j日志绑定

    • Slf4j-api不绑定实现框架:输出到/dev/null,即不输出
    • Slf4j-api绑定Logback:导入logback-core.jar或logback-classic.jar
    • Slf4j-api绑定Log4j:需要适配器,先导入log4j.jar实现框架,后导入slf4j-log412.jar适配器
    • Slf4j-api绑定JUL:导入jul-to-slf4j.jar适配器即可
    • Slf4j-api绑定SimpleLog:导入slf4j-simple.jar
    • Slf4j-api绑定无操作:导入slf4j-nop.jar,输出到/dev/null(相当于日志的开关)
  • Slf4j桥接器

    • 应用场景:将老项目中的Log4j替换成Slf4j+Logback
    • 具体实现:导入log4j-over-slf4j.jar桥接器即可
    • 注意问题:
      • 桥接器和适配器不能同时出现
      • 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是Appender、Filter等对象,将无法产生效果

Logback日志框架(当下主流,需掌握)

Logback是由Log4j创始人设计的另一个开源日志组件,性能比Log4j要好

官方网站:https://logback.qos.ch/

  • Logback主要模块

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

    1. 导入Maven坐标:

      <dependencies>
      <!--    Slf4j门面    -->
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>slf4j-api</artifactId>
                  <version>1.7.5</version>
              </dependency>
          
      <!--    Logback    -->
              <dependency>
                  <groupId>ch.qos.logback</groupId>
                  <artifactId>logback-classic</artifactId>
                  <version>1.2.3</version>
              </dependency>
          </dependencies>
      
    2. 编写Demo:

      public class LogbackTest {
      
          public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
      
          /**
           * 快速入门
           * @throws Exception
           */
          @Test
          public void testQuickStart() throws Exception {
              LOGGER.error("error");
              LOGGER.warn("warn");
              LOGGER.info("info");
              LOGGER.debug("debug");
              LOGGER.trace("trace");
          }
      }
      
  • Logback配置文件

    • Logback会依次读取以下类型的配置文件(均不存在则会采用默认配置):

      • logback.groovy
      • logback-test.xml
      • logback.xml
    • Logback组件之间的关系:

      1. Logger:日志记录器,把它关联到应用的对应的context上后主要用于存放日志对象,也可以定义日志类型、级别
      2. Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等
      3. Layout:负责把事件转换成格式化的字符串日志信息。在Logback中Layout对象被封装在encoder中
    • Logback配置详解:

      <?xml version="1.0" encoding="UTF-8" ?>
      <configuration>
          <!--
              日志输出格式通过 pattern 定义:
                  %-5level
                  %d{yyyy-MM-dd HH:mm:ss.SSS}日期
                  %c类的完整名称
                  %M为method
                  %L为行号
                  %thread线程名称
                  %m或%msg为信息
                  %n换行符
            -->
          <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>
      
          <!--  指定日志文件的存放路径  -->
          <property name="log_dir" value="logs/"></property>
      
          <!--  控制台日志输出的 Appender  -->
          <appender name="console" 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  -->
          <appender name="file" class="ch.qos.logback.core.FileAppender">
              <!--    日志文件的保存路径    -->
              <file>${log_dir}/logback.log</file>
              <!--    日志消息格式配置    -->
              <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                  <pattern>${pattern}</pattern>
              </encoder>
          </appender>
      
          <!--  html格式输出的Appender  -->
          <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
              <!--    日志文件的保存路径    -->
              <file>${log_dir}/logback.html</file>
              <!--    html消息格式配置    -->
              <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                  <layout class="ch.qos.logback.classic.html.HTMLLayout">
                      <!--     html格式需要重写,不加空格与其他符号     -->
                      <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
                  </layout>
              </encoder>
          </appender>
      
          <!--  日志拆分和归档压缩的Appender对象  -->
          <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
              <!--    日志文件的保存路径    -->
              <file>${log_dir}/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_dir}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log%i.gz</fileNamePattern>
                  <!--    按照文件大小拆分    -->
                  <maxFileSize>1MB</maxFileSize>
              </rollingPolicy>
      
              <!--    日志级别过滤器    -->
              <filter class="ch.qos.logback.classic.filter.LevelFilter">
                  <!--    日志过滤级别    -->
                  <level>ERROR</level>
                  <!--    条件符合就ACCEPT    -->
                  <onMatch>ACCEPT</onMatch>
                  <!--    不符合就DENY    -->
                  <onMisMatch>DENY</onMisMatch>
              </filter>
          </appender>
      
          <!--  异步日志Appender,用于解决log4j性能瓶颈,底层是blockingQuene -->
          <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
              <!--    本身抽象,需要指定具体实现的Appender    -->
              <appender-ref ref="rollFile"/>
          </appender>
      
          <!--
              RootLogger中可以指定日志的输出级别,
              默认为DEBUG,
              可选TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF
           -->
          <root level="ALL">
              <appender-ref ref="console"/>
              <appender-ref ref="rollFile"/>
          </root>
      
          <!--
              自定义Logger
              additivity属性用于定义当前Logger对象是否继承RootLogger
           -->
          <logger name="com.xukai" level="info" additivity="false">
              <!--    指定Appender    -->
              <appender-ref ref="console"/>
          </logger>
      </configuration>
      
  • logback-access模块
    logback-access模块与Servlet容器(Tomcat和Jetty)集成,以提供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
      logback-access.xml官方配置请参考:https://logback.qos.ch/access.html#configuration

Log4j2日志框架(新项目趋势,需掌握)

  • Apache Log4j2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,一次带来了一些重大的提升,主要有:

    • 异常处理:在logback中Appender的异常不会被应用所感知,但在Log4j2中,提供了一些异常处理机制
    • 性能提升:Log4j2相较于Log4j和logback都具有很明显的性能提升
    • 热更新:参考了logback的设计,提供了自动刷新参数配置的功能,极大地方便了生产环境中配置文件的修改与更新
    • 无垃圾机制:log4j在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁地日志收集导致的JVM GC

    Log4j2官网:https://logging.apache.org/log4j/2.x/

  • Log4j2入门

    目前市面上最主流的日志门面就是Slf4j,虽然Log4j2也是日志门面,但因为它的日志实现功能非常强大,性能优越,所以开发者一般还是将Log4j2看作是日志的实现,Slf4j + Log4j2应该是未来的大势所趋。

    1. 导入Maven坐标:

      <dependencies>
          <!--    Log4j2日志门面    -->
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-api</artifactId>
              <version>2.13.3</version>
          </dependency>
      
          <!--    Log4j2日志实现    -->
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-core</artifactId>
              <version>2.13.3</version>
          </dependency>
      </dependencies>
      

      编写Demo:

      public class Log4j2Test {
      
          /**
           * 定义日志记录器对象
           */
          public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
      
          /**
           * 快速入门
           * @throws Exception
           */
          @Test
          public void testQuickStart() throws Exception {
              LOGGER.fatal("fatal");
              LOGGER.error("error");
              LOGGER.warn("warn");
              LOGGER.info("info");
              LOGGER.debug("debug");
              LOGGER.trace("trace");
          }
      }
      
  • 配置文件详解:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!--  
    	status="warn" 日志框架本身的输出日志级别
    	monitorInterval="5" 自动加载配置文件的间隔时间,不低于5秒
      -->
    <Configuration status="warn" monitorInterval="5">
    
        <!--  集中配置属性进行管理,使用时通过el表达式(${name})就可以  -->
        <properties>
            <property name="LOG_HOME">F:/JavaProject/LogFrameWorks/Log4j2/logs</property>
        </properties>
    
        <Appenders>
            <!--  控制台输出的Appender  -->
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
            </Console>
    
          <!--  日志文件输出的Appender  -->  
            <File name="File" fileName="${LOG_HOME}/myfile.log">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
            </File>
    
            <!--  随机读写流的日志文件输出Apender,性能大幅提升  -->
            <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
            </RandomAccessFile>
    
            <!--  按照策略拆分日志文件的Appender  -->
            <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                         filePattern="logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
                <!--  日志级别过滤器  -->
                <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
                <!--  日志消息格式  -->
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
                <Policies>
                    <!--  在系统启动时触发拆分规则,生成新的日志文件  -->
                    <OnStartupTriggeringPolicy/>
                    <!--  按照文件大小进行拆分  -->
                    <SizeBasedTriggeringPolicy size="10 MB"/>
                    <!--  按照时间结点进行拆分  -->  
                    <TimeBasedTriggeringPolicy/>
                </Policies>
                <!--  在同一个目录下文件的个数限定为30个,超过则覆盖老的日志  -->
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
        </Appenders>
        <!--  Logger定义  -->
        <Loggers>
            <Root level="trace">
                <!--  指定控制台处理器  -->
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    
  • 异步日志:
    Log4j2提供了两种实现日志的方式,一种是通过AsyncAppender,另一种是通过AsyncLogger,分别对应Appender和Logger两个组件,但AsyncLogger最常用,也是官方推荐的异步方式

    • 异步日志需要导入依赖:

      <dependency>
          <groupId>com.lmax</groupId>
          <artifactId>disruptor</artifactId>
          <version>3.3.4</version>
      </dependency>
      
    • 修改配置文件

      <?xml version="1.0" encoding="UTF-8" ?>
      <Configuration status="warn" monitorInterval="5">
      
          <properties>
              <property name="LOG_HOME">F:/JavaProject/LogFrameWorks/Log4j2/logs</property>
          </properties>
      
              <File name="File" fileName="${LOG_HOME}/myfile.log">
                  <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
              </File>
      
              <!--    指定异步的Appender    -->
              <Async name="Async">
                  <AppenderRef ref="file"/>
              </Async>
          </Appenders>
      
          <Loggers>
              <Root level="trace">
                  <AppenderRef ref="File"/>
                  <!--    启用异步    -->
                  <AppenderRef ref="Async"/>
              </Root>
              
              <!--    
      			自定义AsyncLogger
         			includeLocation="false" 关闭日志记录的行号信息
      			additivity="false" 不再继承RootLogger
       			-->
              <AsyncLogger name="com.xukai" level="trace" includeLocation="false" additivity="false">
                  <AppenderRef ref="File"/>
              </AsyncLogger>
          </Loggers>
      </Configuration>
      
    • 使用异步日志需要注意的问题:

      • 如果使用异步日志,AsyncAppender、AsyncLogger和全局日志不要同时出现。否则性能会降到最低
      • 设置includeLocation=false,打印位置信息会急剧降低异步日志的性能,比同步日志还慢
  • Log4j2无垃圾记录

    垃圾收集暂停是影响性能的常见原因。许多日志框架在稳态日志记录期间分配临时对象,如日志事件对象、字符串、字符数组、字节数组等。这会对JVM的垃圾回收器造成压力并增加GC暂停发生的频率

    从版本2.6开始,默认情况下Log4j以”无垃圾模式“运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。此外还有一个”低垃圾模式“,它不是完全没有垃圾,但不使用ThreadLocal字段

    Log4j2.6中无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现

    Springboot中的日志使用

    Springboot框架在企业中使用越来越普遍,Springboot日志也是开发中最常用的日志系统。Springboot默认使用Slf4j作为日志门面、Logback作为日志实现来记录日志

    • Springboot中的日志设计

      • 导入Maven坐标:

        <dependency>
            <artifactId>spring-boot-starter-logging</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>
        
      • 依赖关系图:

        spring-boot-starter-logging
        log4j-to-slf4j
        logback-classic
        jul-to-slf4j
        log4j-api
        logback-core
        slf4j-api

        可以看出:

        1. Springboot底层默认使用logback作为日志实现
        2. 使用了slf4j作为日志门面
        3. 将JUL也转换成了slf4j
        4. 也可以使用log4j2作为日志门面,但是最终也是通过桥接器转换成slf4j调用logback
  • Springboot日志配置:

    Application.yml中的基本配置:

    logging:
    	level:
    		com.xukai: true
    	pattern:
    		console:  "%d - %msg%n"
    		file: "[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] - %msg %n"
    	file:
    		path: logs/
    

    指定配置:
    给类路径下放上每个日志框架自己的配置文件,Springboot就将不再使用默认配置

    日志框架配置文件
    Logbacklogback-spring.xml / logback.xml
    Log4j2log4j2-spring.xml / log4j2.xml
    JULlogging.properties
  • Springboot将日志框架切换为log4j2

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--  排除默认的logback  -->  
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!--  添加log4j2依赖  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
    </dependencies>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值