zoukankan      html  css  js  c++  java
  • 【一篇文章就够了】Java日志框架

    我们常用的日志框架有Log4j、Log4j 2、Log4j 2和JUL(Java Util Logging)。除了日志框架,常用的还有日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用),Commons Logging和Slf4j

    要想了解这些日志,就要先了解他们的历史。

    • 1996年,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员,这让 Log4j 近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但Sun拒绝了。

    • 2002年JDK1.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ü(Log4j的主要贡献者)不适应Apache的工作方式,离开了Apache。然后先后创建了Slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现,类似于Log4j)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。

    • 此后,Java日志领域被划分为两大阵营:Commons Logging阵营和Slf4j阵营。虽然Commons Logging在Apache大树的笼罩下,有很大的用户基数,但证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出Slf4j的发展趋势更好:

    • 2012年7月,Apache眼看有被Logback反超的势头,重写了Log4j 1.x,成立了新的项目Log4j 2,Log4j 2具有Logback的所有特性。

    看完这些之后是不是就理解这些框架的意义了。

    日志小刀: JUL(Java Util Logging)

    JUL是JDK官方提供的一个记录日志的方式,直接在JDK中就可以使用。

    import java.util.logging.Logger;
    
    public class JDKLog {
        public static void main( String[] args )
        {
            Logger logger = Logger.getLogger("JDKLog");
            logger.info("Hello World.");
        }
    }
    

    非常简单,但是又因为太简单了,所以功能不够强大,用的很少了。

    日志大炮:Log4J

    Log4J 有 1.X 版本和 2.X 版本,两个版本相互不兼容,官方推荐使用 2.X 版本。

    使用 Log4J 框架首先需要引入依赖的包:

            <!-- Log4J -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.6.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.6.2</version>
            </dependency>
    

    配置文件log4j2.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN">
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            </Console>
        </Appenders>
        <Loggers>
            <Root level="info">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    

    如果没有配置 log4j2.xml 配置文件,那么Log4j将自动启用类似于下面的的配置文件,只输出Error级别的日志。
    测试类:

    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    public class Log4jLog {
        public static void main(String args[]) {
            Logger logger = LogManager.getLogger(Log4jLog.class);
            logger.debug("Debug Level");
            logger.info("Info Level");
            logger.warn("Warn Level");
            logger.error("Error Level");
        }
    }
    

    控制台输出:

    Connected to the target VM, address: '127.0.0.1:65086', transport: 'socket'
    17:26:15.302 [main] INFO  log.Log4jLog - Info Level
    17:26:15.316 [main] WARN  log.Log4jLog - Warn Level
    17:26:15.316 [main] ERROR log.Log4jLog - Error Level
    Disconnected from the target VM, address: '127.0.0.1:65086', transport: 'socket'
    
    Process finished with exit code 0
    

    因为 Log4J 有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,所以可以很好地记录不同业务问题。因为这些优点,所以在几年前几乎所有人都使用 Log4J 作为日志记录框架,群众基础可谓非常深厚。但 Log4J 本身也存在一些缺点,比如不支持使用占位符,不利于代码阅读等缺点。

    LogBack:日志火箭

    LogBack 其实可以说是 Log4j 的进化版,因为它们两个都是同一个人(Ceki Gülcü)设计的开源日志组件。

    使用 LogBack 需要首先引入依赖:

    <!-- LogBack -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.7</version>
    </dependency>
    

    配置文件log4j2.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <layout class="ch.qos.logback.classic.PatternLayout">
                <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
            </layout>
        </appender>
        <logger name="com.chanshuyi" level="TRACE"/>
     
        <root level="debug">
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>
    

    如果没有配置 log4j2.xml 配置文件,那么Log4j将自动启用类似于下面的的配置文件,只输出Error级别的日志。
    测试类:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class LogBackLog {
    
        static final Logger logger = LoggerFactory.getLogger(LogBackLog.class);
        public static void main(String[] args) {
            logger.trace("Trace Level.");
            logger.debug("Debug Level.");
            logger.info("Info Level.");
            logger.warn("Warn Level.");
            logger.error("Error Level.");
        }
    }
    

    适配器:SLF4J

    上面说了 JDKLog、Log4J、LogBack 这几个常用的日志记录框架,它们都有各自的优缺点,适合在不同的场景下使用。可能简单的项目直接用 JDKLog 就可以了,而复杂的项目需要用上 Log4J。

    很多时候我们做项目都是从简单到复杂,也就是我们很可能一开始使用的是 JDKLog,之后业务复杂了需要使用 Log4J,这时候我们如何将原来写好的日志用新的日志框架输出呢?

    一个最死板的方法就是一行行代码修改,把之前用 JDKLog 的日志代码全部修改成 Log4J 的日志接口。但是这种方式不仅效率低下,而且做的工作都是重复性的工作,这怎么能忍呢。

    正式因为在实际的项目应用中,有时候可能会从一个日志框架切换到另外一个日志框架的需求,这时候往往需要在代码上进行很大的改动。为了避免切换日志组件时要改动代码,这时候一个叫做 SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)的东西出现了。

    SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)是一个日志的接口规范,它对用户提供了统一的日志接口,屏蔽了不同日志组件的差异。这样我们在编写代码的时候只需要看 SLF4J 这个接口文档即可,不需要去理会不同日之框架的区别。而当我们需要更换日志组件的时候,我们只需要更换一个具体的日志组件Jar包就可以了。

    而整合 SLF4J 和日志框架使用也是一件很简单的事情。

    SLF4J+JUL

    SLF4J+JUL 需要在 Maven 中导入以下依赖包:

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
      <version>1.7.21</version>
    </dependency>
    

    SLF4J+LOG4J

    SLF4J+LOG4J 需要在 Maven 中导入以下依赖包有 slf4j-api.jar、slf4j-412.jar、log4j.jar :

    <!-- 2.SLF4J + Log4J -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    

    SLF4J+LogBack

    SLF4J+LogBack 需要在 Maven 中导入以下依赖包:

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.7</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.1.7</version>
    </dependency>
    

    logback 常用配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration debug="true" scan="true" scanPeriod="30 seconds"> 
    
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
        <!-- encoders are  by default assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>
            
            <!-- 常用的Pattern变量,大家可打开该pattern进行输出观察 -->
            <!-- 
              <pattern>
                  %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
                  Logger: %logger
                  Class: %class
                  File: %file
                  Caller: %caller
                  Line: %line
                  Message: %m
                  Method: %M
                  Relative: %relative
                  Thread: %thread
                  Exception: %ex
                  xException: %xEx
                  nopException: %nopex
                  rException: %rEx
                  Marker: %marker
                  %n
                  
              </pattern>
               -->
        </encoder>
      </appender>
      
      <!-- 按日期区分的滚动日志 -->
      <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>logs/error.log</file>
          
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
        </encoder>
          
          <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>ERROR</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
        </filter>
          
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- daily rollover -->
          <fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
    
          <!-- keep 30 days' worth of history -->
          <maxHistory>30</maxHistory>
        </rollingPolicy>
      </appender>
      
      <!-- 按文件大小区分的滚动日志 -->
      <appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>logs/info.log</file>
          
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
        </encoder>
        
          <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>INFO</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
        </filter>
          
          <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
          <fileNamePattern>info.%i.log</fileNamePattern>
          <minIndex>1</minIndex>
          <maxIndex>3</maxIndex>
        </rollingPolicy>
        
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
          <maxFileSize>5MB</maxFileSize>
        </triggeringPolicy>
        
      </appender>
      
      
      <!-- 按日期和大小区分的滚动日志 -->
      <appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>logs/debug.log</file>
    
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
        </encoder>
          
          <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>DEBUG</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
        </filter>
          
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- rollover daily -->
          <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
          
          <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <!-- or whenever the file size reaches 100MB -->
            <maxFileSize>100MB</maxFileSize>
          </timeBasedFileNamingAndTriggeringPolicy>
          
        </rollingPolicy>
        
      </appender>
      
      
       <!-- 级别阀值过滤 -->
      <appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>logs/sum.log</file>
    
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
        </encoder>
          
        <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
          <level>INFO</level>
        </filter>
    
          
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- rollover daily -->
          <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
          
          <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <!-- or whenever the file size reaches 100MB -->
            <maxFileSize>100MB</maxFileSize>
          </timeBasedFileNamingAndTriggeringPolicy>
          
        </rollingPolicy>
        
      </appender>
      
      
      <root level="debug">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="ERROR-OUT" />
        <appender-ref ref="INFO-OUT" />
        <appender-ref ref="DEBUG-OUT" />
        <appender-ref ref="SUM-OUT" />
      </root>
    </configuration>
    

    参考文献

    https://www.cnblogs.com/chanshuyi/p/something_about_java_log_framework.html
    https://www.cnblogs.com/chenhongliang/p/5312517.html
    https://segmentfault.com/a/1190000021589244
    https://segmentfault.com/a/1190000006925098?utm_source=sf-similar-article

  • 相关阅读:
    AD20改变pcb图纸大小方式
    ceph相关概念
    五种IO模型和三种实现方式
    MongoDB入门
    GO通过sqlx库操作MySQL
    Go原生sql操作MySQL
    Traefik工作原理
    Redis主从
    Nginx入门
    Redis入门
  • 原文地址:https://www.cnblogs.com/zllk/p/14899922.html
Copyright © 2011-2022 走看看