细说 Java 主流日志工具库

点关注,不迷路;持续更新Java相关技术及资讯!!!

公众号:Java耕耘者

在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息。在 Java 世界,有很多的日志工具库来实现日志功能,避免了我们重复造轮子。

我们先来逐一了解一下主流日志工具。

日志框架

java.util.logging (JUL)

JDK1.4 开始,通过 java.util.logging 提供日志功能。

它能满足基本的日志需要,但是功能没有 Log4j 强大,而且使用范围也没有 Log4j 广泛。

Log4j

Log4j 是 apache 的一个开源项目,创始人 Ceki Gulcu。

Log4j 应该说是 Java 领域资格最老,应用最广的日志工具。从诞生之日到现在一直广受业界欢迎。

Log4j 是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX 系统日志等。

Log4j 中有三个主要组成部分:

loggers - 负责捕获记录信息。

appenders - 负责发布日志信息,以不同的首选目的地。

layouts - 负责格式化不同风格的日志信息。

官网地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 创始人 Ceki Gulcu 设计的又一个开源日记组件,目标是替代 log4j。

logback 当前分成三个模块:logback-core、logback-classic 和 logback-access。

logback-core - 是其它两个模块的基础模块。

logback-classic - 是 log4j 的一个 改良版本。此外 logback-classic 完整实现 SLF4J API 使你可以很方便地更换成其它日记系统如 log4j 或 JDK14 Logging。

logback-access - 访问模块与 Servlet 容器集成提供通过 Http 来访问日记的功能。

官网地址:

http://logback.qos.ch/

Log4j2

官网地址:

http://logging.apache.org/log4j/2.x/

按照官方的说法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架构:

Log4j vs Logback vs Log4j2

按照官方的说法,Log4j2 大大优于 Log4j 和 Logback。

那么,Log4j2 相比于先问世的 Log4j 和 Logback,它具有哪些优势呢?

Log4j2 旨在用作审计日志记录框架。Log4j 1.x 和 Logback 都会在重新配置时丢失事件。Log4j 2 不会。在 Logback 中,Appender 中的异常永远不会对应用程序可见。在 Log4j 中,可以将 Appender 配置为允许异常渗透到应用程序。

Log4j2 在多线程场景中,异步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延迟低几个数量级。

Log4j2 对于独立应用程序是无垃圾的,对于稳定状态日志记录期间的 Web 应用程序来说是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应时间性能。

Log4j2 使用插件系统,通过添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常轻松地扩展框架,而无需对 Log4j 进行任何更改。

由于插件系统配置更简单。配置中的条目不需要指定类名。

支持自定义日志等级。

支持 lambda 表达式。

支持消息对象。

Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二进制数组,这使得它能被各种 Appender 使用。

Syslog Appender 支持 TCP 和 UDP 并且支持 BSD 系统日志。

Log4j2 利用 Java5 并发特性,尽量小粒度的使用锁,减少锁的开销。

日志门面

何谓日志门面?

日志门面是对不同日志框架提供的一个 API 封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。

common-logging

common-logging 是 apache 的一个开源项目。也称Jakarta Commons Logging,缩写 JCL。

common-logging 的功能是提供日志功能的 API 接口,本身并不提供日志的具体实现(当然,common-logging 内部有一个 Simple logger 的简单实现,但是功能很弱,直接忽略),而是在运行时动态的绑定日志实现组件来工作(如 log4j、java.util.loggin)。

官网地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全称为 Simple Logging Facade for Java,即 java 简单日志门面。

什么,作者又是 Ceki Gulcu!这位大神写了 Log4j、Logback 和 slf4j,专注日志组件开发五百年,一直只能超越自己。

类似于 Common-Logging,slf4j 是对不同日志框架提供的一个 API 封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,slf4j 在编译时静态绑定真正的 Log 库。使用 SLF4J 时,如果你需要使用某一种日志实现,那么你必须选择正确的 SLF4J 的 jar 包的集合(各种桥接包)。

官网地址:http://www.slf4j.org/

common-logging vs slf4j

slf4j 库类似于 Apache Common-Logging。但是,他在编译时静态绑定真正的日志库。这点似乎很麻烦,其实也不过是导入桥接 jar 包而已。

slf4j 一大亮点是提供了更方便的日志记录方式:

不需要使用logger.isDebugEnabled()来解决日志因为字符拼接产生的性能问题。slf4j 的方式是使用{}作为字符串替换符,形式如下:

logger.debug("id: {},name: {} ",id,name);

总结

综上所述,使用 slf4j + Logback 可谓是目前最理想的日志解决方案了。

接下来,就是如何在项目中实施了。

实施日志解决方案

使用日志解决方案基本可分为三步:

引入 jar 包

配置

使用 API

常见的各种日志解决方案的第 2 步和第 3 步基本一样,实施上的差别主要在第 1 步,也就是使用不同的库。

引入 jar 包

这里首选推荐使用 slf4j + logback 的组合。

如果你习惯了 common-logging,可以选择 common-logging+log4j。

强烈建议不要直接使用日志实现组件(logback、log4j、java.util.logging),理由前面也说过,就是无法灵活替换日志库。

还有一种情况:你的老项目使用了 common-logging,或是直接使用日志实现组件。如果修改老的代码,工作量太大,需要兼容处理。在下文,都将看到各种应对方法。

注:据我所知,当前仍没有方法可以将 slf4j 桥接到 common-logging。如果我孤陋寡闻了,请不吝赐教。

slf4j 直接绑定日志组件

slf4j + logback

添加依赖到 pom.xml 中即可。

logback-classic-1.0.13.jar 会自动将 slf4j-api-1.7.21.jar 和 logback-core-1.0.13.jar 也添加到你的项目中。

ch.qos.logback

logback-classic

1.0.13

slf4j + log4j

添加依赖到 pom.xml 中即可。

slf4j-log4j12-1.7.21.jar 会自动将 slf4j-api-1.7.21.jar 和 log4j-1.2.17.jar 也添加到你的项目中。

org.slf4j

slf4j-log4j12

1.7.21

slf4j + java.util.logging

添加依赖到 pom.xml 中即可。

slf4j-jdk14-1.7.21.jar 会自动将 slf4j-api-1.7.21.jar 也添加到你的项目中。

org.slf4j

slf4j-jdk14

1.7.21

slf4j 兼容非 slf4j 日志组件

在介绍解决方案前,先提一个概念——桥接

什么是桥接呢

假如你正在开发应用程序所调用的组件当中已经使用了 common-logging,这时你需要 jcl-over-slf4j.jar 把日志信息输出重定向到 slf4j-api,slf4j-api 再去调用 slf4j 实际依赖的日志组件。这个过程称为桥接。下图是官方的 slf4j 桥接策略图:

从图中应该可以看出,无论你的老项目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用对应的桥接 jar 包来解决兼容问题。

slf4j 兼容 common-logging

org.slf4j

jcl-over-slf4j

1.7.12

slf4j 兼容 log4j

org.slf4j

log4j-over-slf4j

1.7.12

slf4j 兼容 java.util.logging

org.slf4j

jul-to-slf4j

1.7.12

spring 集成 slf4j

做 java web 开发,基本离不开 spring 框架。很遗憾,spring 使用的日志解决方案是 common-logging + log4j。(系统的学习spring全家桶,可以在Java知音公众号内回复“Springboot聚合”)

所以,你需要一个桥接 jar 包:logback-ext-spring。

ch.qos.logback

logback-classic

1.1.3

org.logback-extensions

logback-ext-spring

0.1.2

org.slf4j

jcl-over-slf4j

1.7.12

common-logging 绑定日志组件

common-logging + log4j

添加依赖到 pom.xml 中即可。

commons-logging

commons-logging

1.2

log4j

log4j

1.2.17

使用 API

slf4j 用法

使用 slf4j 的 API 很简单。使用LoggerFactory初始化一个Logger实例,然后调用 Logger 对应的打印等级函数就行了。

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

publicclassApp{

privatestaticfinal Loggerlog= LoggerFactory.getLogger(App.class);

publicstaticvoidmain(String[] args){

String msg ="print log, current level: {}";

log.trace(msg,"trace");

log.debug(msg,"debug");

log.info(msg,"info");

log.warn(msg,"warn");

log.error(msg,"error");

}

}

common-logging 用法

common-logging 用法和 slf4j 几乎一样,但是支持的打印等级多了一个更高级别的:fatal。

此外,common-logging 不支持{}替换参数,你只能选择拼接字符串这种方式了。

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

publicclassJclTest{

privatestaticfinal Loglog= LogFactory.getLog(JclTest.class);

publicstaticvoidmain(String[] args){

String msg ="print log, current level: ";

log.trace(msg +"trace");

log.debug(msg +"debug");

log.info(msg +"info");

log.warn(msg +"warn");

log.error(msg +"error");

log.fatal(msg +"fatal");

}

}

log4j2 配置

log4j2 基本配置形式如下:

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

value

...

...

配置示例:

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

packages="org.apache.logging.log4j.test">

target/test.log

<!-- class and line number -->

%d %p %C{1.} [%t] %m%n

logback 配置

<configuration>

作用:<configuration>是 logback 配置文件的根元素。

要点:它有<appender>、<logger>、<root>三个子元素。


<appender>

作用:将记录日志的任务委托给名为 appender 的组件。

要点:可以配置零个或多个;它有<file>、<filter>、<layout>、<encoder>四个子元素。

属性:

name:设置 appender 名称。

class:设置具体的实例化类。

<file>

作用:设置日志文件路径。

<filter>

作用:设置过滤器。

要点:可以配置零个或多个。

<layout>

作用:设置 appender。

要点:可以配置零个或一个。

属性:

class:设置具体的实例化类。

<encoder>

作用:设置编码。

要点:可以配置零个或多个。

属性:

class:设置具体的实例化类。

<logger>

作用:设置 logger。

要点:可以配置零个或多个。

属性:

name

level:设置日志级别。不区分大小写。可选值:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。

additivity:可选值:true 或 false。

<appender-ref>

作用:appender 引用。

要点:可以配置零个或多个。

<root>

作用:设置根 logger。

要点:只能配置一个;除了 level,不支持任何属性。level 属性和<logger>中的相同;有一个子元素<appender-ref>,与<logger>中的相同。

完整的 logback.xml 参考示例

在下面的配置文件中,我为自己的项目代码(根目录:org.zp.notes.spring)设置了五种等级:

TRACE、DEBUG、INFO、WARN、ERROR,优先级依次从低到高。

因为关注 spring 框架本身的一些信息,我增加了专门打印 spring WARN 及以上等级的日志。

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

<!-- logback中一共有5种有效级别,分别是TRACE、DEBUG、INFO、WARN、ERROR,优先级依次从低到高 -->

<!-- 将记录日志打印到控制台 -->

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- RollingFileAppender begin -->

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/all.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

30MB

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/error.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

ERROR

ACCEPT

DENY

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/warn.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

WARN

ACCEPT

DENY

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/info.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

INFO

ACCEPT

DENY

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/debug.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

DEBUG

ACCEPT

DENY

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/trace.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

TRACE

ACCEPT

DENY

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- 根据时间来制定滚动策略 -->

${user.dir}/logs/${DIR_NAME}/springframework.%d{yyyy-MM-dd}.log

30

<!-- 根据文件大小来制定滚动策略 -->

10MB

%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n

<!-- RollingFileAppender end -->

<!-- logger begin -->

<!-- 本项目的日志记录,分级打印 -->

<!-- SPRING框架日志 -->

<!-- logger end -->

log4j 配置

完整的 log4j.xml 参考示例

log4j 的配置文件一般有 xml 格式或 properties 格式。这里为了和 logback.xml 做个对比,就不介绍 properties 了,其实也没太大差别。

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

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

value="%d{yyyy-MM-dd HH:mm:ss,SSS\} [%-5p] [%t] %c{36\}.%M - %m%n"/>

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

<!-- 每天重新生成日志文件 -->

<!-- 每小时重新生成日志文件 -->

<!--<param name="DatePattern" value="'-'yyyy-MM-dd-HH'.log'"/>-->

value="%d{yyyy-MM-dd HH:mm:ss,SSS\} [%-5p] [%t] %c{36\}.%M - %m%n"/>

<!-- 指定logger的设置,additivity指示是否遵循缺省的继承机制-->

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

参考

http://www.slf4j.org/manual.html

http://logback.qos.ch/

http://logging.apache.org/log4j/1.2/

http://commons.apache.org/proper/commons-logging/

http://blog.csdn.net/yycdaizi/article/details/8276265

为了感谢支持我的朋友!整理了一份Java高级架构资料、Spring源码分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式等资料可以进群领取。

本号专注Java源码分析。喜欢底层源码的朋友可以来交流探讨。交流群:818491202 验证:33

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 概述 在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息。在Java世界,有很多的日志工具库来实现日志...
    静默虚空阅读 1,839评论 1 9
  • 前言 最近学习开java web服务器开发,开始学习java,处理业务逻辑,但对其中的日志比较好奇,之前没怎么接触...
    九风萍舟阅读 3,283评论 1 6
  • log4j, log4j2, slf4j, logback关系 log4j是由Apache开发的一套元老级日志框架...
    rainybowe阅读 1,646评论 0 4
  • 作为Java开发人员,对于日志记录框架一定非常熟悉。而且几乎在所有应用里面,一定会用到各种各样的日志框架用来记录程...
    意识流丶阅读 13,904评论 0 13
  • 在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此...
    Java架构阅读 3,458评论 2 41