slf4j

时间:2024-07-14 19:36:27编辑:奇闻君

为什么要使用SLF4J而不是Log4J

每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库如java.util.logging、Apache log4j、logback。但如果你还不知道SLF4J(Simple logging facade for Java)的话,那么是时候去在你项目中学习使用SLF4J了。
在这篇文章中,我们将学习为什么使用SLF4J比log4j或者java.util.logging要优秀。自从上次我写Java程序员的10个日志技巧已经有一段时间了,我已经不记得我写的关于日志的一切了。
不管怎样,让我们回到这个话题,SLF4J不同于其他日志类库,与其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层(abstraction layer),它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。
如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。
总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物而且Apache commons logging也已经在使用这种思想了,但现在SLF4J正迅速成为Java世界的日志标准。让我们再看看几个使用SLF4J而不是log4j、logback或者java.util.logging的理由。
SLF4J对比Log4J,logback和java.util.Logging的优势
正如我之前说的,在你的代码中使用SLF4J写日志语句的主要出发点是使得你的程序独立于任意特定的日志类库,依赖于特定类可能需要不同与你已有的配置,并且导致更多维护的麻烦。但除此之外,还要一个SLF4J API的特性使得我坚持使用SLF4J而抛弃我长期间钟爱的Lof4j的理由,是被称为占位符(place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在String的format()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。即使你可能没需要那些对象,但这个依旧成立,取决于你的生产环境的日志级别,例如在DEBUG或者INFO级别的字符串连接。因为String对象是不可修改的并且它们建立在一个String池中,它们消耗堆内存( heap memory)而且大多数时间他们是不被需要的,例如当你的应用程序在生产环境以ERROR级别运行时候,一个String使用在DEBUG语句就是不被需要的。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。
这是你在Log4j中使用的方案,但肯定这一点都不有趣并且降低了代码可读性因为增加了不必要的繁琐重复代码(boiler-plate code):
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

另一方面,如果你使用SLF4J的话,你可以得到在极简洁的格式的结果,就像以下展示的一样:
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);


在SLF4J,我们不需要字符串连接而且不会导致暂时不需要的字符串消耗。取而代之的,我们在一个以占位符和以参数传递实际值的模板格式下写日志信息。你可能会在想万一我有很个参数怎么办?嗯,那么你可以选择使用变量参数版本的日志方法或者用以Object数组传递。这是一个相当的方便和高效方法的打日志方法。记住,在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。这里是使用SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar中的Log4j的适配器类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而不是Log4J

每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库如java.util.logging、Apache log4j、logback。但如果你还不知道SLF4J(Simple logging facade for Java)的话,那么是时候去在你项目中学习使用SLF4J了。
在这篇文章中,我们将学习为什么使用SLF4J比log4j或者java.util.logging要优秀。自从上次我写Java程序员的10个日志技巧已经有一段时间了,我已经不记得我写的关于日志的一切了。

不管怎样,让我们回到这个话题,SLF4J不同于其他日志类库,与其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。

如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。

总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物而且Apache commons logging也已经在使用这种思想了,但现在SLF4J正迅速成为Java世界的日志标准。让我们再看看几个使用SLF4J而不是log4j、logback或者java.util.logging的理由。

SLF4J对比Log4J,logback和java.util.Logging的优势

正如我之前说的,在你的代码中使用SLF4J写日志语句的主要出发点是使得你的程序独立于任意特定的日志类库,依赖于特定类可能需要不同与你已有的配置,并且导致更多维护的麻烦。但除此之外,还要一个SLF4J API的特性使得我坚持使用SLF4J而抛弃我长期间钟爱的Lof4j的理由,是被称为占位符(place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在String的format()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。即使你可能没需要那些对象,但这个依旧成立,取决于你的生产环境的日志级别,例如在DEBUG或者INFO级别的字符串连接。因为String对象是不可修改的并且它们建立在一个String池中,它们消耗堆内存( heap memory)而且大多数时间他们是不被需要的,例如当你的应用程序在生产环境以ERROR级别运行时候,一个String使用在DEBUG语句就是不被需要的。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。

这是你在Log4j中使用的方案,但肯定这一点都不有趣并且降低了代码可读性因为增加了不必要的繁琐重复代码(boiler-plate code):

if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
另一方面,如果你使用SLF4J的话,你可以得到在极简洁的格式的结果,就像以下展示的一样:

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
在SLF4J,我们不需要字符串连接而且不会导致暂时不需要的字符串消耗。取而代之的,我们在一个以占位符和以参数传递实际值的模板格式下写日志信息。你可能会在想万一我有很个参数怎么办?嗯,那么你可以选择使用变量参数版本的日志方法或者用以Object数组传递。这是一个相当的方便和高效方法的打日志方法。记住,在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。这里是使用SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar中的Log4j的适配器类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做Log4J的日志记录

除了以上好处,我想还有一个告诫,就是为了使用SLF4J,你不仅需要包含SLF4J的API jar包,例如 slf4j-api-1.6.1.jar,还需要相关Jar包,这取决于你在后台使用的日志类库。如果你想要使用和Log4J 一起使用SLF4J ,Simple Logging Facade for Java,,你需要包含以下的Jar包在你的classpath中,取决于哪个SLF4J和你在使用的Log4J的版本。例如:

slf4j-api-1.6.1.jar – JAR for SLF4J API
log4j-1.2.16.jar – JAR for Log4J API
slf4j-log4j12-1.6.1.jar – Log4J Adapter for SLF4J
如果你在使用Maven去管理你的项目依赖,你只需要包含SLF4J JAR包,maven会包含它的依赖的相关包。为了和SLF4J一起中使用Log4J,你可以包含以下的依赖在你项目中的pom.xml。


org.slf4j
slf4j-log4j12
1.6.1



org.slf4j
slf4j-log4j12
1.6.1

还有,如果你对于使用变量参数版本(variable argument version )的日志方法感兴趣的话,那么就导入SLF4J 1.7的版本吧。

总结

总结这次说的,我建议使用SLF4J的而不是直接使用 Log4j, commons logging, logback 或者 java.util.logging 已经足够充分了。

在你的开源或内部类库中使用SLF4J会使得它独立于任何一个特定的日志实现,这意味着不需要管理多个日志配置或者多个日志类库,你的客户端会很感激这点。
SLF4J提供了基于占位符的日志方法,这通过去除检查isDebugEnabled(), isInfoEnabled()等等,提高了代码可读性。
通过使用SLF4J的日志方法,你可以延迟构建日志信息(Srting)的开销,直到你真正需要,这对于内存和CPU都是高效的。
作为附注,更少的暂时的字符串意味着垃圾回收器(Garbage Collector)需要做更好的工作,这意味着你的应用程序有为更好的吞吐量和性能。
这些好处只是冰山一角,你将在开始使用SL4J和阅读其中代码的时候知道更多的好处。我强烈建议,任何一个新的Java程序员,都应该使用SLF4J做日志而不是使用包括Log4J在内的其他日志API。


slf4j-api-1.5.2.jar实现类是什么

有很多啊,有Log4j,LogBack,甚至JDK自带的Logger都是slf4j的实现类。
你如果要使用Log4j,那么除了slf4j-api外还需要一个slf4j-log4j12(或slf4j-log4j13这个根据你的Log4j的版本来),你如果要使用JDK作为实现的话,那么除了slf4j-api外,还需要slf4j-jdk14-1.5.10。依次类推。slf4j的作者就是Log4j的作者,他现在已经不推荐使用log4j了,推荐使用logback。


如何配置SLF4J不同的日志实现

使用slf4j库作为你的Java应用日志API层有很多好处,这里我会展示一小部分关于如何使用和配置它的例子。
你可以把slf4j想成一个Java的接口,然后你需要实现这个接口,从而在运行时提供实际的日志记录,例如把日志写到控制台(STDOUT)或者一个文件上等等。每种实现(或成为绑定)都显而易见地有他们自己的方式来配置日志的输出,但是你的应用对实现者并无感知并且一直使用相同的org.slf4j.Logger API。让我们看看如何在实际中使用它。
使用slf4j做简单的日志记录
创建基于Maven的工程,在pom.xml中添加如下内容

org.slf4j
slf4j-api
1.7.5


现在你可以在你的Java代码中这样使用Logger
package deng;

import org.slf4j.*;

public class Hello {
static Logger LOGGER = LoggerFactory.getLogger(Hello.class);
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
if (i % 2 == 0)
LOGGER.info("Hello {}", i);
else
LOGGER.debug("I am on index {}", i);
}
}

以上代码可以编译通过,但是当你运行它时,你会看到这样的输出
bash> java deng.Hello
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.

上面的意思是,在运行时,你没有做日志的实现(或者说日志的绑定),所以slf4j简简单单的使用了一个什么也不会做的空实现。
为了看到正确的输出,你应该尝试使用一个简单(simple)的实现,这个实现根本不需要任何配置!只要回到pom.xml然后添加如下配置

org.slf4j
slf4j-simple
1.7.5


现在你可以在控制台(STDOUT)看见INFO级别的日志输出了。这个简单的logger会默认显示任何INFO或者更高级别的信息。想要看DEBUG级别的信息,你需要在Java启动时传入这个系统属性(system property)
-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG
使用slf4j与Log4j日志
现在我们可以试验并更换不同的日志实现,但你的程序代码可以保持不变。
我们要做的是用另一个流行的日志实现来替换掉slf4j-simple,比如Log4j。

org.slf4j
slf4j-log4j12
1.7.5


又一次,我们必须对我们选的每一个日志实现做配置。在这个例子中,我们需要一个文件src/main/resources/log4j.properties。
log4j.rootLogger=DEBUG, STDOUT
log4j.logger.deng=INFO
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

重运行你的程序,你会看到同样的输出结果。
使用slf4j和JDK日志
JDK实际上带有一个日志包,你可以在pom.xml中替换为另一个日志实现。

org.slf4j
slf4j-jdk14
1.7.5


现在JDK日志的配置起来有一点困难。这里不仅需要一个配置文件src/main/resources/logging.properties,你还需要添加一个系统属性
-Djava.util.logging.config.file=logging.properties
这是一个logging.properties的示例
level=INFO
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINEST
deng.level=FINEST
使用slf4j和Logback日志
Logback日志实现是一个高质量的实现。如果你想在项目中写认真的代码,你会想要考虑这中方式。还是修改你的pom.xml文件,替换成这样

ch.qos.logback
logback-classic
1.0.13


这是一个简单的配置src/main/resources/logback.xml



%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n










用slf4j写你自己的日志库
如果你正在很多终端用户(END USER)提供Java库,那么让你的项目仅依赖slf4j-api绝对是个好想法,然后让你的用户在他们开发和运营环境中选择任意的日志实现。作为终端用户,他们可以快速地从上面提到的日志库中选择一个,并且从他们自己喜欢的日志实现的特性中受益。


为什么使用 SLF4J 而不是 Log4J 来做 Java 日志

您好,很高兴为您解答。在代码中编写日志记录语句使用SLF4J的主要动机是让程序独立于任何特定的日志记录库,这些日志记录库可能需要与现在配置不同的配置,而且还会引入更多令人头疼的维护问题。然而除了这个之外,SLF4J API还有一个让您使用SLF4J而不是用长期感兴趣的 Log4j 更让人信服的功能,就是占位符功能,在代码中用{}来表示。占位符功能与 String的format()方法中的%s非常相似,因为它在运行时刻才提取所提供的真正的字符串。这不仅缩减了代码中的许多字符串连接,而且减少了创建String对象所需要的资源。即便在生产环境日志级别比如DEBUG和INFO级别的字符串连接可能不需要的时候,仍然可以起到同样的效果。由于字符串是不可更改的 ,而它们是在字符串池中创建的,这些字符串使用了堆内存 ,当应用在生产环境中运行在ERROR级别的时候,字符串在大多数情况下就不是必须的,比如DEBUG语句里的字符串就不是必须的。通过使用SLF4J可以延迟字符串的创建到运行时刻,这意味着只有在需要字符串的时候才创建它。如果已经使用了log4j,那么您已经熟悉把调试语句放入if()条件内的工作场景,而SLF4J占位符功能比log4j更适合这种场景。下面是用Log4j时的做法,当然这并不好玩而且它增加了不必要的公式化的代码,减少了代码的可读性。if (logger.isDebugEnabled()) { logger.debug("Processing trade with id: " + id + " symbol: " + symbol);}如果使用SLF4J,可以使用更简洁的格式达到同样的效果,如下:logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);在SLF4J中,我们不需要进行字符串拼接,不会导致使用临时字符串带来的消耗。相反,我们使用带占位符的模板消息来记录日志信息,并提供实际值作为参数。在为日志信息产生最终的字符串之前,该方法会检查是否开启了特定的日志级别,这不仅降低了内存占用,而且预先减少了执行字符串拼接所消耗的CPU时间。下面的SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar包里的Log4j的适配器类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()); }}日志也会对应用程序的性能产生压力,大家通常宣扬的是只在生产环境中才强制记录日志。如若满意,请点击右侧【采纳答案】,如若还有问题,请点击【追问】希望我的回答对您有所帮助,望采纳! ~ O(∩_∩)O~

slf4j-api,slf4j-log4j12以及log4j之间什么关系

几乎在每个jar包里都可以看到log4j的身影,在多个子工程构成项目中,slf4j相关的冲突时不时就跳出来让你不爽,那么slf4j-api、slf4j-log4j12还有log4j他们是什么关系?我把自己了解的和大家简单分享一下:
slf4j:Simple Logging Facade for Java,为java提供的简单日志Facade。Facade:门面,更底层一点说就是接口。他允许用户以自己的喜好,在工程中通过slf4j接入不同的日志系统。更直观一点,slf4j是个数据线,一端嵌入程序,另一端链接日志系统,从而实现将程序中的信息导入到日志系统并记录。
因此,slf4j入口就是众多接口的集合,他不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体有哪些接口,全部都定义在slf4j-api中。查看slf4j-api源码就可以发现,里面除了public final class LoggerFactory类之外,都是接口定义。因此,slf4j-api本质就是一个接口定义。

下图比较清晰的描述了他们之间的关系:




当系统采用log4j作为日志框架实现的调用关系:

首先系统包含slf4j-api作为日志接入的接口;

at compile时slf4j-api中public final class LoggerFactor类中
private final static void bind() 方法会寻找具体的日志实现类绑定,主要通过
StaticLoggerBinder.getSingleton();语句调用


slf4j-log4j12:链接slf4j-api和log4j中间的适配器。它实现了slf4j-apiz中StaticLoggerBinder接口,从而使得在编译时绑定的是slf4j-log4j12的getSingleton()方法
log4j:这个是具体的日志系统。通过slf4j-log4j12初始化Log4j,达到最终日志的输出


上一篇:珠宝展台

下一篇:六维力传感器