Java常用的日志框架对比

  1. 云栖社区>
  2. 博客>
  3. 正文

Java常用的日志框架对比

ben4 2018-07-18 10:05:00 浏览1542
展开阅读全文

作为Java开发人员,对于日志记录框架一定非常熟悉。而且几乎在所有应用里面,一定会用到各种各样的日志框架用来记录程序的运行信息。而对于一个成熟的Java应用,这个是必不可少的。在开发和调试阶段,日志可以帮助我们更快的定位问题;而在应用的运维过程中,日志系统又可以帮助我们记录大部分的异常信息,通常很多企业会通过收集日志信息来对系统的运行状态进行实时监控预警。

总体概览

image.png

目前的日志框架有JDK自带的logginglog4j1log4j2logback ,这些框架都自己定制了日志 API ,并且有相应的实现;目前用于实现日志统一的框架 Apache commons-loggingslf4j ,遵循面向接口编程的原则,这两大框架可以让用户在程序运行期间去选择具体的日志实现系统(log4j1\log4j2\logback等)来记录日志,是统一抽象出来的一些接口。

日志级别

log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),
优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

ALL 最低等级的,用于打开所有日志记录。
TRACE 很低的日志级别,一般不会使用。
DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。这个可以用于生产环境中输出程序运行的一些重要信息。
WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给开发者的一些提示。
ERROR 指出发生错误的信息,可能会导致系统出错或是宕机等,必须要避免
FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
OFF 最高等级,用于关闭所有日志记录。

Log4j

官网地址:https://logging.apache.org/log4j/1.2/

简介:
Apache 的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;用户也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,用户能够更加细致地控制日志的生成过程。这些可以通过一个 配置文件来灵活地进行配置,而不需要修改程序代码。

在JDK 1.3及以前,Java打日志依赖System.out.println(), System.err.println()或者e.printStackTrace(),Debug日志被写到STDOUT流,错误日志被写到STDERR流。这样打日志有一个非常大的缺陷,即无法定制化,且日志粒度不够细。log4j是在这样的环境下诞生的,它是一个里程碑式的框架,它定义的LoggerAppenderLevel等概念如今已经被广泛使用。

https://mvnrepository.com/中可以查到,log4j1从2005年11月更新到2012年3月,后面就没再更新了
最新的依赖(May 26, 2012)

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

2015年8月5日,项目管理委员会宣布Log4j 1.x已达到使用寿命。建议用户使用Log4j 1升级到Apache Log4j 2

Log4j2

官网地址:https://logging.apache.org/log4j/2.x/
Log4j2Log4j1的升级版本。Log4j2基本上把Log4j1版本的核心全部重构掉了,而且基于Log4j1做了很多优化和改变。并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。

Log4j2最新的依赖(Mar 11, 2018)

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>

SpringBoot也有Log4j2相关的依赖
不过SpringBoot自带的jar包已经够用了

image.png

引入依赖(Jun 14, 2018)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

引入之后可以看到外部jar包多了三个关于log4j2的


image.png

关于上面的包
log4j-to-slf4jlog4j2的接口适配到slf4j上,不能和log4j-slf4j-impl同时存在
log4j-api包含.class但是只是一堆接口而已,实际使用需要log4j
log4j-core 包含.class.java也就是源码

jul 指的是java.util.logging,是 java 内置的日志模块
简介:受Log4j启发,Sun在Java1.4版本中引入了java.util.logging,但是jul功能远不如log4j完善,开发者需要自己编写Appenders(Sun称之为Handlers),且只有两个Handlers可用(ConsoleFile),julJava1.5以后性能和可用性才有所提升。

不是SpringBoot项目需要引用jul的话可以加

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jul</artifactId>
    <version>2.11.0</version>
</dependency>

Log4j2的使用

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SpringcloudEurekaApplication {
    static Logger logger = LogManager.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}

启动项目后控制台输出为

image.png

SLF4J

官网地址:https://www.slf4j.org/
SLF4J(Simple Logging Facade for Java)用作各种日志框架(java.util.logging,logback,log4j)的简单外观或抽象,允许最终用户在部署 时插入所需的日志框架。

最新依赖(Mar 21, 2018)

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.8.0-beta2</version>
</dependency>

Springboot项目不需要引入任何依赖都可以使用

用法

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpringcloudEurekaApplication{
    private static final Logger logger = LoggerFactory.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}

启动项目后控制台输出为

image.png

通常输出日志开销非常大,SLF4J通过{}作为占位符的方式输出字符串,相比字符串拼接的方式,效率有显著的提升。

logback

官网地址:https://logback.qos.ch/
logbacklog4j是同一个作者创作,它是log4j的升级版
Logback的体系结构足够通用,以便在不同情况下应用。

logback分为三个模块:logback-corelogback-classiclogback-access

logback-core模块为其他两个模块奠定了基础。
logback-classic模块可以被同化为log4j的显着改进版本。logback-classic本身实现了SLF4J API,因此您可以在logback和其他日志框架(如log4jjava.util.logging(JUL))之间来回切换。
logback-access模块​​与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能。可以在logback-core之上轻松构建自己的模块。

Logback的核心对象:Logger、Appender、Layout

Logback主要建立于LoggerAppenderLayout 这三个类之上。
Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。Logger对象一般多定义为静态常量.
Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQLPostreSQLOracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
Layout:负责把事件转换成字符串,格式化的日志信息的输出。

具体使用

引入依赖(Feb 11, 2018),分别对应logback上的三个模块

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>

注:如果在SpringBoot下是不需要引入的
Spring Boot内部日志系统使用的是Commons Logging,但开放底层的日志实现。默认为会Java Util Logging, Log4J, Log4J2和Logback提供配置。每种情况下都会预先配置使用控制台输出,也可以使用可选的文件输出。

因为在SpringBoot中本身就内置了日志功能,在spring-boot-starter依赖中

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

logback的配置介绍

如果没有配置文件,那么logback默认地会调用BasicConfigurator,创建一个最小化配置。最小化配置由一个关联到根 loggerConsoleAppender组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%nPatternLayoutEncoder进行格式化。root logger 默认级别是 DEBUG

官方推荐

官方推荐使用的xml名字的格式为:logback-spring.xml而不是logback.xml,因为带spring后缀的可以使用<springProfile>这个标签。

Logback配置文件的基本结构

<configuration>开头,后面有任意个<appender>元素,有任意个<logger>元素,有最多一个<root>元素。
Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTDXML schema进行定义。

1、根节点<configuration>,包含下面三个属性:

scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scantrue时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false

<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
  <!--省略其他配置--> 
</configuration>

2.子节点,分别有<appender>,<root>,<logger>,<property>,<contextName>,<conversionRule>,<springProfile>

1.子节点<appender>:Logback将执行日志事件输出的组件称为Appender,实现的Appender必须继承 ch.qos.logback.core.Appender接口

它有两个必要属性nameclassname指定appender名称,class指定appender的全限定名
注:class="ch.qos.logback.core.rolling.RollingFileAppender"常见的日志输出到文件,随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。RollingFileAppender用于切分文件日志

<appender>有四个子节点

<encoder>:对日志进行格式化。必须指定,否则不会往文件输出内容
%d{HH: mm:ss.SSS}——日志输出时间
%thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用
%-5level——日志级别,并且使用5个字符靠左对齐
%logger{36}——日志输出者的名字
%msg——日志消息
%n——平台的换行符

<rollingPolicy>:循环政策:基于时间创建日志文件。当发生日志切换时,RollingFileAppender的切换行为。例如日志文件名的修改
常用子节点
<maxHistory>表示只保留最近天数的日志,最好设置下,以防止日志填满整个磁盘空间。
<timeBasedFileNamingAndTriggeringPolicy><maxFileSize>是对日志大小进行切割,设置每个日志文件的最大值

<filter>:过滤器,执行一个过滤器会有返回个枚举值。
返回DENY,日志将立即被抛弃不再经过其他过滤器;
返回NEUTRAL,有序列表里的下个过滤器过接着处理日志;
返回ACCEPT,日志会被立即处理,不再经过剩余过滤器。

<Appender> 添加一个或多个过滤器后,可以用任意条件对日志进行过滤。<Appender> 有多个过滤器时,按照配置顺序执行。

最常用的过滤器

LevelFilter: 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMathonMismatch接收或拒绝日志。
有以下子节点:
<level>:设置过滤级别
<onMatch>:用于配置符合过滤条件的操作
<onMismatch>:用于配置不符合过滤条件的操作

<file>:指定正在记录的日志文件的路径及文件名。
注意在windows当中,反斜杠\ 需要转义,或直接使用/也可以。例如 c:/temp/test.logc:\\temp\\test.log都可以。可以是相对目录,也可以是绝对目录,没有默认值,如果上层目录不存在,FileAppender会自动创建。

我测试时放在日志文件是放在E盘下log文件夹(目录设置下文有提到)

image.png

<appender>节点定义示例

<!-- 时间滚动输出 level为 INFO 日志 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_info.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

2.子节点<property>:用来定义变量值,它有两个属性namevalue,通过<property>定义的值会被插入到logger上下文中,可以使“${}”来使用变量。

<property name="log.path" value="E:/log" />

在上面定义文件路径中有使用到${log.path}

3.子节点<contextName>:用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。

    <contextName>myAppLogBack</contextName> 

4.子节点<logger>:用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>

<logger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALLOFF
addtivity: 是否向上级logger传递打印信息。默认是true

<logger name="org.springframework.web" level="info" addtivity="true"/>

5.子节点<root>:它也是<logger>元素,但是它是根logger,是所有<logger>的上级。

root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性,因为name已经被命名为root,且已经是最上级了。
level:用来设置打印级别,不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger

<root level="all">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="DEBUG_FILE" />
    <appender-ref ref="INFO_FILE" />
    <appender-ref ref="WARN_FILE" />
    <appender-ref ref="ERROR_FILE" />
</root>

6.子节点<springProfile>:Spring profileSpring 3引入的概念,主要用在项目多环境运行的情况下,通过激活方式实现多环境切换,省去多环境切换时配置参数和文件的修改,相比较Maven profile简单实用,易于上手。并且Spring profile提供了多种激活方法,例如配置文件,注解,jvm参数设置等等

据不同环境(prod:生产环境,test:测试环境,dev:开发环境)来定义不同的日志输出,在 logback-spring.xml中使用 springProfile 节点来定义,方法如下:

<!-- 测试环境+开发环境. 多个使用逗号隔开. -->
<springProfile name="test,dev">
    <logger name="com.example.springcloudeureka" level="info" />
</springProfile>
<!-- 生产环境. -->
<springProfile name="prod">
    <logger name="com.example.springcloudeureka" level="ERROR" />
</springProfile>

可以启动服务的时候指定 profile ,springboot的话可以在配置文件yaml中设置spring.profiles.active

7.子节点<conversionRule>:logback 自定义Pattern模板

例如需要在每条日志都输出logback output字符串,可以这样做
写一个转换器类,继承ClassicConvert

public class LogbackConvert extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent iLoggingEvent) {
        return "logback output";
    }
}

实现里面的方法,返回的都是String类型
logback配置文件中中注册该转换器,并自定义转换符

<conversionRule conversionWord="logback" 
converterClass="com.example.springcloudeureka.LogbackConvert"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" > 
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %logback %n</pattern>
</appender>

<appender>中自定义Pattern模板,用%转换器名即可
效果

image.png

都打印出logback output字符串

Apache Commons Logging

官方地址:https://commons.apache.org/proper/commons-logging/
Jakarta Commons-logging(JCL)是apache最早提供的日志的门面接口。提供简单的日志实现以及日志解耦功能。
commons-loggingApache commons类库中的一员。Apache commons类库是一个通用的类库,提供了基础的功能,比如说commons-fileuploadcommons-httpclientcommons-iocommons-codes等。

commons-logging能够选择使用Log4j还是JDK Logging,但是不依赖Log4jJDK LoggingAPI。如果项目的classpath中包含了log4j的类库,就会使用log4j,否则就使用JDK Logging。使用commons-logging能够灵活的选择使用那些日志方式,而且不需要修改源代码。

不过现在Apache Commons Logging也不更新了,最新的依赖(Jul 05, 2014)

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

具体实现

JCL有两个基本的抽象类:Log( 基本记录器 ) 和 LogFactory( 负责创建 Log 实例 )

public class SpringbootApplication {
    public static Log LOG= LogFactory.getLog(SpringbootApplication.class);
    public static void main(String[] args) {
        LOG.debug("debug()...");
        LOG.info("info()...");
        LOG.error("error()...");
    }
}

控制台显示

image.png

SpringBoot已经默认集成了log4j

关于commons-logging日志解耦可以参考文章:https://blog.csdn.net/sakurainluojia/article/details/53534949

总结:

commons-loggingslf4j都是日志的接口,供用户使用,而没有提供实现。log4jlogback等才是日志的真正实现,日志是接口+具体实现的方式来使用。
目前应用比较广泛的是Log4j2logback,而logback作为后起之秀,以替代log4j为目的,整体性能比log4j较佳,log4j的升级版log4j2也是有诸多亮点

选择使用

logbackSpring Boot默认的日志系统,假如对日志没有特殊要求,可以完全零配置(当然也可以自定义logback-spring.xml)使用 SLF4J(Simple Logging Facade For Java)logback来输出日志。

个人推荐使用log4j2Log4j2Log4j的升级版,与之前的版本Log4j 1.x相比、有重大的改进,在修正了Logback固有的架构问题的同时,改进了许多Logback所具有的功能。关于Log4j2的新特性可以在其官网首页查看

关于log4j2的性能使用可以参考

https://www.jianshu.com/p/570b406bddcd
https://blog.csdn.net/u011054333/article/details/54412360
https://blog.csdn.net/yjh1271845364/article/details/70888262

网友评论

登录后评论
0/500
评论
ben4
+ 关注