简介
说明
本文用示例介绍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,只要满足第二条就不会有问题。
桥接
其他网址
官网:Bridging legacy APIs
简介
假如你正在开发应用程序所调用的组件当中已经使用了 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变量
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>
测试
[main] INFO com.example.Demo - INFO ...
[main] ERROR com.example.Demo - ERROR ...
其他网址
slf4j的包使用说明 - - ITeye博客
log4j与commons-logging,slf4j的关系 - sxfans - ITeye博客