精华内容
下载资源
问答
  • java日志框架详解
    万次阅读
    2018-09-21 18:23:51

    最全Java日志框架详解 

    在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此对于一个项目而言,日志记录是一个非常重要的问题。因

    此,如何选择一个合适的日志记录框架也非常重要。

    在Java开发中,常用的日志记录框架有JDKLog、Log4J、LogBack、SLF4J、SLF4J。这些日志记录框架各有各的特点,各有各的应用场景。了解这些框架的特点及应用场景,有

    利于我们做技术选型的时候做出正确的判断。

    JDKLog:日志小刀

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

     

     
    1. import java.util.logging.Logger;

    2. /****

    3. ** JDKLog Demo

    4. **/

    5. public class JDKLog

    6. {

    7. public static void main( String[] args )

    8. {

    9. Logger logger = Logger.getLogger("JDKLog");

    10. logger.info("Hello World.");

    11. }

    12. }

    JDKLog 的有点是使用非常简单,直接在 JDK 中就可以使用。但 JDKLog 功能比较太过于简单,不支持占位符显示,拓展性比较差,所以现在用的人也很少。

     

    Log4J:日志大炮

    Log4J 是 Apache 的一个日志开源框架,有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,可以很好地将不同日志级别的日志分开记录,极大地方便了日志的查看。

    Log4J 有 1.X 版本和 2.X 版本,现在官方推荐使用 2.X 版本,2.X 版本在架构上进行了一些升级,配置文件也发生了一些变化。但好在官方的配置说明文档非常清楚,通过查阅

    文档能解决大部分的问题。

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

     

     
    1. <!-- Log4J -->

    2. <dependency>

    3. <groupId>org.apache.logging.log4j</groupId>

    4. <artifactId>log4j-api</artifactId>

    5. <version>2.6.2</version>

    6. </dependency>

    7. <dependency>

    8. <groupId>org.apache.logging.log4j</groupId>

    9. <artifactId>log4j-core</artifactId>

    10. <version>2.6.2</version>

    11. </dependency>

    增加配置文件 log4j2.xml 放在 resource 目录下:

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <Configuration status="WARN">

    3. <Appenders>

    4. <Console name="Console" target="SYSTEM_OUT">

    5. <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>

    6. </Console>

    7. </Appenders>

    8. <Loggers>

    9. <Root level="info">

    10. <AppenderRef ref="Console"/>

    11. </Root>

    12. </Loggers>

    13. </Configuration>

    其中节点的 level 属性表示输出的最低级别。

     

    最后编写一个测试类:

     

     
    1. import org.apache.logging.log4j.LogManager;

    2. import org.apache.logging.log4j.Logger;

    3. /****

    4. ** Log4J Demo

    5. **/

    6. public class Log4jLog {

    7. public static void main(String args[]) {

    8. Logger logger = LogManager.getLogger(Log4jLog.class);

    9. logger.debug("Debug Level");

    10. logger.info("Info Level");

    11. logger.warn("Warn Level");

    12. logger.error("Error Level");

    13. }

    14. }

    运行测试类输出结果:

     

    10:16:08.279 [main] INFO com.chanshuyi.Log4jLog - Info Level
    10:16:08.280 [main] WARN com.chanshuyi.Log4jLog - Warn Level
    10:16:08.280 [main] ERROR com.chanshuyi.Log4jLog - Error Level
    如果没有配置 log4j2.xml 配置文件,那么LOG4J将自动启用类似于下面的的配置文件:

     

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <Configuration status="WARN">

    3. <Appenders>

    4. <Console name="Console" target="SYSTEM_OUT">

    5. <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>

    6. </Console>

    7. </Appenders>

    8. <Loggers>

    9. <Root level="error">

    10. <AppenderRef ref="Console"/>

    11. </Root>

    12. </Loggers>

    13. </Configuration>

    使用默认配置文件的输出结果:
    ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
    11:40:07.377 [main] ERROR com.chanshuyi.Log4jLog - Error Level

     

    从上面的使用步骤可以看出 Log4J 的使用稍微复杂一些,但是条理还是很清晰的。而且因为 Log4J 有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,所以可以很好地记

    录不同业务问题。因为这些优点,所以在几年前几乎所有人都使用 Log4J 作为日志记录框架,群众基础可谓非常深厚。

    但 Log4J 本身也存在一些缺点,比如不支持使用占位符,不利于代码阅读等缺点。但是相比起 JDKLog,Log4J 可以说是非常好的日志记录框架了。

    LogBack:日志火箭

    LogBack 其实可以说是 Log4J 的进化版,因为它们两个都是同一个人(Ceki Gülcü)设计的开源日志组件。LogBack 除了具备 Log4j 的所有优点之外,还解决了 Log4J 不能

    使用占位符的问题。

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

     

     
    1. <!-- LogBack -->

    2. <dependency>

    3. <groupId>ch.qos.logback</groupId>

    4. <artifactId>logback-classic</artifactId>

    5. <version>1.1.7</version>

    6. </dependency>

    配置 logback.xml 配置文件:

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <configuration>

    3. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    4. <layout class="ch.qos.logback.classic.PatternLayout">

    5. <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

    6. </layout>

    7. </appender>

    8. <logger name="com.chanshuyi" level="TRACE"/>

    9. <root level="debug">

    10. <appender-ref ref="STDOUT" />

    11. </root>

    12. </configuration>

    LogBack 的日志级别区分可以细分到类或者包,这样就可以使日志记录变得更加灵活。之后在类文件中引入Logger类,并进行日志记录:

     
    1. import org.slf4j.Logger;

    2. import org.slf4j.LoggerFactory;

    3. /****

    4. ** LogBack Demo

    5. **/

    6. public class LogBack {

    7. static final Logger logger = LoggerFactory.getLogger(LogBack.class);

    8. public static void main(String[] args) {

    9. logger.trace("Trace Level.");

    10. logger.debug("Debug Level.");

    11. logger.info("Info Level.");

    12. logger.warn("Warn Level.");

    13. logger.error("Error Level.");

    14. }

    15. }

    输出结果:

     

    14:34:45.747 [main] TRACE com.chanshuyi.LogBack - Trace Level.
    14:34:45.749 [main] DEBUG com.chanshuyi.LogBack - Debug Level.
    14:34:45.749 [main] INFO com.chanshuyi.LogBack - Info Level.
    14:34:45.749 [main] WARN com.chanshuyi.LogBack - Warn Level.
    14:34:45.749 [main] ERROR com.chanshuyi.LogBack - Error Level.

    LogBack 解决了 Log4J 不能使用占位符的问题,这使得阅读日志代码非常方便。除此之外,LogBack 比 Log4J 有更快的运行速度,更好的内部实现。并且 LogBack 内部集成

    了 SLF4J 可以更原生地实现一些日志记录的实现。

    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+JDKLog

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

     

     
    1. <dependency>

    2. <groupId>org.slf4j</groupId>

    3. <artifactId>slf4j-api</artifactId>

    4. <version>1.7.21</version>

    5. </dependency>

    6. <dependency>

    7. <groupId>org.slf4j</groupId>

    8. <artifactId>slf4j-jdk14</artifactId>

    9. <version>1.7.21</version>

    10. </dependency>

    编写测试类:

     
    1. import org.slf4j.Logger;

    2. import org.slf4j.LoggerFactory;

    3. /****

    4. ** SLF4J + JDKLog

    5. **/

    6. public class Slf4jJDKLog {

    7. final static Logger logger = LoggerFactory.getLogger(Slf4jJDKLog.class);

    8. public static void main(String[] args) {

    9. logger.trace("Trace Level.");

    10. logger.info("Info Level.");

    11. logger.warn("Warn Level.");

    12. logger.error("Error Level.");

    13. }

    14. }

    输出结果:
    七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
    信息: Info Level.
    七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
    警告: Warn Level.
    七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main
    严重: Error Level.

     

    SLF4J+LOG4J

    需要依赖的 Jar 包:slf4j-api.jar、slf4j-412.jar、log4j.jar,导入Maven依赖:

     

     
    1. <!-- 2.SLF4J + Log4J -->

    2. <dependency>

    3. <groupId>org.slf4j</groupId>

    4. <artifactId>slf4j-api</artifactId>

    5. <version>1.7.21</version>

    6. </dependency>

    7. <dependency>

    8. <groupId>org.slf4j</groupId>

    9. <artifactId>slf4j-log4j12</artifactId>

    10. <version>1.7.21</version>

    11. </dependency>

    12. <dependency>

    13. <groupId>log4j</groupId>

    14. <artifactId>log4j</artifactId>

    15. <version>1.2.17</version>

    16. </dependency>

    配置 log4j.xml 文件:

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

    3. <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' >

    4. <appender name="myConsole" class="org.apache.log4j.ConsoleAppender">

    5. <layout class="org.apache.log4j.PatternLayout">

    6. <param name="ConversionPattern"

    7. value="[%d{dd HH:mm:ss,SSS} %-5p] [%t] %c{2} - %m%n" />

    8. </layout>

    9. <!--过滤器设置输出的级别-->

    10. <filter class="org.apache.log4j.varia.LevelRangeFilter">

    11. <param name="levelMin" value="debug" />

    12. <param name="levelMax" value="error" />

    13. <param name="AcceptOnMatch" value="true" />

    14. </filter>

    15. </appender>

    16. <!-- 根logger的设置-->

    17. <root>

    18. <priority value ="debug"/>

    19. <appender-ref ref="myConsole"/>

    20. </root>

    21. </log4j:configuration>

    我们还是用上面的代码,无需做改变,运行结果为:
    [15 16:04:06,371 DEBUG] [main] slf4j.SLF4JLog - Debug Level.
    [15 16:04:06,371 INFO ] [main] slf4j.SLF4JLog - Info Level.
    [15 16:04:06,371 WARN ] [main] slf4j.SLF4JLog - Warn Level.
    [15 16:04:06,371 ERROR] [main] slf4j.SLF4JLog - Error Level.
    SLF4J+LogBack

     

    导入依赖:

     

     
    1. <dependency>

    2. <groupId>org.slf4j</groupId>

    3. <artifactId>slf4j-api</artifactId>

    4. <version>1.7.21</version>

    5. </dependency>

    6. <dependency>

    7. <groupId>ch.qos.logback</groupId>

    8. <artifactId>logback-classic</artifactId>

    9. <version>1.1.7</version>

    10. </dependency>

    11. <dependency>

    12. <groupId>ch.qos.logback</groupId>

    13. <artifactId>logback-core</artifactId>

    14. <version>1.1.7</version>

    15. </dependency>

    配置 logback.xml 文件:

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <configuration>

    3. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    4. <layout class="ch.qos.logback.classic.PatternLayout">

    5. <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

    6. </layout>

    7. </appender>

    8. <logger name="com.chanshuyi" level="TRACE"/>

    9. <root level="warn">

    10. <appender-ref ref="STDOUT" />

    11. </root>

    12. </configuration>

    我们还是用上面的代码,无需做改变,运行结果为:
    16:08:01.040 [main] TRACE com.chanshuyi.slf4j.SLF4JLog - Trace Level.
    16:08:01.042 [main] DEBUG com.chanshuyi.slf4j.SLF4JLog - Debug Level.
    16:08:01.043 [main] INFO com.chanshuyi.slf4j.SLF4JLog - Info Level.
    16:08:01.043 [main] WARN com.chanshuyi.slf4j.SLF4JLog - Warn Level.
    16:08:01.043 [main] ERROR com.chanshuyi.slf4j.SLF4JLog - Error Level.

     

    LogBack日志框架

    经过上面的介绍,相信大家对 Java 常用的日志框架都有了一定认识。

    那么在实际使用中到底选择哪种日志框架合适呢?

    按笔者理解,现在最流的日志框架解决方案莫过于SLF4J + LogBack。原因有下面几点:

    LogBack 自身实现了 SLF4J 的日志接口,不需要 SLF4J 去做进一步的适配。

    LogBack 自身是在 Log4J 的基础上优化而成的,其运行速度和效率都比 LOG4J 高。

    SLF4J + LogBack 支持占位符,方便日志代码的阅读,而 LOG4J 则不支持。

    从上面几点来看,SLF4J + LogBack是一个较好的选择。

    LogBack 被分为3个组件:logback-core、logback-classic 和 logback-access。

    logback-core 提供了 LogBack 的核心功能,是另外两个组件的基础。

    logback-classic 则实现了 SLF4J 的API,所以当想配合 SLF4J 使用时,需要将 logback-classic 引入依赖中。

    logback-access 是为了集成Servlet环境而准备的,可提供HTTP-access的日志接口。

    LogBack的日志记录数据流是从 Class(Package)到 Logger,再从Logger到Appender,最后从Appender到具体的输出终端。

    LogBack配置文件可以分为几个节点,其中 Configuration 是根节点,Appender、Logger、Root是Configuration的子节点。

    appender节点

    是的子节点,是负责写日志的组件。appender有两个必要属性name、class 。name指定appender的名称,class指定appender的全限定名class,主要包括:
    ch.qos.logback.core.ConsoleAppender 控制台输出
    ch.qos.logback.core.FileAppender 文件输出
    ch.qos.logback.core.RollingFileAppender 文件滚动输出

     

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration debug="true" scan="true" scanPeriod="2">

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. </appender>

    6. <!-- conf file out -->

    7. <appender name="file_out" class="ch.qos.logback.core.FileAppender">

    8. </appender>

    9. <!-- conf file out -->

    10. <appender name="file_out" class="ch.qos.logback.core.RollingFileAppender">

    11. </appender>

    12. <root></root>

    13. <logger></logger>

    14. </configuration>

    ConsoleAppender
    把日志添加到控制台,有如下节点:

     

    <encoder> : 对日志进行格式化。

    <target> : 字符串System.out 或者 System.err, 默认 System.out;

     

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <encoder>

    6. <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>

    7. </encoder>

    8. </appender>

    9. <root level="INFO">

    10. <appender-ref ref="console_out" />

    11. </root>

    12. </configuration>

    FileAppender
    把日志添加到文件,有如下节点:
    <file>:被写入的文件名,可以是相对目录 , 也可以是绝对目录 , 如果目录不存在则会自动创建。

     

    <append>:如果是true , 日志被追加到文件结尾 , 如果是false,清空现存文件 , 默认是true。

    <encoder>:对日志进行格式化 [具体的转换符说明请参见官网.]

     

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <appender name="file_out" class="ch.qos.logback.core.FileAppender">

    4. <file>logs/debug.log</file>

    5. <encoder>

    6. <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>

    7. </encoder>

    8. </appender>

    9. </configuration>

    rollingFileAppender
    滚动纪录文件,先将日志记录到指定文件,当符合某种条件时,将日志记录到其他文件,有如下节点:
    <file>:被写入的文件名,可以是相对目录,也可以解决目录,如果目录不存在则自动创建。
    <append>:如果是true,日志被追加到文件结尾,如果是false,清空现存文件,默认是true。
    <encoder>:对日志进行格式化。
    <rollingPolicy>:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。
    rollingPolicy
    TimeBaseRollingPolicy :最常用的滚动策略,根据时间来制定滚动策略,即负责滚动也负责触发滚动。有如下节点;
    <fileNamePattern>:必要节点,包含文件及“%d” 转换符,“%d”可以包含一个java.text.SimpleDateFormat 制定的时间格式,如:%d{yyyy-MM},如果直接使用%d ,默认格式是 yyyy-MM-dd。
    <maxHistory>:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,且 是 6,则只保存最近6个月的文件,删除之前的旧文件,注意:删除旧文件是哪些为了归档而创建的目录也会被删除。
    <filenamePattern>:必须包含“%i” 例如:设置最小值,和最大值分别为1和2,命名模式为 log%i.log,会产生归档文件log1.log和log2.log,还可以指定文件压缩选项,例如:log%i.log.gz 或者 log%i.log.zip
    triggeringPolicy:告知RollingFileAppender,激活RollingFileAppender滚动。

     

     

     
    1. <!-- 03:conf errorAppender out -->

    2. <appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">

    3. <file>logs/error.log</file>

    4. <!-- 设置滚动策略 -->

    5. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    6. <!--设置日志命名模式-->

    7. <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>

    8. <!--最多保留30天log-->

    9. <maxHistory>30</maxHistory>

    10. </rollingPolicy>

    11. <!-- 超过150MB时,触发滚动策略 -->

    12. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    13. <maxFileSize>150</maxFileSize>

    14. </triggeringPolicy>

    15. <encoder>

    16. <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>

    17. </encoder>

    18. </appender>

    logger节点
    logger是的子节点,来设置某一个包或者具体的某一个类的日志打印级别,以及指定。logger仅有一个name属性,两个可选属性 level/addtivity。
    name:用来指定受此loger约束的某一个包或者具体的某一个类。
    level:用来设置打印级别,大小写无关。可选值有TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。还有一个特俗值INHERITED 或者 同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别。
    addtivity:是否向上级logger传递打印信息,默认为true;
    可以包含零个或多个元素,表示这个appender将会添加到logger。

     

     

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    6. <!-- 过滤掉非INFO级别 -->

    7. <level>INFO</level>

    8. <onMatch>ACCEPT</onMatch>

    9. <onMismatch>DENY</onMismatch>

    10. </filter>

    11. </appender>

    12. <!-- conf infoAppender out -->

    13. <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">

    14. <file>logs/info.log</file>

    15. <!-- 设置滚动策略 -->

    16. <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    17. <!--设置日志命名模式-->

    18. <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>

    19. <!--最多保留30天log-->

    20. <maxHistory>30</maxHistory>

    21. </rollingPoliy>

    22. <!-- 超过150MB时,触发滚动策略 -->

    23. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    24. <maxFileSize>150</maxFileSize>

    25. </triggeringPolicy>

    26. <encoder>

    27. <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>

    28. </encoder>

    29. </appender>

    30. <!-- 添加两个appender节点 -->

    31. <logger name="logback.olf.log" level="info">

    32. <appender-ref ref = "console_out"/>

    33. <appender-ref ref = "infoAppender"/>

    34. </logger>

    35. </configuration>

    root节点
    元素配置根logger。该元素有一个level属性,没有name属性,因为已经被命名 为root。Level属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、 WARN、ERROR、ALL 和 OFF。如果 root 元素没 有引用任何 appender,就会失去所有 appender。

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    6. <!-- 过滤掉非INFO级别 -->

    7. <level>INFO</level>

    8. <onMatch>ACCEPT</onMatch>

    9. <onMismatch>DENY</onMismatch>

    10. </filter>

    11. </appender>

    12. <!-- 01:conf infoAppender out -->

    13. <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">

    14. <file>logs/info.log</file>

    15. <!-- 设置滚动策略 -->

    16. <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    17. <!--设置日志命名模式-->

    18. <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>

    19. <!--最多保留30天log-->

    20. <maxHistory>30</maxHistory>

    21. </rollingPoliy>

    22. <!-- 超过150MB时,触发滚动策略 -->

    23. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    24. <maxFileSize>150</maxFileSize>

    25. </triggeringPolicy>

    26. <encoder>

    27. <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>

    28. </encoder>

    29. </appender>

    30. <!-- 02:conf debugAppender out -->

    31. <appender name="debugAppender" class="ch.qos.logback.core.RollingFileAppender">

    32. <file>logs/debug.log</file>

    33. <!-- 设置滚动策略 -->

    34. <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    35. <!--设置日志命名模式-->

    36. <fileNamePattern>debugFile.%d{yyyy-MM-dd}.log</fileNamePattern>

    37. <!--最多保留30天log-->

    38. <maxHistory>30</maxHistory>

    39. </rollingPoliy>

    40. <!-- 超过150MB时,触发滚动策略 -->

    41. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    42. <maxFileSize>150</maxFileSize>

    43. </triggeringPolicy>

    44. <encoder>

    45. <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>

    46. </encoder>

    47. </appender>

    48. <!-- 03:conf errorAppender out -->

    49. <appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">

    50. <file>logs/error.log</file>

    51. <!-- 设置滚动策略 -->

    52. <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    53. <!--设置日志命名模式-->

    54. <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>

    55. <!--最多保留30天log-->

    56. <maxHistory>30</maxHistory>

    57. </rollingPoliy>

    58. <!-- 超过150MB时,触发滚动策略 -->

    59. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    60. <maxFileSize>150</maxFileSize>

    61. </triggeringPolicy>

    62. <encoder>

    63. <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>

    64. </encoder>

    65. </appender>

    66. <root level="ALL">

    67. <appender-ref ref="infoAppender"/>

    68. <appender-ref ref="debugAppender"/>

    69. <appender-ref ref="errorAppender"/>

    70. </root>

    71. </configuration>

    filter过滤节点
    级别过滤器(LevelFilter)
    LevelFilter 根据记录级别对记录事件进行过滤。如果事件的级别等于配置的级别,过滤 器会根据 onMatch 和 onMismatch 属性接受或拒绝事件。下面是个配置文件例子:

     

     

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    6. <!-- 过滤掉非INFO级别 -->

    7. <level>INFO</level>

    8. <onMatch>ACCEPT</onMatch>

    9. <onMismatch>DENY</onMismatch>

    10. </filter>

    11. <encoder>

    12. <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>

    13. </encoder>

    14. </appender>

    15. <root level="DEBUG">

    16. <appender-ref ref="console_out" />

    17. </root>

    18. </configuration>

    临界值过滤器(ThresholdFilter)
    ThresholdFilter过滤掉低于指定临界值的事件。

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

    6. <!-- 过滤掉TRACE和DEBUG级别的日志 -->

    7. <level>INFO</level>

    8. </filter>

    9. <encoder>

    10. <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>

    11. </encoder>

    12. </appender>

    13. <root level="DEBUG">

    14. <appender-ref ref="console_out" />

    15. </root>

    16. </configuration>

    求值过滤器(EvaluatorFilter)
    评估是否符合指定的条件

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">

    6. <evaluator>

    7. <!--过滤掉所有日志中不包含hello字符的日志-->

    8. <expression>

    9. message.contains("hello")

    10. </expression>

    11. <onMatch>NEUTRAL</onMatch>

    12. <onMismatch>DENY</onMismatch>

    13. </evaluator>

    14. </filter>

    15. <encoder>

    16. <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>

    17. </encoder>

    18. </appender>

    19. <root level="DEBUG">

    20. <appender-ref ref="console_out" />

    21. </root>

    22. </configuration>

    匹配器(Matchers)

     
    1. <?xml version="1.0" encoding="utf-8"?>

    2. <configuration>

    3. <!-- conf consoel out -->

    4. <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">

    5. <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">

    6. <evaluator>

    7. <matcher>

    8. <Name>odd</Name>

    9. <!-- 过滤掉序号为奇数的语句-->

    10. <regex>statement [13579]</regex>

    11. </matcher>

    12. <expression>odd.matches(formattedMessage)</expression>

    13. <onMatch>NEUTRAL</onMatch>

    14. <onMismatch>DENY</onMismatch>

    15. </evaluator>

    16. </filter>

    17. <encoder>

    18. <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>

    19. </encoder>

    20. </appender>

    21. <root level="DEBUG">

    22. <appender-ref ref="console_out" />

    23. </root>

    24. </configuration>

    下面是一个我常用的logback.xml配置文件,供大家参考:

     
    1. <?xml version="1.0" encoding="UTF-8"?>

    2. <configuration debug="true" scan="true" scanPeriod="30 seconds">

    3. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    4. <!-- encoders are by default assigned the type

    5. ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

    6. <encoder>

    7. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>

    8. <!-- 常用的Pattern变量,大家可打开该pattern进行输出观察 -->

    9. <!--

    10. <pattern>

    11. %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n

    12. Logger: %logger

    13. Class: %class

    14. File: %file

    15. Caller: %caller

    16. Line: %line

    17. Message: %m

    18. Method: %M

    19. Relative: %relative

    20. Thread: %thread

    21. Exception: %ex

    22. xException: %xEx

    23. nopException: %nopex

    24. rException: %rEx

    25. Marker: %marker

    26. %n

    27. </pattern>

    28. -->

    29. </encoder>

    30. </appender>

    31. <!-- 按日期区分的滚动日志 -->

    32. <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

    33. <file>logs/error.log</file>

    34. <encoder>

    35. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>

    36. </encoder>

    37. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    38. <level>ERROR</level>

    39. <onMatch>ACCEPT</onMatch>

    40. <onMismatch>DENY</onMismatch>

    41. </filter>

    42. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    43. <!-- daily rollover -->

    44. <fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>

    45. <!-- keep 30 days' worth of history -->

    46. <maxHistory>30</maxHistory>

    47. </rollingPolicy>

    48. </appender>

    49. <!-- 按文件大小区分的滚动日志 -->

    50. <appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

    51. <file>logs/info.log</file>

    52. <encoder>

    53. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>

    54. </encoder>

    55. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    56. <level>INFO</level>

    57. <onMatch>ACCEPT</onMatch>

    58. <onMismatch>DENY</onMismatch>

    59. </filter>

    60. <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

    61. <fileNamePattern>info.%i.log</fileNamePattern>

    62. <minIndex>1</minIndex>

    63. <maxIndex>3</maxIndex>

    64. </rollingPolicy>

    65. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

    66. <maxFileSize>5MB</maxFileSize>

    67. </triggeringPolicy>

    68. </appender>

    69. <!-- 按日期和大小区分的滚动日志 -->

    70. <appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

    71. <file>logs/debug.log</file>

    72. <encoder>

    73. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>

    74. </encoder>

    75. <filter class="ch.qos.logback.classic.filter.LevelFilter">

    76. <level>DEBUG</level>

    77. <onMatch>ACCEPT</onMatch>

    78. <onMismatch>DENY</onMismatch>

    79. </filter>

    80. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    81. <!-- rollover daily -->

    82. <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>

    83. <timeBasedFileNamingAndTriggeringPolicy

    84. class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

    85. <!-- or whenever the file size reaches 100MB -->

    86. <maxFileSize>100MB</maxFileSize>

    87. </timeBasedFileNamingAndTriggeringPolicy>

    88. </rollingPolicy>

    89. </appender>

    90. <!-- 级别阀值过滤 -->

    91. <appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

    92. <file>logs/sum.log</file>

    93. <encoder>

    94. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>

    95. </encoder>

    96. <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->

    97. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

    98. <level>INFO</level>

    99. </filter>

    100. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

    101. <!-- rollover daily -->

    102. <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>

    103. <timeBasedFileNamingAndTriggeringPolicy

    104. class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

    105. <!-- or whenever the file size reaches 100MB -->

    106. <maxFileSize>100MB</maxFileSize>

    107. </timeBasedFileNamingAndTriggeringPolicy>

    108. </rollingPolicy>

    109. </appender>

    110. <root level="debug">

    111. <appender-ref ref="STDOUT" />

    112. <appender-ref ref="ERROR-OUT" />

    113. <appender-ref ref="INFO-OUT" />

    114. <appender-ref ref="DEBUG-OUT" />

    115. <appender-ref ref="SUM-OUT" />

    116. </root>

    117. </configuration>

    如何进行日志系统转换?
    在实际的日志转换过程中,SLF4J其实是充当了一个中介的角色。例如当我们一个项目原来是使用LOG4J进行日志记录,但是我们要换成LogBack进行日志记录。
    此时我们需要先将LOG4J转换成SLF4J日志系统,再从SLF4J日志系统转成LogBack日志系统。
    从日志框架转向SLF4J
    jul-to-slf4j:jdk-logging到slf4j的桥梁
    log4j-over-slf4j:log4j1到slf4j的桥梁
    jcl-over-slf4j:commons-logging到slf4j的桥梁
    从SLF4J转向具体的日志框架
    slf4j-jdk14:slf4j到jdk-logging的桥梁
    slf4j-log4j12:slf4j到log4j1的桥梁
    log4j-slf4j-impl:slf4j到log4j2的桥梁
    logback-classic:slf4j到logback的桥梁
    slf4j-jcl:slf4j到commons-logging的桥梁
    例如我们一开始使用的是 Log4J 日志框架,现在我们希望转成 LogBack 框架,那么我们首先需要加入 log4j-over-slf4j.jar 将 Log4J 转成 SLF4J,之后再加入 logback-classic.jar 将 SLF4J 转成 LogBack。
    日志技术框架一览
    JUL:JDK中的日志记录工具,也常称为JDKLog、jdk-logging。
    LOG4J1:一个具体的日志实现框架。
    LOG4J2:一个具体的日志实现框架,是LOG4J1的下一个版本。
    LOGBACK:一个具体的日志实现框架,但其性能更好。
    JCL:一个日志门面,提供统一的日志记录接口,也常称为commons-logging。
    SLF4J:一个日志门面,与JCL一样提供统一的日志记录接口,可以方便地切换看具体的实现框架。
    JUL、LOG4J1、LOG4J2、LOGBACK是日志实现框架,而JCL、SLF4J是日志实现门面

    更多相关内容
  • Java 日志框架详解

    千次阅读 2021-12-23 21:29:22
    Java 日志框架详解,JUL,jcl、log4j、slf4j、logback、log4j2

    1. JUL学习

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

    1.1 架构介绍

    image-20211222111107875

    • Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。

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

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

    • Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。

    • Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。

    总结

    用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版。

    @Test
    public void test01(){
        Logger logger = Logger.getLogger("cn.quguai.JULTest");
        logger.info("Hello JUL");
    
        logger.log(Level.INFO, "info msg");
    
        logger.log(Level.INFO, "message:{0}:{1}", new Object[]{"quguai", 1});
    }
    

    1.2 日志级别

    java.util.logging.Level中定义了日志的级别:

    • SEVERE(最高值)
    • WARNING
    • INFO (默认级别)
    • CONFIG
    • FINE
    • FINER
    • FINEST(最低值)

    还有两个特殊的级别:

    • OFF,可用来关闭日志记录。
    • ALL,启用所有消息的日志记录。
    @Test
    public void testLevel(){
        Logger logger = Logger.getLogger("cn.quguai.JULTest");
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
    

    1.3 自定义日志级别配置

    @Test
    public void testSetLevel() throws IOException {
        Logger logger = Logger.getLogger("cn.quguai.JULTest");
    
        // 关闭原有处理器
        logger.setUseParentHandlers(false);
    
        // 创建ConsoleHandle 和 SimpleFormatter 格式化器
        ConsoleHandler consoleHandler = new ConsoleHandler();
        SimpleFormatter simpleFormatter = new SimpleFormatter();
    
        // 进行关联
        logger.addHandler(consoleHandler);
        consoleHandler.setFormatter(simpleFormatter); // 默认就是SimpleFormatter
    
        // 创建FileHandle
        FileHandler fileHandler = new FileHandler("./jul.log");
    
        // 进行关联
        logger.addHandler(fileHandler);
        fileHandler.setFormatter(simpleFormatter);
    
        // 设置默认级别
        consoleHandler.setLevel(Level.ALL);
        logger.setLevel(Level.ALL);
        // fileHandler.setLevel(Level.ALL); 默认沿用logger的配置
    
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
    

    1.4 Logger 父子关系

    @Test
    public void testLogParent(){
        Logger logger1 = Logger.getLogger("cn.quguai");
        Logger logger2 = Logger.getLogger("cn");
        // 默认顶级Logger对象是 java.util.logging.LogManager$RootLogger
        System.out.println(logger2.getParent());
    
        // 修改logger2的配置
        logger2.setUseParentHandlers(false);
        ConsoleHandler consoleHandler = new ConsoleHandler();
        logger2.addHandler(consoleHandler);
    
        logger2.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);
    
        logger1.severe("severe");
        logger1.warning("warning");
        logger1.info("info");
        logger1.config("config");
        logger1.fine("fine");
        logger1.finer("finer");
        logger1.finest("finest");
    }
    

    1.5 Logger 默认配置文件

    文件路径:D:\jdk-11.0.10\conf\logging.properties

    java.util.logging.Logger#getLogger() ->
    java.util.logging.Logger#demandLogger ->
    java.util.logging.LogManager#getLogManager ->
    java.util.logging.LogManager#ensureLogManagerInitialized->
    java.util.logging.LogManager#readPrimordialConfiguration ->
    java.util.logging.LogManager#readConfiguration() ->
    java.util.logging.LogManager#getConfigurationFileName

    ############################################################
    #  	Global properties
    ############################################################
    
    # "handlers" specifies a comma separated list of log Handler 
    # classes.  These handlers will be installed during VM startup.
    # Note that these classes must be on the system classpath.
    # By default we only configure a ConsoleHandler, which will only
    # show messages at the INFO and above levels.
    handlers= java.util.logging.ConsoleHandler
    
    # To also add the FileHandler, use the following line instead.
    #handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
    
    # Default global logging level.
    # This specifies which kinds of events are logged across
    # all loggers.  For any given facility this global level
    # can be overriden by a facility specific level
    # Note that the ConsoleHandler also has a separate level
    # setting to limit messages printed to the console.
    .level= INFO
    
    ############################################################
    # Handler specific properties.
    # Describes specific configuration info for Handlers.
    ############################################################
    
    # default file output is in user's home directory.
    java.util.logging.FileHandler.pattern = %h/java%u.log   # 当前用户目录下 java01.log
    java.util.logging.FileHandler.limit = 50000
    java.util.logging.FileHandler.count = 1
    # Default number of locks FileHandler can obtain synchronously.
    # This specifies maximum number of attempts to obtain lock file by FileHandler
    # implemented by incrementing the unique field %u as per FileHandler API documentation.
    java.util.logging.FileHandler.maxLocks = 100
    java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
    
    # Limit the message that are printed on the console to INFO and above.
    java.util.logging.ConsoleHandler.level = INFO
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    # Example to customize the SimpleFormatter output format 
    # to print one-line log message like this:
    #     <level>: <log message> [<date/time>]
    #
    # java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
    
    ############################################################
    # Facility specific properties.
    # Provides extra control for each logger.
    ############################################################
    
    # For example, set the com.xyz.foo logger to only log SEVERE
    # messages:
    com.xyz.foo.level = SEVERE
    

    1.6 自定义配置文件

    # 顶级父元素的默认的处理器
    handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler   // 追加文件管理器
    
    # 顶级父元素RootLogger的默认输出级别
    .level= ALL  // 修改默认级别
    # 文件配置路径
    java.util.logging.FileHandler.pattern = ./java%u.log
    # 指定日志文件内容大小
    java.util.logging.FileHandler.limit = 50000
    # 指定日志文件数量
    java.util.logging.FileHandler.count = 1
    
    java.util.logging.FileHandler.maxLocks = 100
    java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter  #XML格式化器
    java.util.logging.FileHandler.append = true
    
    java.util.logging.ConsoleHandler.level = ALL  # 修改默认级别
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    java.util.logging.ConsoleHandler.encoding = UTF-8
    
    @Test
    public void testProperties() throws IOException {
        InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
        
        // LogManager 是一个单例对象
        LogManager logManager = LogManager.getLogManager();
        logManager.readConfiguration(ins);
    
        Logger logger = Logger.getLogger("cn.quguai.JULTest");
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
        ins.close();
    }
    

    1.7 JUL配置文件详情

    1.7.1 ConsoleHandler配置形式

    java.util.logging.ConsoleHandler.encoding = UTF-8

    image-20211222155356134

    1.7.2 配置文件格式化方式

    java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

    SurrogateLogger.getSimpleFormat 获取配置文件中的key值对应的格式化方式

    image-20211222160043652

    1.7.3 追加文件

    java.util.logging.FileHandler.append = true

    1.7.4 自定义Logger

    # 自定义Logger
    cn.quguai.handlers = java.util.logging.FileHandler
    cn.quguai.level = CONFIG
    cn.quguai.useParentHandlers=false
    

    1.8 日志原理解析

    1. 初始化LogManager
      1. LogManager加载logging.properties配置
      2. 添加Logger到LogManager
    2. 从单例LogManager获取Logger
    3. 设置级别Level,并指定日志记录LogRecord
    4. Filter提供了日志级别之外更细粒度的控制
    5. Handler是用来处理日志输出位置
    6. Formatter是用来格式化LogRecord的

    image-20211222164607347

    2. Log4j

    官网地址 | 官方文档

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

    2.1 日志级别

    级别含义
    fatal指出每个严重的错误事件将会导致应用程序的退出。
    error虽然发生错误事件,但仍然不影响系统的继续运行。
    warn表明会出现潜在的错误情形。
    info一般和在粗粒度级别上,强调应用程序的运行全程。
    debug一般用于细粒度级别上,对调试应用程序非常有帮助。默认级别
    trace是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。

    推荐:实际开发中只会使用到最中间的四个步骤。

    @Test
    public void testQuick(){
        Logger logger = Logger.getLogger(Log4jTest.class);
        BasicConfigurator.configure();
        // logger.setLevel(Level.INFO);
        logger.fatal("fatal");
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");  // 默认级别
        logger.trace("trace");
    }
    

    2.2 Log4j 组件

    Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。

    其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件 等);Layout 控制日志信息的输出格式。

    2.2.1 Loggers

    日志记录器,负责收集处理日志记录,实例的命名就是类“XX”的full quailied name(类的全限定名), Logger的名字大小写敏感,其命名有继承机制:例如:name为org.apache.commons的logger会继承 name为org.apache的logger。

    Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接 或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。

    但是,自log4j 1.2版以来, Logger 类已经取代了 Category 类。对于熟悉早期版本的log4j的人来说, Logger 类可以被视为 Category 类的别名。

    img

    2.2.2 Appenders

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

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

    2.2.3 Layouts

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

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

    2.3 配置文件详情

    配置文件的后缀名称全部来源于对应的set方法注入,例如conversionPattern 使用SetConversionPattern方法完成

    log4j.rootLogger = trace,console
    
    # 指定appender
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    # 指定layout
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    # 指定格式化信息
    log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
    

    2.4 配置文件源码

    2.4.1 支持的配置文件

    public class LogManager {
        /** @deprecated */
        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";
    }
    

    org.apache.log4j.LogManager ->
    org.apache.log4j.helpers.OptionConverter#selectAndConfigure ->
    org.apache.log4j.PropertyConfigurator ->

    public class PropertyConfigurator implements Configurator {
        protected Hashtable registry = new Hashtable(11);
        protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
        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";
        static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
        static final String APPENDER_PREFIX = "log4j.appender.";
        static final String RENDERER_PREFIX = "log4j.renderer.";
        static final String THRESHOLD_PREFIX = "log4j.threshold";
        public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
        private static final String INTERNAL_ROOT_NAME = "root";
    }
    

    2.4.2 rootLogger 解析原理

    rootLogger 的配置源码 org.apache.log4j.PropertyConfigurator#parseCategory

    void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
        LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
        // 按照逗号进行分割
        StringTokenizer st = new StringTokenizer(value, ",");
        if (!value.startsWith(",") && !value.equals("")) {
            if (!st.hasMoreTokens()) {
                return;
            }
    		// 第一个参数作为 level
            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();
    	// 后续的参数作为 appender
        while(st.hasMoreTokens()) {
            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);
                }
            }
        }
    }
    

    2.5 内部日志记录

    public class LogLog {
        public static void debug(String msg) {
            if (debugEnabled && !quietMode) {
                System.out.println("log4j: " + msg);
            }
        }
    }
    

    可以使用 LogLog.setInternalDebugging(true) 打开开关,这是LogLog的内部静态方法。

    2.6 Layouts 格式

    在 log4j.properties 配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出 格式。

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

    形式含义
    %m输出代码中指定的日志信息
    %p输出优先级,及 DEBUG、INFO 等
    %n换行符(Windows平台的换行符为 “\n”,Unix 平台为 “\n”)
    %r输出自应用启动到输出该 log 信息耗费的毫秒数
    %c输出打印语句所属的类的全名
    %t输出产生该日志的线程全名
    %d输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日HH:mm:ss}
    %l输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10) 小写 【不推荐】会影响性能
    %F输出日志消息产生时所在的文件名称 【不推荐】会影响性能
    %L输出代码中的行号,【不推荐】会影响性能
    %%输出一个 “%” 字符

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

    形式含义
    %5c输出category名称,最小宽度是5,category<5,默认的情况下右对齐
    %-5c输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
    %.5c输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格
    %20.30ccategory名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉

    2.7 日志文件配置

    log4j.rootLogger = trace,console
    
    # 指定appender
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    # 指定layout
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    # 指定格式化信息
    log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
    
    log4j.appender.file = org.apache.log4j.FileAppender
    log4j.appender.file.layout = org.apache.log4j.PatternLayout
    log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
    # 来源于SetFile方法
    log4j.appender.file.file = ./log4j.log
    log4j.appender.file.encoding = UTF-8
    

    2.7.1 RollingFileAppender

    log4j.rootLogger = trace,rollingFile
    
    log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
    log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.rollingFile.file = ./log4j.log
    log4j.appender.rollingFile.encoding = UTF-8
    log4j.appender.rollingFile.maxFileSize = 10KB
    log4j.appender.rollingFile.maxBackupIndex = 1
    

    2.7.2 DailyRollingFileAppender

    log4j.rootLogger = trace,dailyRollingFile
    
    # 按照文件的大小进行拆分
    log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.dailyRollingFile.file = ./log4j.log
    log4j.appender.dailyRollingFile.encoding = UTF-8
    # 指定日期拆分规则
    log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
    

    2.7.3 JDBCAppender

    log4j.rootLogger = trace,sql
    
    # 按照数据库级别进行日志记录
    log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.sql.layout = org.apache.log4j.PatternLayout
    log4j.appender.sql.Driver = com.mysql.jdbc.Driver
    log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
    log4j.appender.sql.User = root
    log4j.appender.sql.Password = root
    log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
    
    CREATE TABLE `log` (
        `log_id` int(11) NOT NULL AUTO_INCREMENT,
        `project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
        `create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
        `level` varchar(255) DEFAULT NULL COMMENT '优先级',
        `category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
        `file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
        `thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
        `line` varchar(255) DEFAULT NULL COMMENT '号行',
        `all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
        `message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
        PRIMARY KEY (`log_id`)
    );
    

    2.8 自定义Logger

    level会进行覆盖,appender会进行继承,也就是dailyRollingFileconsole会同时起作用。

    该**包名**下面的全部类都是使用这个这种类型

    log4j.rootLogger = trace,dailyRollingFile
    
    # 自定义logger
    log4j.logger.cn.quguai = info,console
    
    log4j.rootLogger = trace,dailyRollingFile
    
    log4j.logger.cn.quguai = info,console
    
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
    
    log4j.appender.file = org.apache.log4j.FileAppender
    log4j.appender.file.layout = org.apache.log4j.PatternLayout
    log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.file.file = ./log4j.log
    log4j.appender.file.encoding = UTF-8
    
    log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
    log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.rollingFile.file = ./log4j.log
    log4j.appender.rollingFile.encoding = UTF-8
    log4j.appender.rollingFile.maxFileSize = 1KB
    log4j.appender.rollingFile.maxBackupIndex = 5
    
    log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.dailyRollingFile.file = ./log4j.log
    log4j.appender.dailyRollingFile.encoding = UTF-8
    log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
    
    log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.sql.layout = org.apache.log4j.PatternLayout
    log4j.appender.sql.Driver = com.mysql.jdbc.Driver
    log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
    log4j.appender.sql.User = root
    log4j.appender.sql.Password = root
    log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
    

    3. JCL日志门面

    image-20211222203416866

    3.1 JCL入门

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

    默认使用JUL作为日志底层实现,当导入log4j的包的时候自动进行转换。

    @Test
    public void testQuick(){
        Log log = LogFactory.getLog(JCLTest.class);
        BasicConfigurator.configure();
        log.fatal("s");
        log.error("error");
        log.warn("warning");
        log.info("info");
        log.debug("debug");
        log.trace("trace");
    }
    

    3.2 JCL原理

    Log

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

    流程如下:

    org.apache.commons.logging.LogFactory#getLog(java.lang.Class)
    org.apache.commons.logging.impl.LogFactoryImpl#getInstance(java.lang.String)
    org.apache.commons.logging.impl.LogFactoryImpl#newInstance
    org.apache.commons.logging.impl.LogFactoryImpl#discoverLogImplementation

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

    4. Slf4J 技术

    官网文档 | 官方地址

    4.1 Slf4j 入门

    使用 slf4j 内置的简单的实现

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.26</version>
    </dependency>
    <!--slf4j简单日志实现-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.26</version>
    </dependency>
    

    slf4j 相比于JCL 只提供了5个日志级别,去除了fatal

    public class Slf4jTest {
        private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
        
        @Test
        public void testQuick(){
            logger.error("error");
            logger.warn("warning");
            logger.info("info");  // slf4j-simple 默认日志级别 简单的日志框架的都是这个
            logger.debug("debug"); // 复杂的日志框架都是这个,logback log4j
            logger.trace("trace");
        }
    }
    

    输出形式

    logger.info("用户:{}{}", "李扬", 1);
    try {
        int i = 10 / 0;
    } catch (Exception e) {
        logger.error("出现异常", e);
    }
    

    4.2 日志绑定

    本质上就是依靠slf4j进行日志门面的同意管理

    • 蓝色部分代表默认遵循了slf4j的规范可以直接进行使用
    • 青色部分代表了适配层,需要引入桥接进行使用

    • slf4j-nop 代表了日志的开关,如果引入这个包,代表日志不在进行使用,全部禁用;
    • 适配层的包,默认会将上下两层的包全部导入,例如slf4j-log412.jar,默认依赖了slf4j和log4j;
    • 直接实现的包,默认也直接依赖了slf4j;

    推荐直接引入处于第三层的包(红色边框)就可以完全导入所有依赖。

    绑定流程

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

    4.3 绑定原理流程

    org.slf4j.LoggerFactory#getLogger(java.lang.String)

    org.slf4j.LoggerFactory#getILoggerFactory()

    org.slf4j.LoggerFactory#performInitialization()

    org.slf4j.LoggerFactory#bind()

    org.slf4j.LoggerFactory#findPossibleStaticLoggerBinderPathSet() 其中的STATIC_LOGGER_BINDER_PATH=“org/slf4j/impl/StaticLoggerBinder.class”,只要实现这个方法就可以完成绑定操作。

    4.4 桥接原理

    桥接和绑定不同,绑定适用于前期的选型问题,确定下来就已经定死了,桥接适用于一些老代码的日志切换为Slf4j进行管理。不会改变原有的代码格式。桥接解决的是项目中日志的遗留问题,当系统中存在之前的日志API,可以通过桥接转换到slf4j的实现。

    1. 先去除之前老的日志框架的依赖
    2. 添加SLF4J提供的桥接组件
    3. 为项目添加SLF4J的具体实现

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>1.7.26</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.8</version>
    </dependency>
    

    注意问题:

    1. jcl-over-slf4j.jarslf4j-jcl.jar不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给 SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致无限循环;
    2. log4j-over-slf4j.jarslf4j-log4j12.jar不能同时出现;
    3. jul-to-slf4j.jarslf4j-jdk14.jar不能同时出现;
    4. 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是 Appender,Filter等对象,将无法产生效果。

    5. logback

    官网文档 | 中文文档

    logback-classic 默认导入了 slf4j 以及 logback-core(都是一个人的嘛)

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.8</version>
    </dependency>
    

    默认日志级别debug (复杂日志框架(log4j logback)都是debug,简单实现日志框架(jul slf4j-sample)都是info)

    image-20211223154125775

    @Test
    public void testQuick() {
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");  // 默认级别
        logger.trace("trace");
    }
    

    5.1 配置文件

    可以通过 PropertiesTranslator 将log4j.properties 转化为xml形式

    logback会依次读取以下类型配置文件:

    • logback.groovy
    • logback-test.xml
    • logback.xml
    • 如果均不存在会采用默认配置(BasicConfigurator,直接输出到控制台上)

    logback组件之间的关系

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

    5.1.1 配置文件详情

    大写的简化形式都不推荐,影响性能问题

    格式化含义备注
    %-5level = %p = %le日志级别
    %d{yyyy-MM-dd HH:mm:ss.SSS}日期
    %c{length} = %logger = %lo类的完整名称后面数字代表简写的形式不代表长度
    %thread= %t线程名称
    %r = %relative程序启动到创建日志记录的时间单位是毫秒
    %m = %msg信息
    %n换行
    %C = %class全限定类名避免使用
    %Mmethod避免使用
    %L行号避免使用
    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        
        <!--配置集中管理属性-->
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--控制输出流对象,默认为System.out 变为红色字体-->
            <target>System.err</target>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="console"/>
        </root>
    </configuration>
    

    5.1.2 文件输出

    一般封装好的类都处于classic包下面,没有封装好的,进行二次开发的都处于core包下面

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <appender name="file" class="ch.qos.logback.core.FileAppender">
            <file>${log_dir}/logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="file"/>
        </root>
    </configuration>
    

    5.1.3 自定义html输出

    一般封装好的类都处于classic包下面,没有封装好的,进行二次开发的都处于core包下面

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <!--配置集中管理属性-->
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
        
        <appender name="file" class="ch.qos.logback.core.FileAppender">
            <file>${log_dir}/logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <appender name="html" class="ch.qos.logback.core.FileAppender">   <==classic包下面
            <file>${log_dir}/logback.html</file>
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">   <==core包下面
                <layout class="ch.qos.logback.classic.html.HTMLLayout">        <==classic包下面
                    <pattern>%d{HH:mm:ss.SSS} %thread %-5level %c{36} - %msg%n</pattern>
                </layout>
            </encoder>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="file"/>
            <appender-ref ref="html"/>
        </root>
    </configuration>
    

    5.1.4 日志拆分和归档压缩

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <!--日志拆分和归档压缩-->
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log_dir}/roll_logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--按照文件大小进行拆分-->
                <maxFileSize>1mb</maxFileSize>
                <!--按照时间进行拆分和归档压缩-->
                <fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
            </rollingPolicy>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="rollFile"/>
        </root>
    </configuration>
    

    5.1.5 日志过滤器

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log_dir}/roll_logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <maxFileSize>1mb</maxFileSize>
                <fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
            </rollingPolicy>
            <!--过滤器-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">   <== 过滤器
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="rollFile"/>
        </root>
    </configuration>
    

    5.1.6 异步日志记录

    异步日志借助其他的appender完成异步任务,底层使用阻塞队列完成

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log_dir}/roll_logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <maxFileSize>1mb</maxFileSize>
                <fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
            </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-ref ref="rollFile"/>  <== 借助rollFile完成异步任务
        </appender>
    
        <root level="ALL">
            <appender-ref ref="async"/>
        </root>
    </configuration>
    

    5.1.7 自定义Logger

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log_dir}/roll_logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <maxFileSize>1mb</maxFileSize>
                <fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
            </rollingPolicy>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="rollFile"/>
        </root>
         
         <!--自定义logger additivity=false不存在父子继承关系-->
        <logger name="cn.quguai" level="info" additivity="false">
            <appender-ref ref="console"/>
        </logger>
    </configuration>
    

    **注意:**即使会输出root级别的文件,但是大小均为0kb。

    5.2 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元素中添加:

      <Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
      
    3. logback默认会在$TOMCAT_HOME/conf下查找文件 logback-access.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
      <!-- always a good activate OnConsoleStatusListener -->
      	<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
      	<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.TimeBasedRollingPolicy">
      			<fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
      		</rollingPolicy>
          	<encoder>
      		<!-- 访问日志的格式 -->
      			<pattern>combined</pattern>  <== logback集成的格式信息可以参考下方链接
      		</encoder>
      	</appender>
      	<appender-ref ref="FILE"/>
      </configuration>
      
    4. 官方配置: https://logback.qos.ch/access.html#configuration

    6. Log4j2

    官方文档 | 配置文件

    6.1 快速入门

    <!--日志门面-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.11.1</version>
    </dependency>
    <!--日志实现-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.11.1</version>
    </dependency>
    
    public class Log4j2Test {
        private static final Logger logger = LogManager.getLogger(Log4j2Test.class);
    
        @Test
        public void testQuick(){
            logger.fatal("fatal");
            logger.error("error"); // 默认级别
            logger.warn("warn");
            logger.info("info");
            logger.debug("debug");
            logger.trace("trace");
        }
    }
    

    实际开发当中依然使用slf4jlogback的组合方式进行

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.15.0</version>
    </dependency>
    

    6.2 配置文件

    配置文件参数上和log4j形式上十分相同

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" monitorInterval="5">
        <properties>
            <property name="LOG_HOME">./logs</property>
        </properties>
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
            </Console>
            <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>
            <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>
            <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                         filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-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} - %msg%n"/>
                <Policies>
                    <!--系统启动时,会自动进行触发,生成一个新的日志文件-->
                    <OnStartupTriggeringPolicy/>
                    <SizeBasedTriggeringPolicy size="10 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
                <!--同一个文件夹下,最大的文件个数-->
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
        </Appenders>
        <Loggers>
            <!--默认使用rootLogger-->
            <Root level="trace">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    

    6.3 异步日志(额外依赖)

    image-20211223193012334

    • 异步日志形式

    image-20211223193037614

    Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应 前面我们说的Appender组件和Logger组件。
    注意:配置异步日志需要添加依赖

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

    6.3.1 AsyncAppender方式

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" monitorInterval="5">
        <properties>
            <property name="LOG_HOME">./logs</property>
        </properties>
        <Appenders>
            <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>
            <Async name="Async">  <==异步形式
                <AppenderRef ref="file"/>
            </Async>
        </Appenders>
        <Loggers>
            <!--默认使用rootLogger-->
            <Root level="error">
                <AppenderRef ref="Async"/> <==异步形式
            </Root>
        </Loggers>
    </Configuration>
    

    6.3.2 AsyncLogger方式

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

    • 全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个 log4j2.component.properties 配置;

      Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
      
    • 混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。

      <?xml version="1.0" encoding="UTF-8"?>
      <Configuration status="warn" monitorInterval="5">
          <properties>
              <property name="LOG_HOME">./logs</property>
          </properties>
          <Appenders>
              <Console name="Console" target="SYSTEM_OUT">
                  <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
              </Console>
              <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>
          </Appenders>
          <Loggers>
              <!--默认使用rootLogger-->
              <Root level="error">
                  <AppenderRef ref="Console"/>
              </Root>
      
              <!--自定义异步Logger对象 includeLocation:关闭日志行号信息 addivitity:是否进行继承-->
              <AsyncLogger name="cn.quguai" level="info" includeLocation="false" addivitity="false">
                  <AppenderRef ref="file"/>
              </AsyncLogger>
          </Loggers>
      </Configuration>
      

    使用异步日志需要注意的问题:

    1. 如果使用异步日志,AsyncAppenderAsyncLogger全局日志,不要同时出现。性能会和 AsyncAppender一致,降至最低。
    2. 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。

    7. SpringBoot 日志配置

    默认使用slf4j + logback 进行实现

    <dependency>
        <artifactId>spring-boot-starter-logging</artifactId>
        <groupId>org.springframework.boot</groupId>
    </dependency>
    

    7.1 快速入门

    直接使用spring配置

    logging.level.cn.quguai = debug
    
    logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
    logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
    
    # 两者只能留下一个path默认文件名是spring.log
    #logging.file.name=./logs/springboot.log
    logging.file.path=./logs 
    
    logging.logback.rollingpolicy.file-name-pattern=${logging.file.path}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip
    logging.logback.rollingpolicy.max-file-size=1KB
    logging.logback.rollingpolicy.max-history=30
    
    @SpringBootTest
    class SpringBootLogApplicationTests {
    
        private static final Logger logger = LoggerFactory.getLogger(SpringBootLogApplicationTests.class);
    
        @Test
        void contextLoads() {
            logger.info("info");  // 默认级别
            logger.debug("debug");
    
            // 即使桥接器的形式使用log4j2的门面输出,默认还是会转为slf4j+logback
            org.apache.logging.log4j.Logger logger = LogManager.getLogger(SpringBootLogApplicationTests.class);
            logger.info("info log4j2");
            logger.debug("debug log4j2");
        }
    }
    

    7.2 指定配置

    给类路径下放上每个日志框架自己的配置文件;SpringBoot就不使用默认配置的了

    日志框架配置文件
    Logbacklogback-spring.xml, logback.xml
    Log4j2log4j2-spring.xml , log4j2.xml
    JULlogging.properties

    7.3 使用SpringBoot解析日志配置

    logback-spring.xml:由SpringBoot解析日志配置

    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <springProfile name="default">
            <pattern>${pattern}</pattern>
        </springProfile>
        <springProfile name="test">
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} ==== %msg%n</pattern>
        </springProfile>
    </encoder>
    
    spring.profiles.active=test
    

    7.4 日志切换到log4j2

    image-20211223210425792

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>   <== 排除logging依赖:logback依赖
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>  <== 引入log4j2的依赖,底层依然使用slf4j
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
    

    image-20211223211107925

    7. 配置文件汇总

    7.1 JUL配置文件

    handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
    .level= ALL
    
    # Self-Logger
    cn.quguai.handlers = java.util.logging.FileHandler
    cn.quguai.level = CONFIG
    cn.quguai.useParentHandlers=false
    
    java.util.logging.FileHandler.pattern = ./java%u.log
    java.util.logging.FileHandler.limit = 50000
    java.util.logging.FileHandler.count = 1
    
    java.util.logging.FileHandler.maxLocks = 100
    java.util.logging.FileHandler.append = true
    java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
    
    java.util.logging.ConsoleHandler.level = ALL
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    java.util.logging.ConsoleHandler.encoding = UTF-8
    java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
    

    7.2 Log4j 配置文件

    log4j.rootLogger = trace,dailyRollingFile
    
    # ???logger
    log4j.logger.cn.quguai = info,console
    
    # ??appender
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    # ??layout
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    # ???????
    log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
    
    log4j.appender.file = org.apache.log4j.FileAppender
    log4j.appender.file.layout = org.apache.log4j.PatternLayout
    log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.file.file = ./log4j.log
    log4j.appender.file.encoding = UTF-8
    
    # ???????????
    log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
    log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.rollingFile.file = ./log4j.log
    log4j.appender.rollingFile.encoding = UTF-8
    # ???????????
    log4j.appender.rollingFile.maxFileSize = 1KB
    # ?????????
    log4j.appender.rollingFile.maxBackupIndex = 5
    
    # ???????????
    log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
    log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
    log4j.appender.dailyRollingFile.file = ./log4j.log
    log4j.appender.dailyRollingFile.encoding = UTF-8
    # ????????
    log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
    
    # ?????????????
    log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.sql.layout = org.apache.log4j.PatternLayout
    log4j.appender.sql.Driver = com.mysql.jdbc.Driver
    log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
    log4j.appender.sql.User = root
    log4j.appender.sql.Password = root
    log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
    

    7.3 Logback 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
        <!--配置集中管理属性-->
        <!-- 日志输出格式:
            %-5level = %p = %le
            %d{yyyy-MM-dd HH:mm:ss.SSS}日期
            %c{length}类的完整名称 = %logger = %lo :后面数字代表简写的形式不代表长度
            %C = %class :避免使用
            %M为method   :避免使用
            %L为行号     :避免使用
            %thread线程名称 = %t
            %m或者%msg为信息
            %n换行 -->
        <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
        <property name="log_dir" value="./logs"/>
    
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!--控制输出流对象,默认为System.out 变为红色字体-->
            <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_dir}/logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
        </appender>
    
        <!--html 格式输出-->
        <appender name="html" class="ch.qos.logback.core.FileAppender">
            <file>${log_dir}/logback.html</file>
            <!--自定义 encoder -->
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="ch.qos.logback.classic.html.HTMLLayout">
                    <pattern>%d{HH:mm:ss.SSS} %thread %-5level %c{36} - %msg%n</pattern>
                </layout>
            </encoder>
        </appender>
    
        <!--日志拆分和归档压缩-->
        <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log_dir}/roll_logback.log</file>
            <append>true</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${pattern}</pattern>
            </encoder>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--按照文件大小进行拆分-->
                <maxFileSize>1mb</maxFileSize>
                <!--按照时间进行拆分和归档压缩-->
                <fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
                <!--只保留最近30天的-->
                <maxHistory>30</maxHistory>
            </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-ref ref="rollFile"/>
        </appender>
    
        <root level="ALL">
            <appender-ref ref="console"/>
            <appender-ref ref="async"/>
            <appender-ref ref="html"/>
        </root>
    
        <!--自定义logger additivity是否存在父子继承关系-->
        <logger name="cn.quguai" level="info" additivity="false">
            <appender-ref ref="console"/>
        </logger>
    </configuration>
    

    7.4 Log4j2 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" monitorInterval="5">
        <properties>
            <property name="LOG_HOME">./logs</property>
        </properties>
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
            </Console>
            <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>
            <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>
            <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                         filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-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} - %msg%n"/>
                <Policies>
                    <!--系统启动时,会自动进行触发,生成一个新的日志文件-->
                    <OnStartupTriggeringPolicy/>
                    <SizeBasedTriggeringPolicy size="10 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
                <!--同一个文件夹下,最大的文件个数-->
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
            <!--使用异步Appender 等同于Logback-->
            <!--<Async name="Async">
                <AppenderRef ref="file"/>
            </Async>-->
        </Appenders>
        <Loggers>
            <!--默认使用rootLogger-->
            <Root level="error">
                <AppenderRef ref="Console"/>
            </Root>
    
            <!--自定义异步Logger对象 includeLocation:关闭日志行号信息 addivitity:是否进行继承-->
            <AsyncLogger name="cn.quguai" level="info" includeLocation="false" addivitity="false">
                <AppenderRef ref="file"/>
            </AsyncLogger>
        </Loggers>
    </Configuration>
    

    使用异步日志需要注意的问题:

    1. 如果使用异步日志,AsyncAppenderAsyncLogger全局日志,不要同时出现。性能会和 AsyncAppender一致,降至最低。
    2. 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。
    展开全文
  • Java日志框架详解.ppt

    2022-06-08 11:42:55
    Java日志框架详解.ppt
  • 记得前几年工作的时候,公司使用的日志框架还是log4j,大约从16年中到现在,不管是我参与的别人已经搭建好的项目还是我自己主导的项目,日志框架基本都换成了logback,总结一下,logback大约有以下的一些优点:内核...
  • JUL(Java util logging),Java 原生日志框架,不需要引入第三方依赖包,使用简单方便。

    JUL 简介

    JUL(Java util logging),Java 原生日志框架,不需要引入第三方依赖包,使用简单方便,一般在小型应用中使用,主流项目中现在很少使用了。

    JUL 架构

    在这里插入图片描述

    • Application:Java 应用程序。
    • Logger:记录器,Java 应用程序通过调用记录器的方法来发布日志记录。
    • Handler:处理器,每一个 Logger 都可以关联一个或多个 Handler,Handler 从 Logger 获取日志并将日志输出到某个目的地,目的地可以是控制台,本地文件,或网络日志服务,或将它们转发到操作系统日志等等。通过Handler.setLevel(level.off)方法可以禁用一个 Handler,也可以设置其他级别来开启此 Handler。
    • Filter:过滤器,根据条件过滤哪些日志记录。每一个 Logger 和 Handler 都可以关联一个 Filter。
    • Formatter :格式化器,负责对日志事件中的日志记录进行转换和格式化。
    • Level:每一条日志记录都有一个关联的日志级别,表示此条日志的重要性和紧急程度。也可以对 Logger 和 Handler 设置关联的日志级别。

    入门示例

    package com.chenpi;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import org.junit.Test;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class ChenPiJULMain {
    
      // JUL 日志框架演示
      @Test
      public void testLog() {
    
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
    
        // 日志记录输出
        logger.severe(">>>>> Hello ChenPi!!");
        logger.warning(">>>>> Hello ChenPi!!");
        logger.info(">>>>> Hello ChenPi!!"); // 默认日志级别
        logger.config(">>>>> Hello ChenPi!!");
        logger.fine(">>>>> Hello ChenPi!!");
        logger.finer(">>>>> Hello ChenPi!!");
        logger.finest(">>>>> Hello ChenPi!!");
    
        // 输出指定日志级别的日志记录
        logger.log(Level.WARNING, ">>>>> Hello Warning ChenPi!!");
    
        // 占位符形式
        String name = "ChenPi";
        int age = 18;
        logger.log(Level.INFO, ">>>>> Hello {0},{1} years old!", new Object[]{name, age});
    
        // 异常堆栈信息
        logger.log(Level.SEVERE, ">>>>> Hello NPE!", new NullPointerException());
    
      }
    }
    

    JUL 默认将日志信息输出到控制台,默认日志级别是 info。控制台输出结果如下:

    在这里插入图片描述

    Logger 父子继承关系

    在 JUL 中,Logger 有父子继承关系概念,会根据 Logger 对象的名称的包含关系,划分父子继承关系。对于某个 Logger 对象,如果找不到显性创建的父 Logger 对象,那么它的父 Logger 是根 Logger,即 RootLogger。

    子代 Logger 对象会继承父 Logger 的配置,例如日志级别,关联的 Handler,日志格式等等。对于任何 Logger 对象,如果没对其做特殊配置,那么它最终都会继承 RootLogger 的配置。

    package com.chenpi;
    
    import java.util.logging.Logger;
    import org.junit.Test;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class ChenPiJULMain {
    
      @Test
      public void testLog() {
    
        // logger1的父Logger是RootLogger
        Logger logger1 = Logger.getLogger("com.chenpi.a");
        // logger2的父Logger是logger1  
        Logger logger2 = Logger.getLogger("com.chenpi.a.b.c");
    
        Logger logger1Parent = logger1.getParent();
        System.out.println("logger1Parent:" + logger1Parent + ",name:" + logger1Parent.getName());
    
        Logger logger2Parent = logger2.getParent();
        System.out.println("logger1:" + logger1 + ",name:" + logger1.getName());
        System.out.println("logger2Parent:" + logger2Parent + ",name:" + logger2Parent.getName());
    
      }
    }
    
    // 输出结果如下
    logger1Parent:java.util.logging.LogManager$RootLogger@61e717c2,name:
    logger1:java.util.logging.Logger@66cd51c3,name:com.chenpi.a
    logger2Parent:java.util.logging.Logger@66cd51c3,name:com.chenpi.a
    

    日志配置

    我们可以通过2种方式调整 JUL 默认的日志行为(设置日志级别,日志输出目的地,日志格式等等),一种是通过在程序中硬编码形式(不推荐),另一种是通过单独的配置文件形式。

    硬编码日志配置

    在 JUL 中,Logger 是有父子继承关系的,所以当我们需要对某一个 Logger 对象进行单独的配置时,需要将它设置为不继承使用父 Logger 的配置。

    以下演示名称为com.chenpi.ChenPiJULMain的 Logger 对象单独进行配置,并且关联两个 Handler。

    package com.chenpi;
    
    import java.io.IOException;
    import java.util.logging.*;
    import org.junit.Test;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class ChenPiJULMain {
    
      @Test
      public void testLog() throws IOException {
    
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
    
        // 关闭默认配置,即不使用父Logger的Handlers
        logger.setUseParentHandlers(false);
    
        // 设置记录器的日志级别为ALL
        logger.setLevel(Level.ALL);
    
        // 日志记录格式,使用简单格式转换对象
        SimpleFormatter simpleFormatter = new SimpleFormatter();
    
        // 控制台输出Handler,并且设置日志级别为INFO,日志记录格式
        ConsoleHandler consoleHandler = new ConsoleHandler();
        consoleHandler.setLevel(Level.INFO);
        consoleHandler.setFormatter(simpleFormatter);
    
        // 文件输出Handler,并且设置日志级别为FINE,日志记录格式
        FileHandler fileHandler = new FileHandler("./jul.log");
        fileHandler.setLevel(Level.FINE);
        fileHandler.setFormatter(simpleFormatter);
    
        // 记录器关联处理器,即此logger对象的日志信息输出到这两个Handler进行处理
        logger.addHandler(consoleHandler);
        logger.addHandler(fileHandler);
    
        // 日志记录输出
        logger.severe(">>>>> Hello ChenPi!!");
        logger.warning(">>>>> Hello ChenPi!!");
        logger.info(">>>>> Hello ChenPi!!");
        logger.config(">>>>> Hello ChenPi!!");
        logger.fine(">>>>> Hello ChenPi!!");
        logger.finer(">>>>> Hello ChenPi!!");
        logger.finest(">>>>> Hello ChenPi!!");
      }
    }
    

    因为 Logger 设置的日志级别是 ALL,即所有级别的日志记录都可以通过。但是 ConsoleHandler 设置的日志级别是 INFO,所以控制台只输出 INFO 级别以上的日志记录。而 FileHandler 设置的日志级别是 FINE,所以日志文件中输出的是 FINE 级别以上的日志记录。

    以下是控制台输出的日志结果:

    在这里插入图片描述

    以下是日志文件jul.log中输出的日志结果:

    在这里插入图片描述

    日志配置文件

    通过 debug 调试,按以下方法顺序,可以发现,如果我们没有配置 JUL 的配置文件,系统默认从 JDK 的安装目录下的 lib 目录下读取默认的配置文件logging.properties

    getLogger()  -> demandLogger() -> LogManager.getLogManager() -> ensureLogManagerInitialized() -> owner.readPrimordialConfiguration() -> readConfiguration()
    

    在这里插入图片描述

    在这里插入图片描述

    以下是 JDK 自带的 JUL 默认日志配置文件内容:

    在这里插入图片描述

    对于配置选项有哪些,其实可以通过相应类的源码找出,例如 Logger 类的源码如下:

    在这里插入图片描述

    FileHandler 类的源码如下:

    在这里插入图片描述

    所以我们可以拷贝默认的配置文件到我们工程的 resources 目录下,自定义修改配置信息。

    ############################################################
    # 全局属性
    ############################################################
    
    # 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
    # 对于其他Logger,如果没有指定自己的Handler,则默认继承此
    handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
    
    # 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
    .level= ALL
    
    
    ############################################################
    # Handler 配置
    ############################################################
    
    # FileHandler定义
    # 日志文件存储位置
    java.util.logging.FileHandler.pattern = ./jul%u.log
    # 单个文件的最大字节数,0代表不限制
    java.util.logging.FileHandler.limit = 50000
    # 文件数量上限,多个文件为jul0.log.0,jul0.log.1 ...
    java.util.logging.FileHandler.count = 5
    # 日志级别
    java.util.logging.FileHandler.level = SEVERE
    # 日志追加方式
    java.util.logging.FileHandler.append = true
    # Handler对象采用的字符集
    java.util.logging.FileHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
    
    # ConsoleHandler定义
    # 日志级别
    java.util.logging.ConsoleHandler.level = INFO
    # Handler对象采用的字符集
    java.util.logging.ConsoleHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    
    ############################################################
    # Logger 配置
    ############################################################
    
    # 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
    com.chenpi.person.level = WARNING
    

    然后我们应用中加载我们类路径中自定义的配置文件。

    package com.chenpi;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.logging.LogManager;
    import java.util.logging.Logger;
    import org.junit.Test;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class ChenPiJULMain {
    
      // JUL 日志框架演示
      @Test
      public void testLog() throws IOException {
    
        // 读取配置文件
        // 也可以通过使用java.util.logging.config.file系统属性指定文件名
        // 例如 java -Djava.util.logging.config.file=myfile
        InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
            .getResourceAsStream("logging.properties");
        // 获取LogManager
        LogManager logManager = LogManager.getLogManager();
        // 记载配置文件
        logManager.readConfiguration(resourceAsStream);
    
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
    
        // 日志记录输出
        logger.severe(">>>>> Hello ChenPi!!");
        logger.warning(">>>>> Hello ChenPi!!");
        logger.info(">>>>> Hello ChenPi!!");
        logger.config(">>>>> Hello ChenPi!!");
        logger.fine(">>>>> Hello ChenPi!!");
        logger.finer(">>>>> Hello ChenPi!!");
        logger.finest(">>>>> Hello ChenPi!!");
    
        // 获取日志记录器对象
        Logger personLogger = Logger.getLogger("com.chenpi.person");
    
        // 日志记录输出
        personLogger.severe(">>>>> Hello Person!!");
        personLogger.warning(">>>>> Hello Person!!");
        personLogger.info(">>>>> Hello Person!!");
        personLogger.config(">>>>> Hello Person!!");
        personLogger.fine(">>>>> Hello Person!!");
        personLogger.finer(">>>>> Hello Person!!");
        personLogger.finest(">>>>> Hello Person!!");
      }
    }
    

    控制台和文件中输出内容如下:

    在这里插入图片描述

    在这里插入图片描述

    自定义 Logger

    我们可以针对某一个 Logger 进行单独的配置,例如日志级别,关联的 Handler 等,而不默认继承父级的。

    当然我们也可以为以包名为名称的 Logger 进行配置,这样这个包名下的所有子代 Logger 都能继承此配置。

    ############################################################
    # 全局属性
    ############################################################
    
    # 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
    # 对于其他Logger,如果没有指定自己的Handler,则默认继承此
    handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
    
    # 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
    .level= ALL
    
    
    ############################################################
    # Handler 配置
    ############################################################
    
    # FileHandler定义
    # 日志文件存储位置
    java.util.logging.FileHandler.pattern = ./jul%u.log
    # 单个文件的最大字节数,0代表不限制
    java.util.logging.FileHandler.limit = 50000
    # 文件数量上限
    java.util.logging.FileHandler.count = 5
    # 日志级别
    java.util.logging.FileHandler.level = SEVERE
    # 日志追加方式
    java.util.logging.FileHandler.append = true
    # Handler对象采用的字符集
    java.util.logging.FileHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
    
    # ConsoleHandler定义
    # 日志级别
    java.util.logging.ConsoleHandler.level = INFO
    # Handler对象采用的字符集
    java.util.logging.ConsoleHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    
    ############################################################
    # Logger 配置
    ############################################################
    
    # 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
    com.chenpi.person.level = WARNING
    # 只关联FileHandler
    com.chenpi.person.handlers = java.util.logging.FileHandler
    # 关闭默认配置
    com.chenpi.person.useParentHandlers = false
    
    package com.chenpi;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.logging.LogManager;
    import java.util.logging.Logger;
    import org.junit.Test;
    
    /**
    * @author 陈皮
    * @version 1.0
    * @description
    * @date 2022/3/2
    */
    public class ChenPiJULMain {
        
        // JUL 日志框架演示
        @Test
        public void testLog() throws IOException {
            
            // 读取配置文件
            InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
                .getResourceAsStream("logging.properties");
            // 获取LogManager
            LogManager logManager = LogManager.getLogManager();
            // 记载配置文件
            logManager.readConfiguration(resourceAsStream);
            
            // 获取日志记录器对象
            Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
            
            // 日志记录输出
            logger.severe(">>>>> Hello ChenPi!!");
            logger.warning(">>>>> Hello ChenPi!!");
            logger.info(">>>>> Hello ChenPi!!");
            logger.config(">>>>> Hello ChenPi!!");
            logger.fine(">>>>> Hello ChenPi!!");
            logger.finer(">>>>> Hello ChenPi!!");
            logger.finest(">>>>> Hello ChenPi!!");
            
            // 获取日志记录器对象
            Logger personLogger = Logger.getLogger("com.chenpi.person");
            
            // 日志记录输出
            personLogger.severe(">>>>> Hello Person!!");
            personLogger.warning(">>>>> Hello Person!!");
            personLogger.info(">>>>> Hello Person!!");
            personLogger.config(">>>>> Hello Person!!");
            personLogger.fine(">>>>> Hello Person!!");
            personLogger.finer(">>>>> Hello Person!!");
            personLogger.finest(">>>>> Hello Person!!");
        }
    }
    

    上述例子中,对于名称为com.chenpi.person的 Logger,只将日志输出到文件中,其他 Logger 则会同时输出到控制台和文件中。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    自定义日志格式

    SimpleFormatter类源码为例,再到LoggingSupport类源码,发现首先判断我们是否通过java.util.logging.SimpleFormatter.format属性配置了格式,如果没有则使用默认的日志格式。

    在这里插入图片描述

    在这里插入图片描述

    所以我们可以在配置文件中自定义日志记录的格式。

    ############################################################
    # 全局属性
    ############################################################
    
    # 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
    # 对于其他Logger,如果没有指定自己的Handler,则默认继承此
    handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
    
    # 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
    .level= ALL
    
    
    ############################################################
    # Handler 配置
    ############################################################
    
    # FileHandler定义
    # 日志文件存储位置
    java.util.logging.FileHandler.pattern = ./jul%u.log
    # 单个文件的最大字节数,0代表不限制
    java.util.logging.FileHandler.limit = 1024
    # 文件数量上限
    java.util.logging.FileHandler.count = 5
    # 日志级别
    java.util.logging.FileHandler.level = FINE
    # 日志追加方式
    java.util.logging.FileHandler.append = true
    # Handler对象采用的字符集
    java.util.logging.FileHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
    # 自定义SimpleFormatter的日志格式
    java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
    
    # ConsoleHandler定义
    # 日志级别
    java.util.logging.ConsoleHandler.level = INFO
    # Handler对象采用的字符集
    java.util.logging.ConsoleHandler.encoding = UTF-8
    # 日志格式,使用系统默认的简单格式
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    
    ############################################################
    # Logger 配置
    ############################################################
    
    # 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
    com.chenpi.person.level = WARNING
    

    这样,所有绑定了 SimpleFormatter 的 Handler 的所有日志记录就使用了自定义的格式。

    在这里插入图片描述

    当然通过源码发现,日志格式类还有其他几个实现,如下所示:

    在这里插入图片描述

    日志过滤器

    Filter,日志过滤器,用来对输出的日志记录进行过滤。我们可以根据多个维度进行过滤,例如只输出 message 包含某段文本信息的日志,只输出某个方法中记录的日志,某个级别的日志等等。

    Logger 对象将日志信息包装成一个 LogRecord 对象,然后将该对象传给 Handler 进行处理。每一个 Logger 和 Handler 都可以关联一个 Filter。LogRecord 中包含了日志的文本信息、日志生成的时间戳、日志来自于哪个类、日志来自于哪个方法、日志来自于哪个线程等等信息。

    Filter 源码如下所示,我们只需要创建一个 Filter 的实现类,重写方法即可。

    package java.util.logging;
    
    /**
    * A Filter can be used to provide fine grain control over
    * what is logged, beyond the control provided by log levels.
    * <p>
    * Each Logger and each Handler can have a filter associated with it.
    * The Logger or Handler will call the isLoggable method to check
    * if a given LogRecord should be published.  If isLoggable returns
    * false, the LogRecord will be discarded.
    *
    * @since 1.4
    */
    @FunctionalInterface
    public interface Filter {
        
        /**
        * Check if a given log record should be published.
        * @param record  a LogRecord
        * @return true if the log record should be published.
        */
        public boolean isLoggable(LogRecord record);
    }
    

    我们实现一个 Filter ,如果日志消息包含暴力两个字,则不予放行,即不记录此条日志。

    package com.chenpi;
    
    import java.util.logging.Filter;
    import java.util.logging.LogRecord;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class MyLoggerFilter implements Filter {
    
      private static final String SENSITIVE_MESSAGE = "暴力";
    
      @Override
      public boolean isLoggable(LogRecord record) {
        String message = record.getMessage();
        return null == message || !message.contains(SENSITIVE_MESSAGE);
      }
    }
    

    然后我们将此过滤器对象设置绑定到 Logger 中即可。

    package com.chenpi;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.logging.LogManager;
    import java.util.logging.Logger;
    import org.junit.Test;
    
    /**
     * @author 陈皮
     * @version 1.0
     * @description
     * @date 2022/3/2
     */
    public class ChenPiJULMain {
    
      // JUL 日志框架演示
      @Test
      public void testLog() throws IOException {
    
        // 读取配置文件
        InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
            .getResourceAsStream("logging.properties");
        // 获取LogManager
        LogManager logManager = LogManager.getLogManager();
        // 记载配置文件
        logManager.readConfiguration(resourceAsStream);
    
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
        // Logger关联过滤器
        logger.setFilter(new MyLoggerFilter());
    
        // 日志记录输出
        logger.info(">>>>> Hello ChenPi!!");
        logger.info(">>>>> 暴力小孩!!");
      }
    }
    
    // 输出结果如下
    信息: >>>>> Hello ChenPi!! [星期三 三月 02 15:31:06 CST 2022]
    

    本次分享到此结束啦~~

    我是陈皮,一个在互联网 Coding 的 ITer。如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!

    展开全文
  • 主要介绍了Java日志框架之logback使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • java日志框架详解总结(含配置)

    千次阅读 2018-05-25 10:17:41
    该篇是集合了百度众多的日志框架详解java日志框架分析的总结篇。 具体网址: https://blog.csdn.net/foreverling/article/details/51385128 https://blog.csdn.net/chszs/article/details/8653460 ...

    该篇是集合了百度众多的日志框架详解,java日志框架分析的总结篇。
    具体网址:

    https://blog.csdn.net/foreverling/article/details/51385128
    https://blog.csdn.net/chszs/article/details/8653460
    http://baijiahao.baidu.com/s?id=1585361583532845302&wfr=spider&for=pc
    http://www.importnew.com/28541.html
    https://blog.csdn.net/victor_cindy1/article/details/78716917
    https://zhuanlan.zhihu.com/p/24275518
    https://my.oschina.net/aiguozhe/blog/87981
    http://www.importnew.com/28541.html
    https://blog.csdn.net/xktxoo/article/details/76359299

    主要的就是这些了。
    java里常见的日志库有java.util.logging(JDKlog)、Apache log4j、log4j2、logback、slf4j等等。
    这么多的日志框架里如何选择。
    首先需要梳理的是日志接口,以及日志接口对应的实现。然后再考虑如何选择使用哪个日志框架。

    由图可知我们经常使用的日志框架实质上是一些接口的实现。而目前使用较广泛的统一日志规范接口就是slf4j和commons-logging。

    slf4j

       它是基于API的java日志框架,slf4j提供了简单统一的日志记录的接口,开发者在配置部署
    时只需要是吸纳这个接口就能是实现日志功能。它自身并没有提供具体的日志解决方案,它是负责
    服务于各种各样的日志系统,允许用户在部署应用上使用自己常用的日志框架。
        也就是说,SLF4j是一个抽象层,它提供了众多的适配器能是配合其他所有开源日志框架。
    为了考虑其他项目会使用大量的第三方库,而第三方库使用的日志框架又各不相同,不同的日志框
    架又需要不同的配置,不同配置就会导致日志输出到不同的位置。所以我们就需要一个可以将日志
    level、日志输出等统一管理,而slf4j的适配器又对各种日志都实现了接管,接管后就可以统一
    配置这些第三方库中使用的日志。

    commons-logging

    commons-logging(jcl)也为众多日志实现库提供了统一的接口,作用和slf4j类似。它允许运行时绑定
    任意的日志库。但commons-loggins对log4j和java.util.logging的配置问题兼容性不太好,还会
    遇到类加载问题。所以当时log4j的作者又创作了slf4j.....

    有了日志接口,就需要选择实现框架。在众多的日志框架如何选择

    log4j

    Log4j是apache下一个功能非常丰富的java日志库实现,Log4j应该是出现比较早而且最受欢迎的java
    日志组件,它是基于java的开源的日志组件。Log4j的功能非常强大,通过Log4j可以把日志输出到控制
    台、文件、用户界面。也可以输出到操作系统的事件记录器和一些系统常驻进程。值得一提的是:Log4j可
    以允许你非常便捷地自定义日志格式和日志等级,可以帮助开发人员全方位的掌控自己的日志信息是一个日
    志开源框架。

    logback

    logback是log4j的改进版本,而且原生支持slf4j(因为是同一作者开发)。所以logback+slf4j的组
    合是日志框架的最佳选择。logback解决了log4j不能使用占位符的问题。logback可以通过jmx修改日
    志配置,可以从jmx控制台直接操作,无需重启应用程序。它相比于log4j有更多的优点:
    
     - 原生实现了slf4j API(log4j需要中间层转换)
     - 支持xml、Groovy方式配置
     - 支持配置文件中加入条件判断
     - 更强大的过滤器
     - 更充分的测试
     - 更丰富的免费文档
     - 自动重载有变更的配置文件
     - 自动压缩历史日志
     等等
    

    Log4j2

    log4j2是log4j的升级版本2.x但并不兼容log4j因为它基本上把log4j版本的核心全部重构掉了,log4j2设计上很大程度模仿了slf4j-logback,性能也
    获得了很大的提升。具体的差异自行百度。

    至于网上不少说的性能比较,应该都是单个框架比较。但与slf4j绑定使用的话,怎么说都是logback性能最佳吧(猜测,欢迎指正)


    日志级别

    日志level定义应该遵循以下几个:
    
     - debug: 完整详细的记录流程的关键路径,用于开发人员比较感兴趣的跟踪和调试信息,生产环境中正常不会打开debug状态
     - info: 应简洁明确让管理员确定状态,记录相当重要有意义的信息。关键的系统参数的回显、后台服务的初始化状态、需要系统管理员确认的关键信息都需要使用info级别
     - warn: 能清楚告知发生了什么情况,指示潜在问题能引起别人重视,但不一定需要处理
     - error: 系统出现异常或不希望出现的问题,能及时得到关注处理。但也不是所有的异常都记录成error
    

    根据上述,这里将以slf4j为日志接口,而其中logback与slf4j配合桥接性能最优,所以在日志实现上选择logback。
    此处以maven进行管理,首先引入依赖,这两个依赖就够了,版本可根据需求修改

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

    classpath中编写logback的配置文件logback.xml, logback.xml文件放在/src/main/resources/路径中

    logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 
    scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 
    scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    -->
    <configuration scan="true" scanPeriod="60 seconds" debug="false">
    
        <!-- 上下文变量设置,用来定义变量值,其中name的值是变量的名称,value的值时变量定义的值。
            通过<property>定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
        <property name="CONTEXT_NAME" value="logback-test" />
    
        <!-- 上下文名称:<contextName>, 每个logger都关联到logger上下文,
            默认上下文名称为“default”。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。
            一旦设置,不能修改。 -->
        <contextName>${CONTEXT_NAME}</contextName>
    
        <!-- <appender>是<configuration>的子节点,是负责写日志的组件。
            有两个必要属性name和class。
            name指定appender名称,
            class指定appender的实现类。 -->
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <!-- 对日志进行格式化。 -->
            <encoder>
                <pattern>
                    %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
                </pattern>
            </encoder>
        </appender>
    
        <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。 -->
            <file>${logs.dir}/logback-test.log</file>
    
            <!-- 当发生滚动时的行为  -->
            <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
                <!-- 必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip -->
                <FileNamePattern>${logs.dir}/logback-test.%i.log</FileNamePattern>
                <!-- 窗口索引最小值 -->
                <minIndex>1</minIndex>
                <!-- 窗口索引最大值 -->
                <maxIndex>1</maxIndex>
            </rollingPolicy>
    
            <!-- 激活滚动的条件。 -->
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <!-- 活动文件的大小,默认值是10MB -->
                <maxFileSize>30MB</maxFileSize>
            </triggeringPolicy>
    
            <!-- 对记录事件进行格式化。 -->
            <encoder>
                <charset>UTF-8</charset>
                <Pattern>
                    %d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n
                </Pattern>
            </encoder>
        </appender>
    
        <!-- 特殊的<logger>元素,是根logger。只有一个level属性,应为已经被命名为"root".
            level:设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。
            <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger。 -->
        <root>
            <level value="WARN" />
            <appender-ref ref="stdout" />
            <appender-ref ref="file" />
        </root>
    
        <!-- 用来设置某一个 包 或者具体的某一个 类 的日志打印级别、以及指定<appender>, 
            name:用来指定受此logger约束的某一个包或者具体的某一个类。
            level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级的级别。 
            additivity:是否向上级logger传递打印信息。默认是true。(这个logger的上级就是上面的root)
            <logger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。-->
        <logger name="xuyihao.logback.test" level="DEBUG" additivity="true"></logger>
    
    </configuration>
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,707
精华内容 12,282
关键字:

java日志框架详解