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

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

1.日志框架使用示例

1.1 Log4j 使用示例

  • maven 依赖:
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
  • 配置文件:
    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
  • 测试代码:
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());
    }
}
  • 日志输出:
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 commons-logging 使用示例

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

  • maven 依赖:
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
  • 配置文件

1-commons-logging.properties

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

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

  • 测试代码:
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());
    }
}
  • 日志输出:
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.3 slf4j 使用示例

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

  • maven 依赖:
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.28</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>1.2.3</version>
</dependency>
  • 配置文件:
    logback.xml
<configuration debug="true" scan="true" scanPeriod="1 seconds">
    <contextName>logback</contextName>
    <property name="app.name" value="~/Code"/>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${app.name}/logs/slf4jlogback.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${app.name}/logs/slf4jlogback.%d{yyyy-MM-dd.HH}.log.gz</fileNamePattern>
            <maxHistory>60</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>100MB</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="file"/>
    </root>
</configuration>
  • 测试代码:
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());
    }
}

  • 日志输出:
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


2.常见报错案例

2.1 找不到绑定器

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

  • maven 依赖:
<dependencies>
  <!-- slf4j -->
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.28</version>
  </dependency>
  <!-- Log4j2 -->
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
  </dependency>
  <!-- binder -->
  <!--<dependency>-->
  <!--<groupId>org.slf4j</groupId>-->
  <!--<artifactId>slf4j-log4j12</artifactId>-->
  <!--<version>1.7.28</version>-->
  <!--</dependency>-->
</dependencies>

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

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.

2.2 配置了多个绑定器

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

在这里插入图片描述

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


<!-- slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.28</version>
</dependency>
<!-- Log4j2 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- binder -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.28</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.12.1</version>
</dependency>

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

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]

2.3 循环依赖

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

在这里插入图片描述

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

<!-- slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.28</version>
</dependency>
<!-- Log4j2 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- binder -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.28</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.28</version>
</dependency>

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

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jin_Kwok

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值