日志那些事之二—java日志框架的比较与选择

上一篇(日志那些事之一—java日志框架分类)主要介绍了Java中常用的日志框架以及不同的日志级别,本篇将针对上一篇提到的几种日志框架做详细地介绍,并说明不同的日志框架在实际的代码中如何使用,具体的日志demo,如下:

https://github.com/OnlyIsssilence/loggerDemo

一.项目中如何选取日志框架

1 Commons Logging与Slf4j实现机制对比

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

2 选择日志框架

如果是在一个新的项目中建议使用Slf4j与Logback组合,这样有如下的几个优点:

  • Slf4j实现机制决定Slf4j限制较少,使用范围更广。由于Slf4j在编译期间,静态绑定本地的LOG库使得通用性要比Commons logging要好。
  • Logback拥有更好的性能。Logback声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在Logback中需要3纳秒,而在Log4J中则需要30纳秒。LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的。
  • Commons Logging开销更高 在使Commons Logging时为了减少构建日志信息的开销,通常的做法是:
    if(log.isDebugEnabled()){
    log.debug("User name: " +
    user.getName() + " buy goods id :" + good.getId());
    }
    Slf4j阵营,你只需这么做:
    log.debug(“User name:{} ,buy goods id :{}”, user.getName(),good.getId());
    也就是说,slf4j把构建日志的开销放在了它确认需要显示这条日志之后,减少内存和cup的开销,使用占位符号,代码也更为简洁;
  • Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档。

3 如何在项目中使用Slf4j

Slf4j与其他各种日志组件的“拼接”方案,早已经被有了开源的api包,如表2所示:

拼接之后的jar包名称 说明
Slf4j-logj12-1.7.13.jar Log4j1.2版本的桥接器,需要将log4j.jar加入到classpath中
Slf4j-jdk14-1.7.13.jar Java.util.logging的桥接器,JDK原生日志框架
Slf4j-jcl-1.7.13.jar Jakarta commons logging的桥接器,这个桥接器将SLF4J所有日志委派给JCL
Logback-classic-1.0.13.jar(需要logback-core-1.0.13.jar) Slf4j的原生实现,logback直接实现了slf4j的接口,因此使用slf4j与logback的结合使用也意味着更小的内存与计算开销
slf4j-simple-1.7.13.jar 一个简单实现的桥接器,该实现输出所有事件到system.err,只有info以及高于该级别的消息被打印
Slf4j-nop-1.7.13 NOP桥接器,丢弃一切日志

4 如何桥接遗留的api

在实际环境中我们经常会遇到不同的组件使用的日志框架不同的情况,例如Spring Framework使用的是日志组件是Commons logging,XSocket依赖的则是Java Util Logging。当我们在同一项目中使用不同的组件时应该如果解决不同组件依赖的日志组件不一致的情况呢?现在我们需要统一日志方案,统一使用SLF4J,把他们的日志输出重定向到SLF4J,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。Slf4j带有几个桥接模块,可以重定向log4j,JCL和java.util.logging中的API到Slf4j。

重定向包名 作用
Log4j-over-slf4j-version.jar 将log4j重定向到slf4j将commons logging里的simple logger重定向到slf4j
Jul-to-slf4j-version.jar 将java util logging重定向到slf4j

日志那些事之二—java日志框架的比较与选择_第1张图片

5 使用slf4j桥接要注意事项

在使用slf4j桥接时要注意避免形成死循环,在项目依赖的jar包中不要存在以下情况。如下表4所示:

多个日志jar包形成死循环的条件 产生的原因
Log4j-over-slf4j.jar和slf4j-log4j12.jar同时存在 由于slf4j-log4j12.jar的存在会将所有的日志调用委托给log4j,但由于Log4j-over-slf4j.jar的存在,会将所有对log4j.api的调用委托给相应等值的slf4j,所有它们同时存在会出现死循环
Jul-to-slf4j.jar和slf4j-jdk14.jar同时存在 由于slf4j-jdk14.jar的存在会将所有的日志调用委托给jdk的log,但由于Jul-to-slf4j.jar的存在,会将所有对jul.api的调用委托给相应等值的slf4j,所有它们同时存在会出现死循环

二、日志框架的具体使用

1 Jdk原生日志

Pom包与使用的包类文件:无依赖,有JDK即可
sun本身自带的日志工具

import java.util.logging.LogManager;
import java.util.logging.Logger;
public final static Logger logger = LogManager.getLogManager().getLogger("");
public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        logger.info("日志测试!");
    }

输出结果:

Hello World!
三月 08, 2018 12:07:42 上午 java.util.logging.LogManager$RootLogger log
信息: 日志测试!

2 Log4j日志使用

Pom包与使用的包类文件:

/**
 * apach下的log4j日志工具
 * import org.apache.log4j.LogManager;
 * import org.apache.log4j.Logger;
 *
 * 
 * log4j
 * log4j
 * 1.2.17
 * 
 */
  public final static Logger logger = LogManager.getLogger(App.class.getName());

输出结果:

Hello World!
[2018-03-08  00:09:49 INFO] com.onlyisssilence.muya.App:78 - 日志测试!

注意:
使用log4j与log4j2日志框架时,由于它们需要读取对应的日志”模型”——appender、logger,通俗地说即是日志的输出格式,日志输出到哪的参数,此时需要分别在工程的resource位置(也可以是其他的位置,但是一定要通过代码的方式显示地告诉编译器日志配置文件的位置)上添加对应格式的配置文件,log4j的配置文件如下所示:



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

    
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd  HH:mm:ss\} %p] %c:%L - %m%n"/>
        layout>
    appender>

    
    <root>
        <priority value="debug"/>
        <appender-ref ref="console"/>
    root>

log4j:configuration>

3 Log4j2日志框架的使用

Pom包与使用的包类文件:
为了偷懒直接使用了如下图中的pom依赖
日志那些事之二—java日志框架的比较与选择_第2张图片
这里简单分析一下这个依赖,如上图所示,pom工程实际上只依赖了一个spring-boot-starter-log4j2的包,然后这个包有分别直接依赖了后面的5个,上图中红色圈圈中的三个包是使用log4j2日志框架的核心包,如果在工程中不导入spring-boot-starter-log4j2包,而直接导入红圈中三个包也是可行的,如下所示:


    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-apiartifactId>
      <version>2.0.1version>
    dependency>
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-coreartifactId>
      <version>2.0.1version>
    dependency>
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-slf4j-implartifactId>
      <version>2.0.1version>
    dependency>

同log4j一样,所需要加入的配置文件:


<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5p] %d %c - %m%n"/>
        Console>
    Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        Root>
    Loggers>
Configuration>

4 Logback日志框架

Pom包与使用的包类文件:

/**
     * Logback 基于slf4j
     * import org.slf4j.Logger;
     * import org.slf4j.LoggerFactory;
     *
     * 
     * ch.qos.logback
     * logback-classic
     * 1.2.3
     * 
     */
    public final static Logger logger = LoggerFactory.getLogger(App.class.getName());

输出结果

Hello World!
00:23:45.812 [main] INFO com.onlyisssilence.muya.App - 日志测试!

从结果上来看可以发现log4j2与logback日志一样,他们都是实现的是slf4j的日志接口

5 Commons logging日志框架

Pom包与使用的包类文件:

/**
     * common logging 包装了sun自己的log工具和apach下的log4j工具
     * import org.apache.commons.logging.Log;
     * import org.apache.commons.logging.LogFactory;
     *
     * 
     * commons-logging
     * commons-logging
     * 1.2
     * 
     */
    public final static Log logger = LogFactory.getLog(App.class.getName());

输出结果

Hello World!
三月 08, 2018 12:29:32 上午 com.onlyisssilence.muya.App main
信息: 日志测试!

注意:如第二节中提到的,commons logging日志框架接口,其内部其实已经有了两种日志的实现:JDK的log和log4j的log,当用户没有配置log4j相关的依赖以及配置文件时,获取的实际上是JDK的log打印日志,上面的结果即是JDK的log打印出来的,当添加log4j依赖,并配置好log4j.xml配置文件时,依赖包如下:

<dependency>
      <groupId>log4jgroupId>
      <artifactId>log4jartifactId>
      <version>1.2.17version>
    dependency>
    
    
    <dependency>
      <groupId>commons-logginggroupId>
      <artifactId>commons-loggingartifactId>
      <version>1.2version>
    dependency>

打印的日志如下:

Hello World!
[2018-03-08  00:31:15 INFO] com.onlyisssilence.muya.App:78 - 日志测试!

你可能感兴趣的:(项目积累)