• Java 日志系列(三):日志使用示例及常见报错


    承接前面两篇文章(《Java 日志系列一》和 《Java 日志系列二》),本文将介绍几种主流日志框架的使用示例和常见的报错。为了便于读者理解,文中的报错案例力求信息完整,并给出了测试代码,感兴趣的读者,可以通过示例快速实践。

    1.日志框架使用示例

    1.1 Log4j 使用示例

    • maven 依赖:
    <dependency>
      <groupId>log4jgroupId>
      <artifactId>log4jartifactId>
      <version>1.2.17version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 配置文件:
      log4j.properties
    ### 设置
    log4j.rootLogger = debug,stdout,INFO
    ### 输出信息到控制台,测试的时候便于观察,实际应用中不配置 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
    ### 输出 INFO 级别以上的日志到指定文件
    log4j.appender.INFO = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.INFO.File = ~/Code/logs/log4j.log
    log4j.appender.INFO.Append = true
    log4j.appender.INFO.Threshold = info 
    log4j.appender.INFO.layout = org.apache.log4j.PatternLayout
    log4j.appender.INFO.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 测试代码:
    public class Log4jTest {
        private static final Logger LOGGER = Logger.getLogger(Log4jTest.class);
    
        public static void main(String[] args) {
    
            LOGGER.info("log test");
            try {
                ((Object) null).toString();
    
            } catch (Exception e) {
                LOGGER.info("exception info", e);
            }
            LOGGER.info("This is log4j. Thread=" + Thread.currentThread().getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 日志输出:
    2019-10-31 21:05:36  [ main:0 ] - [ INFO ]  log test
    2019-10-31 21:05:36  [ main:5 ] - [ INFO ]  exception info
    java.lang.NullPointerException
            at Log4jTest.main(Log4jTest.java:18)
    2019-10-31 21:05:36  [ main:9 ] - [ INFO ]  This is log4j. Thread=main
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2 commons-logging 使用示例

    本例中,使用 Commons-Logging (也称为 JCL) 作为日志门面提供统一的日志接口,Log4j 作为具体的日志实现。

    • maven 依赖:
    <dependency>
      <groupId>commons-logginggroupId>
      <artifactId>commons-loggingartifactId>
      <version>1.2version>
    dependency>
    <dependency>
      <groupId>log4jgroupId>
      <artifactId>log4jartifactId>
      <version>1.2.17version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 配置文件

    1-commons-logging.properties

    org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
    
    • 1

    2-log4j.properties

    ### 设置
    log4j.rootLogger = debug,stdout,INFO
    ### 输出信息到控制台 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
    ### 输出 INFO 级别以上的日志到指定文件
    log4j.appender.INFO = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.INFO.File = ~/Code/logs/jcllog4j.log
    log4j.appender.INFO.Append = true
    log4j.appender.INFO.Threshold = info 
    log4j.appender.INFO.layout = org.apache.log4j.PatternLayout
    log4j.appender.INFO.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 测试代码:
    public class JclTest {
        private static final Log LOGGER = LogFactory.getLog(JclTest.class);
    
        public static void main(String[] args) {
    
            LOGGER.info("log test");
            try {
                ((Object) null).toString();
    
            } catch (Exception e) {
                LOGGER.info("exception info", e);
            }
            LOGGER.info("This is jcl log. Thread=" + Thread.currentThread().getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 日志输出:
    2019-10-31 21:30:42  [ main:0 ] - [ INFO ]  log test
    2019-10-31 21:30:42  [ main:4 ] - [ INFO ]  exception info
    java.lang.NullPointerException
            at JclTest.main(JclTest.java:19)
    2019-10-31 21:30:42  [ main:6 ] - [ INFO ]  This is jcl log. Thread=main     
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.3 slf4j 使用示例

    本例中,使用 Slf4j 作为日志门面提供统一的日志接口,Logback 作为具体的日志实现。

    • maven 依赖:
    <dependency>
      <groupId>org.slf4jgroupId>
      <artifactId>slf4j-apiartifactId>
      <version>1.7.28version>
    dependency>
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-classicartifactId>
      <version>1.2.3version>
    dependency>
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-coreartifactId>
      <version>1.2.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 配置文件:
      logback.xml
    <configuration debug="true" scan="true" scanPeriod="1 seconds">
        <contextName>logbackcontextName>
        <property name="app.name" value="~/Code"/>
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>DEBUGlevel>
            filter>
            <encoder>
                <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%npattern>
            encoder>
        appender>
        <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${app.name}/logs/slf4jlogback.logfile>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${app.name}/logs/slf4jlogback.%d{yyyy-MM-dd.HH}.log.gzfileNamePattern>
                <maxHistory>60maxHistory>
                <totalSizeCap>20GBtotalSizeCap>
            rollingPolicy>
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <maxFileSize>100MBmaxFileSize>
            triggeringPolicy>
            <encoder>
                <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%npattern>
            encoder>
        appender>
        <root level="info">
            <appender-ref ref="stdout"/>
            <appender-ref ref="file"/>
        root>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 测试代码:
    public class Slf4jTest {
        private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
    
        public static void main(String[] args) {
    
            LOGGER.info("log test");
            try {
                ((Object) null).toString();
    
            } catch (Exception e) {
                LOGGER.info("exception info", e);
            }
            LOGGER.info("This is slf4jLogback log. Thread=" + Thread.currentThread().getName());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 日志输出:
    019-10-31 21:26:17,806 [main] INFO  Slf4jTest [Slf4jTest.java : 17] - log test
    2019-10-31 21:26:17,811 [main] INFO  Slf4jTest [Slf4jTest.java : 22] - exception info
    java.lang.NullPointerException: null
            at Slf4jTest.main(Slf4jTest.java:19)
    2019-10-31 21:26:17,811 [main] INFO  Slf4jTest [Slf4jTest.java : 24] - This is slf4jLogback log. Thread=main
    
    • 1
    • 2
    • 3
    • 4
    • 5


    2.常见报错案例

    2.1 找不到绑定器

    如下图所示,应用使用的是 Slf4j 标准接口,目标日志框架为 Log4j,但是没有引入绑定器(slf4j-log4j12)。
    在这里插入图片描述

    • maven 依赖:
    <dependencies>
      
      <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-apiartifactId>
        <version>1.7.28version>
      dependency>
      
      <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
      dependency>
      
      
      
      
      
      
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行测试代码将会报错,提示加载绑定器失败,具体内容如下:

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    
    • 1
    • 2
    • 3

    2.2 配置了多个绑定器

    如下图所示,应用使用的是 Slf4j 标准接口,目标日志框架为 Log4j,但是同时引入了两个日志绑定器。

    在这里插入图片描述

    存在问题的 maven 依赖如下所示:

    
    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-apiartifactId>
        <version>1.7.28version>
    dependency>
    
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-log4j12artifactId>
        <version>1.7.28version>
    dependency>
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-slf4j-implartifactId>
        <version>2.12.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这种情况下,将无法按照预期打印日志,应用会加载默认的日志配置,将日志打印到控制台,详细的错误信息如下:

    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/Users/test/.m2/repository/org/slf4j/slf4j-log4j12/1.7.28/slf4j-log4j12-1.7.28.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/Users/test/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 循环依赖

    如下图,应用使用的是 Slf4j 标准接口,目标日志框架为 Log4j,但是同时引入了两个日志绑定器: log4j-over-slf4j 和 slf4j-log4j12,如此将会出现循环依赖。

    在这里插入图片描述

    存在循环依赖问题的 maven 配置如下:

    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-apiartifactId>
        <version>1.7.28version>
    dependency>
    
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-log4j12artifactId>
        <version>1.7.28version>
    dependency>
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>log4j-over-slf4jartifactId>
        <version>1.7.28version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    出现循环依赖问题时,日志系统将无法正常启动,进而导致应用无法启动,详细的错误信息如下:

    SLF4J: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. 
    SLF4J: See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
    Exception in thread "main" java.lang.ExceptionInInitializerError
        at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:72)
        at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:45)
        at org.slf4j.LoggerFactory.bind(LoggerFactory.java:150)
        at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)
        at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:412)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:383)
        at Log4jTest.<clinit>(Log4jTest.java:13)
    Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
        at org.slf4j.impl.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:54)
        ... 8 more
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    IDEA中的神仙插件——Smart Input (自动切换输入法)
    Ninja: Towards Transparent Tracing and Debugging on ARM【TEE的应用】
    如何设计一个 JVM 语言下的 LLM 应用开发框架?以 Chocolate Factory 为例
    Selenium进行无界面爬虫开发
    FreeRTOS深入教程(任务的引入及栈的作用)
    来啦来啦|开源 * 安全 * 赋能 - .NET Conf China 2022
    学编程的第十八天
    RabbitMQ初步到精通-第八章-Java-AMQP-Client源码分析
    力扣100114. 元素和最小的山形三元组 II(中等)
    暑假打工 2 个 月,让我明白了 Keepalived 高可用的三种路由方案
  • 原文地址:https://blog.csdn.net/Jin_Kwok/article/details/132796572