Java日志框架的依赖设置备查(SLF4J, Log4j, Logback)
前言
最近在看代码的过程中,发现身边的许多人在使用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的依赖,因此,最简单的设置可以是这样的:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
<scope>runtime</scope>
</dependency>
不过,就像简述里说的,为了避免开发者不小心误用实现类ch.qos.logback.classic.Logger,推荐使用如下的依赖设置,注意其中的scope设置:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
配置文件(logback.xml)
这里给出一个最常见的配置文件,包含控制台输出、滚动文件输出:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app-%d{yyyy-MM-dd-HH}-%i.log</fileNamePattern>
<!-- 单个日志文件超过10M,则进行滚动,对文件进行递增编号(即%i) -->
<maxFileSize>10MB</maxFileSize>
<!-- 所有日志文件的大小限制,超出则删除旧文件 -->
<totalSizeCap>5GB</totalSizeCap>
<!-- 与fileNamePattern相结合,本例中由于时间粒度是小时,因此这里表示保存48个小时 -->
<maxHistory>48</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ROLLING_FILE" />
</root>
</configuration>
代码示例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger logger = LoggerFactory.getLogger(App.class);
...
logger.info("First name: {}, last name: {}", firstName, lastName);
SLF4J + Log4j
依赖设置
由于log4j-slf4j-impl中既有实现层,也包含了对接口层SLF4J的依赖,因此,最简单的设置可以是这样的:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.20.0</version>
</dependency>
不过,基于与上一节同样的逻辑,推荐使用下面的设置:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.20.0</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
配置文件(log4j2.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO"> <!-- log4j internal log level -->
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="ROLLING_FILE"
fileName="logs/log4j2/roll-by-time-and-size/app.log"
filePattern="logs/log4j2/roll-by-time-and-size/app-%d{yyyy-MM-dd-HH}-%i.log"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Pattern>
</PatternLayout>
<Policies>
<!-- 启动时,会删除多余的日志文件 -->
<OnStartupTriggeringPolicy/>
<!-- 自动感知filePattern中的时间设置,本例中是按小时粒度进行滚动 -->
<TimeBasedTriggeringPolicy/>
<!-- 单个日志文件超过10M,则进行滚动,递增编号(即filePattern中的%i) -->
<SizeBasedTriggeringPolicy size="10M"/>
</Policies>
<!-- max配置与上面的filePattern结合,由于本例中是按小时粒度进行滚动,因此这里表示每小时内最多产生五个编号文件,超出这循环覆盖,如不设置max,则默认为7 -->
<DefaultRolloverStrategy max="5">
<Delete basePath="logs" maxDepth="1">
<!-- 最近30天,最多5GB的日志 -->
<IfFileName glob="app-*.log">
<IfAny>
<IfLastModified age="30d"/>
<IfAccumulatedFileSize exceeds="5GB"/>
</IfAny>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="ROLLING_FILE"/>
</Root>
</Loggers>
</Configuration>
代码示例
由于与上例相同,都是基于SLF4J接口层,因此使用方式相同:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger logger = LoggerFactory.getLogger(App.class);
...
logger.info("First name: {}, last name: {}", firstName, lastName);
单独使用Log4j
一般我们会基于SLF4J接口层进行开发,但是如果你硬要单独使用Log4j,也不是不可以。
依赖配置
最简单的,我们可以使用以下配置:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
不过,由于Log4j自身也分了接口层和实现层,推荐使用如下配置:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
配置文件(log4j2.xml)
(同上)
代码示例
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
...
private static final Logger logger = LogManager.getLogger(App.class);
...
logger.info("First name: {}, last name: {}", firstName, lastName);
有人可能会说,Log4j自身拆成了接口层和实现层,是不是意味着,使用Log4j接口层的情况下,实现层还能使用别的日志系统?是的,例如可以使用「Log4j接口层 + Logback」的搭配:
<!-- 1) Log4j接口层 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 2) Log4j项目提供的「桥接层」,将Log4j接口层桥接到SLF4J接口层,由于Logback是基于SLF4J,因此经过桥接之后,就可以使用Logback作为实现层 -->
<!-- 注:log4j-to-slf4j含有对log4j-api的依赖,因此上面可以不用单独列出log4j-api依赖,不过,为了逻辑清晰,还是保留 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 3) Logback实现层 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
<scope>runtime</scope>
</dependency>
总结
如果你在开发一个玩具项目,对于日志框架的选择和使用当然可以比较随意,但是,如果是开发一个正经的项目,尤其是你的项目将作为公众可用的第三方库时,遵循最佳实践、保持灵活性则是非常必要的,因为你不知道使用方希望在他的项目中使用什么日志框架。
Java日志框架的依赖设置备查(SLF4J, Log4j, Logback)的更多相关文章
- Java日志框架使用技巧收集(slf4j、jcl、jul、log4j1、log4j2、logback)
乒乓狂魔-教程: jdk-logging.log4j.logback日志介绍及原理 commons-logging与jdk-logging.log4j1.log4j2.logback的集成原理 slf ...
- Java日志框架SLF4J和log4j以及logback的联系和区别
1.SLF4J(Simple logging Facade for Java) 意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接 ...
- Java 日志框架概述(slf4j / log4j / JUL / Common-logging(JCL) / logback)
一.简介 JAVA日志在初期可能官方并没有提供很好且实用的规范,导致各公司或OSS作者选择自行造轮子,这也导致了目前初学者觉得市面上 Java 日志库繁杂的局面. 现在市面流行以 slf4j(Simp ...
- Java日志框架那些事儿
文章首发于[博客园-陈树义],点击跳转到原文Java日志框架那些事儿. 在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而 ...
- java 日志框架总结
在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而言,日志记录是一个非常重要的问题.因此,如何选择一个合适的日志记录框架也 ...
- Java日志框架(一)
在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而言,日志记录是一个非常重要的问题.因此,如何选择一个合适的日志记录框架也 ...
- JAVA日志框架概述
日志用来记录应用的运行状态以及一些关键业务信息,其重要性不言而喻,通常我们借助于现有的日志框架完成日志输出.目前开源的日志框架很多,常见的有log4j.logback等,有时候我们还会 ...
- java日志框架笔记-log4j-springboot整合
# 日志框架slf4j log4j logback之间的关系 简答的讲就是slf4j是一系列的日志接口,而log4j logback是具体实现了的日志框架. ```java SLF4J获得logger ...
- Java程序员最常用的8个Java日志框架
转自:http://www.codeceo.com/article/8-java-log-framework.html 作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用 ...
- 转:Java程序员最常用的8个Java日志框架
作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用以及移动应用.然而日志系统是一个成熟Java应用所必不可少的,在开发和调试阶段,日志可以帮助我们更好更快地定位bug:在 ...
随机推荐
- Netty自定义协议要素
魔数:用来判断是否是无效数据包 协议版本号:可以支持协议的升级 序列化算法:消息正文使用哪种序列化方式,可以扩展.例如:protobuf,json,hessian等 指令类型:跟业务相关,例如:登录, ...
- vue自定义组件——search-box
pre { overflow-y: auto; max-height: 300px } github地址: https://github.com/lxmghct/my-vue-components 组 ...
- 2013年蓝桥杯C/C++大学A组省赛真题(高斯的日记)
题目描述: 大数学家高斯有个好习惯:无论如何都要记日记. 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第 ...
- adb知识
一.原理: adb客户端:能够对adb服务下发指令或命令的工具 server服务端:adb.exe执行之后的进程,表示adb的服务端,通过服务端与手机上的adb守护进程进行通信 adb守护进程 : 随 ...
- ArcMap手动新建矢量要素的方式
本文介绍在ArcGIS下属ArcMap软件中,新建点.线.面等矢量要素图层,并对新建图层的空间范围加以划定的方法. 首先,在右侧"Catalog"栏中选择需要存放新建立矢量 ...
- 使用Git进行版本控制和协作:代码共享、协作和版本管理
目录 引言 Git 是一款开源的分布式版本控制系统,它已经成为了现代软件开发中必不可少的工具之一.在这篇文章中,我们将介绍如何使用 Git 进行版本控制和协作,以实现代码共享.协作和版本管理.Git ...
- 行行AI人才直播第3期:浙江大学周鑫博士《空中机器人复杂环境高效自主导航—从单机到集群》
行行AI人才是博客园和顺顺智慧共同运营的AI行业人才全生命周期服务平台. 空中飞行的无人车.无人机集群是科幻电影的常客,也往往是未来科技的一大代表.在电影<星球大战:西斯的复仇>(2005 ...
- 如何让一句话木马绕过waf ?
一.什么是一句话木马? 一句话木马就是只需要一行代码的木马,短短一行代码,就能做到和大马相当的功能.为了绕过waf的检测,一句话木马出现了无数中变形,但本质是不变的:木马的函数执行了我们发送的命令. ...
- 统信UOS国产服务器操作系统(UOS Server 20-1060e)安装使用体验
总体来说,UOS系统的安装还是很简明的.需要注意的是后期的驱动安装和其他各方面的使用细节. 以下是具体安装过程:(感谢统信软件河北团队的大力支持.) 特别感谢统信的郭赞.喵喵喵.Zero等各位大神的帮 ...
- 详解共识算法的Raft算法模拟数
摘要:Raft算法是一种分布式共识算法,用于解决分布式系统中的一致性问题. 本文分享自华为云社区<共识算法之Raft算法模拟数>,作者: TiAmoZhang . 01.Leader选举 ...