这是我有生以来第一次发现自己正在编写一个开源的Java API。希望能被列入许多其他项目。
对于日志记录,我(以及与我一起工作的人)一直使用JUL(java.util.logging),从来没有遇到过任何问题。然而,现在我需要更详细地了解我应该为我的API开发做些什么。我对此做了一些研究,我得到的信息让我更加困惑。因此有了这篇文章。
因为我是从七月来的,所以我对此有偏见。我对其余的知识不是那么多。
从我所做的研究中,我得出了人们不喜欢Jul的以下原因:
>
“早在Sun发布JUL之前,我就开始在Java进行开发了,对我来说,继续使用logging-framework-X比学习新东西更容易”。嗯。我不是在开玩笑,这其实是人们所说的。有了这个论证,我们都可以在做COBOL。(不过,我也可以肯定地认为自己是一个懒惰的家伙)
“我不喜欢JUL的伐木等级名称”。好吧,说真的,这还不足以作为引入新依赖项的理由。
“我不喜欢JUL输出的标准格式”。嗯。这只是配置而已。您甚至不需要在代码方面做任何事情。(确实,在过去,您可能需要创建自己的格式化程序类来正确地实现它)。
“我使用的其他库也使用logging-framework-X,所以我认为使用那个库更容易”。这是一个循环的论点,不是吗?为什么“Everybody”使用logging-framework-X而不是Jul?
“其他人都在使用Logging-Framework-X”。这对我来说只是上面的一个特例。多数并不总是正确的。
所以真正的大问题是为什么不是Jul?。我错过了什么?logging facades(SLF4J,JCL)存在的理由是历史上存在过多种日志记录实现,我认为这实际上可以追溯到JUL之前的时代。如果JUL是完美的,那么伐木门面就不存在了,或者怎么样?更令人困惑的是,JUL在某种程度上是一个门面本身,允许处理程序、格式化程序甚至日志管理器进行交换。
与其接受多种方式来做同一件事(日志记录),我们难道不应该质疑为什么它们在一开始是必要的吗?(并看看那些原因是否还存在)
好吧,到目前为止,我的研究已经导致了一些事情,我可以看到,可能是Jul的真正问题:
>
性能。有人说SLF4J的性能优于其他的。在我看来,这是一个过早优化的案例。如果您需要记录每秒数百兆字节,那么我不确定您是在正确的道路上无论如何。JUL也进化了,你在Java 1.4上做的测试可能不再是真的。你可以在这里读到它,这个修正已经进入了Java 7号。许多人还讨论了日志记录方法中字符串级联的开销。然而,基于模板的日志记录避免了这一代价,并且在JUL中也存在。就我个人而言,我从来没有真正写过基于模板的日志记录。太懒了。例如,如果我对jul执行以下操作:
log.finest("Lookup request from username=" + username
+ ", valueX=" + valueX
+ ", valueY=" + valueY));
我的IDE将警告我并请求允许它将其更改为:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}",
new Object[]{username, valueX, valueY});
我当然会接受。批准了!谢谢你的帮助。
所以我并不是自己写这样的语句,这是由IDE完成的。
总而言之,关于表现的问题,我没有发现任何可以表明朱尔的表现与比赛相比不是很好的东西。
类路径中的配置。开箱即用的JUL无法从类路径加载配置文件。它只需要几行代码就可以做到这一点。我明白为什么这可能是恼人的,但解决方案是简短和简单的。
输出处理程序的可用性。JUL自带5个输出处理程序:控制台、文件流、套接字和内存。这些可以扩展,也可以编写新的。例如,这可能是写入UNIX/Linux系统日志和Windows事件日志。我个人从来没有这个需求,也没有看到它被使用过,但我可以肯定地解释为什么它可能是一个有用的特性。例如,Logback附带了Syslog的附加器。但我还是要说
我真的很担心我忽略了什么。除了JUL之外,日志外观和日志实现的使用是如此广泛,以至于我不得不得出结论,就是我不理解。恐怕这不是第一次了。:-)
那么我应该如何处理我的API呢?我希望它能成功。当然,我可以“随波逐流”地实现SLF4J(这似乎是最近最流行的),但为了我自己的缘故,我仍然需要弄清楚今天的JUL到底是怎么了,才会有这么多的模糊?我选择JUL作为我的图书馆,会破坏我自己吗?
(nolan600于2012年7月7日增加的部分)
下面有一个来自Ceki的参考,关于SLF4J的参数化比JUL的快10倍或更多。所以我开始做一些简单的测试。乍一看,这个说法当然是正确的。以下是初步结果(但请继续阅读!):
上面的数字是MSEC,所以越少越好。所以10倍的性能差实际上是非常接近的。我最初的反应是:那是很多!
这里是测试的核心。可以看到,一个整数和一个字符串在循环中被解释,然后在log语句中使用:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(我希望log语句同时具有原始数据类型(在本例中为int)和更复杂的数据类型(在本例中为String)。不确定这有什么关系,但您已经拥有了它。)
SLF4J的log语句:
logger.info("Logging {} and {} ", i, someString);
jul的log语句:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
在实际测量完成之前,JVM“预热”了一次,执行了一次相同的测试。在Windows7上使用了Java 1.7.03。使用了最新版本的SLF4J(v1.6.6)和Logback(v1.0.6)。Stdout和stderr被重定向到空设备。
但是,现在要小心了,JUL的大部分时间都花在getSourceClassName()
上,因为JUL默认在输出中打印源类名,而Logback不打印。所以我们在比较苹果和橘子。我必须再次进行测试,并以类似的方式配置日志记录实现,以便它们实际输出相同的内容。然而,我怀疑SLF4J+Logback仍然会排在首位,但与上面给出的初始数字相去甚远。请继续收看。
顺便说一句:这个测试是我第一次实际使用SLF4J或Logback。愉快的经历。当你刚开始的时候,七月肯定就不那么受欢迎了。
(nolan600于2012年7月8日增加的部分)
事实证明,在JUL中如何配置模式对性能并不重要,即它是否包含源名。我尝试了一个非常简单的模式:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
而这一点也没有改变上述的时间安排。我的探查器显示,记录器仍然花费大量时间调用getSourceClassName()
,即使这不是我模式的一部分。模式不重要。
因此,关于性能问题,我得出的结论是,至少对于测试过的基于模板的log语句,在JUL(慢)和slf4j+logback(快)之间的实际性能差异大约是10倍。就像切基说的。
我还可以看到另一件事,即SLF4J的getLogger()
调用比Jul的同上调用要贵得多。(如果我的探查器准确,则为95毫秒,而0.3毫秒)。这是有道理的。SLF4J必须花一些时间绑定底层日志记录实现。这吓不倒我。在应用程序的生存期内,这些调用应该是比较少见的。牢度应该在实际的日志调用中。
(nolan600于2012年7月8日增加的部分)
谢谢你的回答。与我最初的想法相反,我最终决定将SLF4J用于我的API。这是基于许多事情和您的输入:
>
它提供了在部署时选择日志实现的灵活性。
JUL的配置在应用服务器内部运行时缺乏灵活性的问题。
SLF4J当然要快得多,如上面所详述的,特别是如果您将它与Logback结合在一起。即使这只是一个粗略的测试,我有理由相信在SLF4J+Logback上做了更多的优化工作,而不是在Jul上。
文件。SLF4J的文档更加全面和精确。
图案柔韧性。在我进行测试时,我开始让JUL模拟Logback中的默认模式。这个模式包括线程的名称。事实证明JUL不可能做到这一点。好的,直到现在我都没有错过它,但是我不认为它是一个日志框架应该缺少的东西。句号!
现在大多数(或许多)Java项目都使用Maven,所以添加一个依赖项并不是什么大事,特别是当依赖项相当稳定的时候,也就是不经常改变它的API的时候。对于SLF4J来说似乎也是如此。而且SLF4J的罐子和朋友的体积都很小。
所以发生的奇怪的事情是,我实际上在与SLF4J工作了一段时间后,对JUL感到很不爽。我还是很遗憾Jul不得不这样。朱尔远不是完美的,但有点做的工作。只是还不够好。作为一个例子,properties
也可以这样说,但我们并不考虑对其进行抽象,这样人们就可以插入自己的配置库和您拥有的内容。我认为原因是properties
正好在横杆上方,而今天的JUL正好相反...在过去它是零的,因为它不存在。
免责声明:我是log4j、SLF4J和logback项目的创始人。
偏爱SLF4J是有客观原因的。首先,SLF4J允许最终用户自由选择底层日志记录框架。此外,精明的用户倾向于使用logback,它提供的功能超过了log4j,而J.U.L则远远落后。功能方面的J.U.L可能对一些用户来说已经足够了,但对许多其他用户来说却并非如此。简而言之,如果日志记录对您很重要,那么您将希望使用带有logback的SLF4J作为底层实现。如果伐木不重要,J.U.L也行。
但是,作为一名oss开发人员,您需要考虑用户的偏好,而不仅仅是您自己的偏好。因此,您应该采用SLF4J并不是因为您确信SLF4J比J.U.L更好,而是因为大多数Java开发人员目前(2012年7月)更喜欢将SLF4J作为他们的日志API。如果你最终决定不关心公众的意见,请考虑以下事实:
由此可见,将“铁的事实”凌驾于民意之上,而看似勇敢,在本案中却是一种逻辑谬误。
如果仍然没有被说服,JB Nizet提出了另外一个有力的论点:
但最终用户可能已经为自己的代码或使用log4j或logback的另一个库进行了这种定制。J.U.L是可扩展的,但是必须扩展logback、J.U.L、log4j和God只知道哪个其他日志框架,因为他使用了四个使用四个不同日志框架的库,这是很麻烦的。通过使用SLF4J,您允许他配置他想要的日志记录框架,而不是您选择的那个。请记住,一个典型的项目使用了无数的库,而不仅仅是您的库。
如果出于任何原因你讨厌SLF4J API,并且使用它会扼杀你工作的乐趣,那么一定要选择J.U.L。毕竟,有办法将J.U.L重定向到SLF4J。
顺便说一句,J.U.L参数化比SLF4J的慢至少10倍,最终产生了显著的差异。
问题内容: 我阅读的源代码,很惊讶地发现它不使用Knuth–Morris–Pratt算法?众所周知,KMP更有效。那么为什么不使用它呢? 我周围的人告诉我,对于短字符串KMP来说已经足够了,但是如果您需要性能并且打算与大字符串一起使用,则不是一个好选择。但是他没有告诉我细节。 所以,这是我的问题: 为什么我们不使用KMP ? 为什么KMP对于大字符串不是一个好选择? 问题答案: KMP在最坏情况下
问题内容: 据我所知,由于安全性,在JavaScript中使用JSON对象被认为是不好的做法。如果JSON来自其他服务器,我可以理解此问题。 但是,如果JSON是由我自己的服务器提供的,并且是使用PHP创建的(我们假设它不是越野车),那么简单地使用JS中的JSON读取是否合法,或者我目前无法想到任何安全问题? ? 我真的不想处理动态加载JSON解析器的问题,因此很高兴使用。 PS:很明显,我将使用
我是一个新手,学习Javascript已经有一段时间了,唯一的目的是用它来编写硒自动化(最终的目的是李尔QA自动化)。 问题是,当谈到语言时,我完全感到困惑。我在Selenium文档和Scriptp示例中看到的都是类似“driver.FindElement”、“sendKeys”、“getTitle”等函数。 据我所知,(纯)Javascript不使用这些函数,而是使用“document.getE
eval 函数会在当前作用域中执行一段 JavaScript 代码字符串。 var foo = 1; function test() { var foo = 2; eval('foo = 3'); return foo; } test(); // 3 foo; // 1 但是 eval 只在被直接调用并且调用函数就是 eval 本身时,才在当前作用域中执行。 var fo
问题内容: 为什么Hashtable不带钥匙? 另外,为什么HashMap允许键? 使这两个类的键行为如此不同的目的是什么? 问题答案: 从 JavaDoc: 简而言之,由于不是对象,因此无法调用或对其进行调用,因此无法计算哈希值以将其用作键。 较新,并且具有更高级的功能,基本上只是对功能的改进。这样,在创建时,它专门设计为将值作为键来处理,并将它们作为特殊情况来处理。 具体来说,在发出时,像这样
问题内容: 众所周知,Java 中的接口已损坏。造成这种情况的原因很多,我将不再赘述。其他人已经做到了。这也是Java架构师本身的立场。 因此,我的问题是:为什么还不被弃用?如果核心Java团队已确定它已损坏,那么他们还必须考虑过时。他们反对这样做的原因是什么(在Java 8中仍不建议弃用)? 问题答案: 有一个错误在1997年提交给Java的错误数据库有关添加方法,所以将不再是无用的。它以“无法