前言

最近在看代码的过程中,发现身边的许多人在使用Java日志框架时,对于应该引入何种依赖不甚了解,搜索网络上的文章,常常也是互不一致。这篇文章可以看着是Java日志框架的入门使用和实践建议,重点介绍不同组合方式下的依赖设置及其背后的逻辑,一方面给自己备查,另外也希望对小伙伴们有所帮助。

Java日志框架家族繁杂,出于实用的原则,这里主要介绍主流的几个项目:SLF4J、Logback、Log4j 2,以及它们之间各种搭配用法和使用建议。

另外,由于Log4j 1项目已经在2015-08-05正式宣布死亡(最终版本停留在2012-05-13发布的1.2.17),因此这里也不再讨论Log4j 1,下文所有提到Log4j的地方,都是指Log4j 2。

简述

对于日志框架,可以按照「分层」的概念来理解:接口层、实现层。开发者在使用日志框架时,建议基于接口层而非实现层进行开发,这样的好处是,避免项目与某一个具体日志框架耦合,这是一个常见的编程理念,应该比较容易理解。

例如,项目最初使用SLF4J作为接口层,使用Logback作为实现层,而你的项目代码中使用的也是接口层的类org.slf4j.Logger,这种情况下,当将来你想将实现层切换为Log4j时,你最需要改动依赖项,而不需要改动代码。

但是,如果你最初的项目代码中使用的并非是接口层的类,而是实现层(即Logback)的类ch.qos.logback.classic.Logger(这可能是因为手滑,毕竟类名都是一样的)。这种情况下,想要切换实现层,就需要改动所有涉及使用到这个类的代码。

SLF4J + Logback

依赖设置

由于logback-classic中既有实现层,也包含了对接口层SLF4J的依赖,因此,最简单的设置可以是这样的:

  1. <dependency>
  2. <groupId>ch.qos.logback</groupId>
  3. <artifactId>logback-classic</artifactId>
  4. <version>1.2.12</version>
  5. <scope>runtime</scope>
  6. </dependency>

不过,就像简述里说的,为了避免开发者不小心误用实现类ch.qos.logback.classic.Logger,推荐使用如下的依赖设置,注意其中的scope设置:

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-api</artifactId>
  4. <version>1.7.32</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>ch.qos.logback</groupId>
  8. <artifactId>logback-classic</artifactId>
  9. <version>1.2.12</version>
  10. <scope>runtime</scope>
  11. <exclusions>
  12. <exclusion>
  13. <groupId>org.slf4j</groupId>
  14. <artifactId>slf4j-api</artifactId>
  15. </exclusion>
  16. </exclusions>
  17. </dependency>

配置文件(logback.xml)

这里给出一个最常见的配置文件,包含控制台输出、滚动文件输出:

  1. <configuration>
  2. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  3. <encoder>
  4. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</pattern>
  5. </encoder>
  6. </appender>
  7. <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  8. <file>logs/app.log</file>
  9. <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  10. <fileNamePattern>logs/app-%d{yyyy-MM-dd-HH}-%i.log</fileNamePattern>
  11. <!-- 单个日志文件超过10M,则进行滚动,对文件进行递增编号(即%i) -->
  12. <maxFileSize>10MB</maxFileSize>
  13. <!-- 所有日志文件的大小限制,超出则删除旧文件 -->
  14. <totalSizeCap>5GB</totalSizeCap>
  15. <!-- 与fileNamePattern相结合,本例中由于时间粒度是小时,因此这里表示保存48个小时 -->
  16. <maxHistory>48</maxHistory>
  17. </rollingPolicy>
  18. <encoder>
  19. <pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</pattern>
  20. </encoder>
  21. </appender>
  22. <root level="INFO">
  23. <appender-ref ref="CONSOLE" />
  24. <appender-ref ref="ROLLING_FILE" />
  25. </root>
  26. </configuration>

代码示例

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. ...
  4. private static final Logger logger = LoggerFactory.getLogger(App.class);
  5. ...
  6. logger.info("First name: {}, last name: {}", firstName, lastName);

SLF4J + Log4j

依赖设置

由于log4j-slf4j-impl中既有实现层,也包含了对接口层SLF4J的依赖,因此,最简单的设置可以是这样的:

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-slf4j-impl</artifactId>
  4. <version>2.20.0</version>
  5. </dependency>

不过,基于与上一节同样的逻辑,推荐使用下面的设置:

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-api</artifactId>
  4. <version>1.7.25</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.logging.log4j</groupId>
  8. <artifactId>log4j-slf4j-impl</artifactId>
  9. <version>2.20.0</version>
  10. <scope>runtime</scope>
  11. <exclusions>
  12. <exclusion>
  13. <groupId>org.slf4j</groupId>
  14. <artifactId>slf4j-api</artifactId>
  15. </exclusion>
  16. </exclusions>
  17. </dependency>

配置文件(log4j2.xml)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Configuration status="INFO"> <!-- log4j internal log level -->
  3. <Appenders>
  4. <Console name="CONSOLE" target="SYSTEM_OUT">
  5. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
  6. </Console>
  7. <RollingFile name="ROLLING_FILE"
  8. fileName="logs/log4j2/roll-by-time-and-size/app.log"
  9. filePattern="logs/log4j2/roll-by-time-and-size/app-%d{yyyy-MM-dd-HH}-%i.log"
  10. ignoreExceptions="false">
  11. <PatternLayout>
  12. <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Pattern>
  13. </PatternLayout>
  14. <Policies>
  15. <!-- 启动时,会删除多余的日志文件 -->
  16. <OnStartupTriggeringPolicy/>
  17. <!-- 自动感知filePattern中的时间设置,本例中是按小时粒度进行滚动 -->
  18. <TimeBasedTriggeringPolicy/>
  19. <!-- 单个日志文件超过10M,则进行滚动,递增编号(即filePattern中的%i) -->
  20. <SizeBasedTriggeringPolicy size="10M"/>
  21. </Policies>
  22. <!-- max配置与上面的filePattern结合,由于本例中是按小时粒度进行滚动,因此这里表示每小时内最多产生五个编号文件,超出这循环覆盖,如不设置max,则默认为7 -->
  23. <DefaultRolloverStrategy max="5">
  24. <Delete basePath="logs" maxDepth="1">
  25. <!-- 最近30天,最多5GB的日志 -->
  26. <IfFileName glob="app-*.log">
  27. <IfAny>
  28. <IfLastModified age="30d"/>
  29. <IfAccumulatedFileSize exceeds="5GB"/>
  30. </IfAny>
  31. </IfFileName>
  32. </Delete>
  33. </DefaultRolloverStrategy>
  34. </RollingFile>
  35. </Appenders>
  36. <Loggers>
  37. <Root level="warn">
  38. <AppenderRef ref="CONSOLE"/>
  39. <AppenderRef ref="ROLLING_FILE"/>
  40. </Root>
  41. </Loggers>
  42. </Configuration>

代码示例

由于与上例相同,都是基于SLF4J接口层,因此使用方式相同:

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. ...
  4. private static final Logger logger = LoggerFactory.getLogger(App.class);
  5. ...
  6. logger.info("First name: {}, last name: {}", firstName, lastName);

单独使用Log4j

一般我们会基于SLF4J接口层进行开发,但是如果你硬要单独使用Log4j,也不是不可以。

依赖配置

最简单的,我们可以使用以下配置:

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-core</artifactId>
  4. <version>2.20.0</version>
  5. </dependency>

不过,由于Log4j自身也分了接口层和实现层,推荐使用如下配置:

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-api</artifactId>
  4. <version>2.20.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.logging.log4j</groupId>
  8. <artifactId>log4j-core</artifactId>
  9. <version>2.20.0</version>
  10. <scope>runtime</scope>
  11. <exclusions>
  12. <exclusion>
  13. <groupId>org.apache.logging.log4j</groupId>
  14. <artifactId>log4j-api</artifactId>
  15. </exclusion>
  16. </exclusions>
  17. </dependency>

配置文件(log4j2.xml)

(同上)

代码示例

  1. import org.apache.logging.log4j.LogManager;
  2. import org.apache.logging.log4j.Logger;
  3. ...
  4. private static final Logger logger = LogManager.getLogger(App.class);
  5. ...
  6. logger.info("First name: {}, last name: {}", firstName, lastName);

有人可能会说,Log4j自身拆成了接口层和实现层,是不是意味着,使用Log4j接口层的情况下,实现层还能使用别的日志系统?是的,例如可以使用「Log4j接口层 + Logback」的搭配:

  1. <!-- 1) Log4j接口层 -->
  2. <dependency>
  3. <groupId>org.apache.logging.log4j</groupId>
  4. <artifactId>log4j-api</artifactId>
  5. <version>2.20.0</version>
  6. </dependency>
  7. <!-- 2) Log4j项目提供的「桥接层」,将Log4j接口层桥接到SLF4J接口层,由于Logback是基于SLF4J,因此经过桥接之后,就可以使用Logback作为实现层 -->
  8. <!-- 注:log4j-to-slf4j含有对log4j-api的依赖,因此上面可以不用单独列出log4j-api依赖,不过,为了逻辑清晰,还是保留 -->
  9. <dependency>
  10. <groupId>org.apache.logging.log4j</groupId>
  11. <artifactId>log4j-to-slf4j</artifactId>
  12. <version>2.20.0</version>
  13. </dependency>
  14. <!-- 3) Logback实现层 -->
  15. <dependency>
  16. <groupId>ch.qos.logback</groupId>
  17. <artifactId>logback-classic</artifactId>
  18. <version>1.2.12</version>
  19. <scope>runtime</scope>
  20. </dependency>

总结

如果你在开发一个玩具项目,对于日志框架的选择和使用当然可以比较随意,但是,如果是开发一个正经的项目,尤其是你的项目将作为公众可用的第三方库时,遵循最佳实践、保持灵活性则是非常必要的,因为你不知道使用方希望在他的项目中使用什么日志框架。

Java日志框架的依赖设置备查(SLF4J, Log4j, Logback)的更多相关文章

  1. Java日志框架使用技巧收集(slf4j、jcl、jul、log4j1、log4j2、logback)

    乒乓狂魔-教程: jdk-logging.log4j.logback日志介绍及原理 commons-logging与jdk-logging.log4j1.log4j2.logback的集成原理 slf ...

  2. Java日志框架SLF4J和log4j以及logback的联系和区别

    1.SLF4J(Simple logging Facade for Java) 意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接 ...

  3. Java 日志框架概述(slf4j / log4j / JUL / Common-logging(JCL) / logback)

    一.简介 JAVA日志在初期可能官方并没有提供很好且实用的规范,导致各公司或OSS作者选择自行造轮子,这也导致了目前初学者觉得市面上 Java 日志库繁杂的局面. 现在市面流行以 slf4j(Simp ...

  4. Java日志框架那些事儿

    文章首发于[博客园-陈树义],点击跳转到原文Java日志框架那些事儿. 在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而 ...

  5. java 日志框架总结

    在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而言,日志记录是一个非常重要的问题.因此,如何选择一个合适的日志记录框架也 ...

  6. Java日志框架(一)

    在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而言,日志记录是一个非常重要的问题.因此,如何选择一个合适的日志记录框架也 ...

  7. JAVA日志框架概述

            日志用来记录应用的运行状态以及一些关键业务信息,其重要性不言而喻,通常我们借助于现有的日志框架完成日志输出.目前开源的日志框架很多,常见的有log4j.logback等,有时候我们还会 ...

  8. java日志框架笔记-log4j-springboot整合

    # 日志框架slf4j log4j logback之间的关系 简答的讲就是slf4j是一系列的日志接口,而log4j logback是具体实现了的日志框架. ```java SLF4J获得logger ...

  9. Java程序员最常用的8个Java日志框架

    转自:http://www.codeceo.com/article/8-java-log-framework.html 作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用 ...

  10. 转:Java程序员最常用的8个Java日志框架

    作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用以及移动应用.然而日志系统是一个成熟Java应用所必不可少的,在开发和调试阶段,日志可以帮助我们更好更快地定位bug:在 ...

随机推荐

  1. linux DNS域名解析

    目录 一.DNS概念 二.域名格式类型 三.查询类型 四.解析类型 五.配置DNS 六.dns解析实验 1.配置正向解析 2.反向解析 3.主从解析 一.DNS概念 概念:域名和IP地址的相互映射的分 ...

  2. Python潮流周刊#3:PyPI 的安全问题

    你好,我是豌豆花下猫.这里记录每周值得分享的 Python 及通用技术内容,部分为英文,已在小标题注明.(标题取自其中一则分享,不代表全部内容都是该主题,特此声明.) 文章&教程 1.掌握Py ...

  3. SQL基础知识扫盲

    @ 目录 SQL & 数据库基础知识扫盲 SQL是什么? 数据库是什么? 挺身入局,实践出真知 DBMS初体验 MySQL:初体验 Oracle:初体验 PostgreSQL:初体验 Demo ...

  4. ping不通能curl通

    今天发现一个域名或ip居然在ping不通的情况下能curl通,以前的思维定式直接给整破防了啊!!! 涨见识了,具体原因和原理后续补充~

  5. 自然语言处理 Paddle NLP - 快递单信息抽取 (ERNIE 1.0)

    文档检索:需要把业务问题拆解成子任务.文本分类 -> 文本匹配 -> 等任务 -> Panddle API 完成子任务 -> 子任务再拼起来 介绍 在2017年之前,工业界和学 ...

  6. 用AI技术实现自动化的社交媒体广告投放,提高广告效果和收益

    目录 1. 引言 2. 技术原理及概念 2.1 基本概念解释 随着社交媒体的普及,广告投放已经成为了广告行业的重要一环.在过去的几年中,社交媒体广告投放的效果和收益都得到了显著提高,但同时也存在着一些 ...

  7. 让AI支持游戏AI模型:从经典AI算法到最新技术的应用

    目录 20. 让 AI 支持游戏AI模型:从经典 AI 算法到最新技术的应用 1. 引言 2. 技术原理及概念 2.1 基本概念解释 2.2 技术原理介绍 2.2.2 最新技术介绍 3. 实现步骤与流 ...

  8. 大数据实战手册-开发篇之spark实战案例:实时日志分析

    2.6 spark实战案例:实时日志分析 2.6.1 交互流程图 2.6.2 客户端监听器(java) @SuppressWarnings("static-access") pri ...

  9. 精选8道ES高频面试题和答案,后悔没早点看。

    不要再干巴巴的背诵八股文了,一定要结合具体场景回答面试问题! 前言 我们在回答面试题的时候,不能干巴巴的去背八股文,一定要结合应用场景,最好能结合过去做过的项目,去和面试官沟通. 这些场景题虽然不要求 ...

  10. easyexce报错BeanMap$Generator

    class net.sf.cglib.core.DebuggingClassWriter overrides final method visit 这两个报错都可以在一起解决,因为这是由于Jar包冲突 ...