简介
说明
本文用示例介绍Java的slf4j这个日志框架的用法。同时也会介绍相关的知识。
官网
官网:SLF4J
官网文档:SLF4J Documentation
slf4j简介
概述
说明
项 | 说明 |
名词解释 | slf4j : simple log facade for java :简单日志门面。 |
作用 | 类似适配器,直接使用slf4j的方法打印即可,最终会调用到具体的日志,比如java.util.logging、logback和log4j。 |
优点 | 系统换了一个日志源后,不需要更改代码;省内存(可以使用占位符{})。 |
配置方法 | 日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现。 |
支持的日志系统 | NOP, Simple,log4j, log4j2, java.util.logging, JCL and logback。 |
绑定日志实现库的时机 | 编译时静态绑定真正的Log库 |
切换日志系统方法 | 切换jar包即可。例如:从java.util.logging切换到 log4j,仅把 slf4j-jdk14-xxx.jar替换成 slf4j-log4j12-xxx.jar即可。 |
slf4j与commons-logging区别
项 | slf4j | commons-logging |
绑定日志实现库的时机 | 编译时静态绑定真正的Log库。 可在OSGI中使用 | 动态查找的机制,在程序运行时自动找出真正使用的日志库。 它使用ClassLoader寻找和载入日志库, 导致了像OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader(OSGI的这种机制保证了插件互相独立)。 |
占位符 | 可以使用占位符“{}” | 不可使用占位符 |
slf4j的占位符
一般log用法(以log4j为例)
java文件
import org.apache.log4j.Logger; public class TestLog4j { private static final Logger LOGGER = Logger.getLogger(TestLog4j.class); public static void main(String[] args) { String message = "Hello World."; LOGGER.info("This is a test message: " + message); } }
此法缺点
1.字符串相加是一个比较消耗性能的操作
2.若log4j配置的输出级别是ERROR,则根本不会输出,占用内存。
第2个缺点的解决方法:加判断,但会使代码繁琐。例如:
if (LOGGER.isInfoEnabled()) { LOGGER.info("This is a test message: " + message); }
slf4j的{}占位符方法
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestLog4j { private static final Logger LOGGER = LoggerFactory.getLogger(TestLog4j.class); public static void main(String[] args) { String message = "Hello World."; LOGGER.info("This is a test message: {}", message); } }
使用了{}占位符,不会有字符串拼接操作。
在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。
相关源码:slf4j-log4j12-1.6.1.jar=> Log4jLoggerAdapter
public void debug(String format, Object arg1, Object arg2) { if (logger.isDebugEnabled()) { FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable()); } }
slf4j的包
下表中,只需指定加粗的依赖即可,它会自动拉取其他依赖。也可以强制其他依赖一个确定的版本,只需将其他依赖放到加粗依赖的后边即可。
包 | 含义 | 所需依赖 |
slf4j-api-xxx.jar | slf4j核心接口包 | |
slf4j-simple-xxx.jar | slf4jSimpleLogger日志实现包 | slf4j-api-xxx.jar=> slf4j-simple-xxx.jar |
slf4j-log4j12-xxx.jar | slf4j调用log4j的1.2版本的实现包 | slf4j-api-xxx.jar=> slf4j-log4j12-xxx.jar=> log4j-xxx.jar(1.2版本) |
slf4j-log4j13-xxx.jar | slf4j调用log4j的1.3版本的实现包 | |
slf4j-jdk14-xxx.jar | slf4j调用jdk的java.util.logging的实现包(jdk1.4及之后才有) | slf4j-api-xxx.jar=> slf4j-jdk14-xxx.jar => java.util.logging |
slf4j-nop-xxx.jar | slf4j调用nop(没有任何打印) | slf4j-api-xxx.jar=> slf4j-nop-xxx.jar => /dev/null |
slf4j-migrator-xxx.jar | 帮助迁移你的代码来使用SLF4J。 | |
slf4j-ext-xxx.jar | ||
jul-to-slf4j-xxx.jar | 原来jul的打印代码委托给slf4j | slf4j-api-xxx.jar=> jul-to-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar) |
log4j-over-slf4j-xxx.jar | 原来log4j的代码保持不变,使用slf4j接口输出 | slf4j-api-xxx.jar=> log4j-over-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar) |
jcl-over-slf4j-xxx.jar | 原来jcl的代码保持不变,使用slf4j接口输出 | slf4j-api-xxx.jar=> log4j-over-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar) |
slf4j-jcl-xxx.jar | 原来slf4j的代码保持不变,使用jcl输出 | |
logback-classic-xxx.jar | log4j的升级。不是由slf4j提供。 | slf4j-api-xxx.jar=> logback-classic-xxx.jar, logback-core-xxx.jar logback天生支持slf4j,所以不需要连接层 |
调用结构
slf4j-api(接口层)
|
各日志实现包的连接层( slf4j-jdk14, slf4j-log4j等)
|
各日志实现包(实现层)
兼容问题
1. 连接层绑定一个具体的实现层
比如: slf4j-jdk14.jar或 slf4j-log4j12.jar。
2. 接口层与连接层版本要相同
不同版本的slf4j-api(接口层)和连接层绑定混合在一起可能会引起问题。比如:slf4j-api-1.7.19.jar 对应 slf4j-simple-1.7.19.jar,使用 slf4j-simple-1.5.5.jar将不会正常工作。
3. 客户端与任意接口层兼容
从客户端的观点来看,所有版本的 slf4j-api都是兼容的。可以使用任何版本的 slf4j-api.jar,只要满足第二条就不会有问题。
桥接
其他网址
简介
假如你正在开发应用程序所调用的组件当中已经使用了 JCL 的,还有一些组建可能直接调用了 java.util.logging,这时你需要一个桥接器(名字为 xxx-over/to-slf4j.jar)把他们的日志输出重定向到 SLF4J。
桥接器就是一个假的日志实现工具。比如:把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到”SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。下边有相关例程:下边的示例2,示例3。
CLASS_PATH 里同时放置 log4j-over-slf4j.jar 和 slf4j-log4j12-version.jar 会发生的情况:日志会被踢来踢去,最终进入死循环,会造成内存溢出,其他包相互转发也是这种情况。(log4j-over-slf4j 与 slf4j-log4j12例外,log4j-over-slf4内部做了一个判断,可以防止造成内存溢出:判断slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,如果存在则表示冲突了,抛出异常提示用户要去掉对应的jar包,代码在slf4j-log4j12-xxx.jar=> org.apache.log4j.Log4jLoggerFactory=> static域)。
slf4j官方图
示例
@Slf4j
每次写新的类,就需要写:private static final Logger logger = = LoggerFactory.getLogger(Demo.class);简洁方法:使用lombok的@Slf4j。
步骤
1.引入lombok依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
2.类上写上@Slf4j
3.直接使用log变量
log.debug(“debug”)
slf4j-simple
java文件
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo { private static final Logger logger = = LoggerFactory.getLogger(Demo.class); public static void main(String[] args) { logger.info("Hello World"); } }
pom.xml
pom.xml(slf4j-simple)
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency>
测试
编译并运行HelloWorld程序,现在在控制台会有下列输出:
[main] INFO org.example.a.Demo - Hello World
注意:若没配置slf4j-xxx(比如没有加入依赖),则会有如下报错。
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
失败原因:class path下没有找到绑定的slf4j
slf4j-log4j12
要提供log4j.properties配置文件。
依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.30</version> <scope>test</scope> </dependency>
用法跟slf4j-simple相同
jul转为slf4j-simple
转换之前:jdk自带的java.util.logging(jul)
java文件
使用jdk自带的java.util.logging(jul)。
package com.example; import java.util.logging.Logger; public class HelloWorld { private static final Logger logger = Logger.getLogger(HelloWorld.class.getName()); public static void main(String[] args) { logger.severe("jul severe 测试"); logger.warning("jul warning 测试"); logger.info("jul info 测试"); } }
测试
三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
严重: jul severe 测试
三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
警告: jul warning 测试
三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
信息: jul info 测试
转换之后:jul转到slf4j-simple
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>mytest</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> </dependencies> </project>
java文件
main函数里面添加下列语句:
SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install();
添加之后的java文件
package com.example; import org.slf4j.bridge.SLF4JBridgeHandler; import java.util.logging.Logger; public class Demo { private static final Logger logger = Logger.getLogger(Demo.class.getName()); public static void main(String[] args) { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); logger.severe("jul severe 测试"); logger.warning("jul warning 测试"); logger.info("jul info 测试"); } }
测试
[main] ERROR com.example.Demo - jul severe 测试 [main] WARN com.example.Demo - jul warning 测试 [main] INFO com.example.Demo - jul info 测试
jcl转为slf4j
转换之前:jcl
java程序
package com.example; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Demo { private final static Log logger = LogFactory.getLog(Demo .class); public static void main(String[] args) { logger.debug("DEBUG ..."); logger.info("INFO ..."); logger.error("ERROR ..."); } }
maven依赖
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
配置文件
commons-logging.properties
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
测试
[INFO] Demo - INFO ... [ERROR] Demo - ERROR ...
转换之后:jcl转为slf4j
只修改maven依赖
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency>
请先
!