为什么不使用java.util.logging?

pet*_*erh 333 java logging logback slf4j

这是我生命中的第一次,我发现自己处于一个可以开源的Java API的位置.希望被包括在许多其他项目中.

对于日志记录我(以及与我一起工作的人)总是使用JUL(java.util.logging)并且从未遇到任何问题.但是现在我需要更详细地了解我应该为我的API开发做些什么.我已经对此做了一些研究,并且我得到的信息让我感到更加困惑.因此这篇文章.

由于我来自JUL,我对此持偏见态度.我对其余的知识并不是那么大.

从我所做的研究中我得出了人们不喜欢JUL的原因:

  1. "在Sun发布JUL之前,我开始使用Java进行开发,而且我更容易继续使用logging-framework-X而不是学习新东西".嗯.我不是在开玩笑,这实际上就是人们所说的.有了这个论点,我们都可以做COBOL.(但我当然可以说这是一个懒惰的家伙)

  2. "我不喜欢JUL中日志记录级别的名称".好吧,说真的,这还不足以成为引入新依赖的理由.

  3. "我不喜欢JUL输出的标准格式".嗯.这只是配置.你甚至不需要做任何代码方面的事情.(确实,过去你可能不得不创建自己的Formatter类来实现它).

  4. "我使用其他也使用logging-framework-X的库,所以我觉得使用那个更容易".这是一个循环论证,不是吗?为什么'每个人'都使用logging-framework-X而不是JUL?

  5. "其他人都在使用logging-framework-X".这对我来说只是上面的一个特例.多数并不总是正确的.

所以真正的大问题是为什么不是JUL?.我错过了什么?伐木立面的存在理由(SLF4J,JCL)是历史上存在多种伐木实施,其原因可以追溯到JUL之前的时代,正如我所看到的那样.如果JUL是完美的那么伐木外墙将不存在,或者什么?我们不应该首先质疑为什么它们是必要的而不是拥抱它们?(看看这些原因是否仍然存在)

好吧,到目前为止我的研究已经导致我可以看到的一些事情可能是JUL的真正问题:

  1. 表现.有人说SLF4J的表现优于其他表现.在我看来,这是一个过早优化的案例.如果你需要每秒记录数百兆字节,那么无论如何我都不确定你是否在正确的路径上.JUL也在不断发展,你在Java 1.4上做的测试可能不再适用.你可以在这里阅读它,这个修复已经成为Java 7.许多人还谈到了日志记录方法中字符串连接的开销.但是,基于模板的日志记录可以避免这种成本,并且它也存在于JUL中.我个人从来没有真正编写基于模板的日志记录 太懒了.例如,如果我使用JUL执行此操作:

    log.finest("Lookup request from username=" + username 
       + ", valueX=" + valueX
       + ", valueY=" + valueY));
    
    Run Code Online (Sandbox Code Playgroud)

    我的IDE会警告我并请求允许它将其更改为:

    log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", 
       new Object[]{username, valueX, valueY});
    
    Run Code Online (Sandbox Code Playgroud)

    ..我当然会接受.许可授予 !谢谢您的帮助.

    所以我自己并没有自己编写这样的语句,这是由IDE完成的.

    关于性能问题的结论我没有发现任何迹象表明JUL的表现与竞争对手相比并不好.

  2. 从类路径配置.开箱即用的JUL无法从类路径加载配置文件.要做到这一点,需要几行代码.我可以看出为什么这可能很烦人,但解决方案简短而简单.

  3. 输出处理程序的可用性.JUL带有5个开箱即用的输出处理程序:控制台,文件流,套接字和内存.这些可以扩展或可以编写新的.例如,这可能是写入UNIX/Linux Syslog和Windows事件日志.我个人从来没有这个要求,也没有看过它,但我当然可以说明为什么它可能是一个有用的功能.例如,Logback附带了Syslog的附加程序.我仍然会争辩

    1. JUL开箱即用的产品涵盖99.5%的输出目的地需求.
    2. 特殊需求可以通过JUL之上的自定义处理程序来满足,而不是在其他方面.对我而言,没有任何迹象表明为JUL编写Syslog输出处理程序需要花费更多时间,而不是为另一个日志框架编写.

我真的很担心我忽略了一些东西.除了JUL之外,使用伐木外墙和伐木实施是如此普遍,我必须得出结论,我只是不明白.那恐怕不是第一次.:-)

那我该怎么办?我希望它成功.我当然可以"顺其自然"并实施SLF4J(这些日子似乎最受欢迎)但是为了我自己的缘故,我仍然需要明白今天的JUL究竟出了什么问题才能保证所有的模糊?我会为我的图书馆选择JUL来破坏自己吗?

测试性能

(nolan600于2012年7月7日添加的部分)

下面有一篇来自Ceki的参考文献,其中提到SLF4J的参数化比JUL快10倍或更快.所以我开始做一些简单的测试.乍一看,这种说法肯定是正确的.以下是初步结果(但请继续阅读!):

  • 执行时间SLF4J,后端Logback:1515
  • 执行时间SLF4J,后端JUL:12938
  • 执行时间JUL:16911

上面的数字是msecs,所以越少越好.因此,10倍的性能差异实际上非常接近.我最初的反应:这是很多!

这是测试的核心.可以看出,整数和字符串是在循环中构造的,然后在log语句中使用:

    for (int i = 0; i < noOfExecutions; i++) {
        for (char x=32; x<88; x++) {
            String someString = Character.toString(x);
            // here we log 
        }
    }
Run Code Online (Sandbox Code Playgroud)

(我希望log语句既包含原始数据类型(在本例中为int),也包含更复杂的数据类型(在本例中为String).不确定它是否重要但是你有它.)

SLF4J的日志语句:

logger.info("Logging {} and {} ", i, someString);
Run Code Online (Sandbox Code Playgroud)

JUL的日志声明:

logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
Run Code Online (Sandbox Code Playgroud)

JVM在实际测量完成之前执行了相同的测试,"预热"了一次.在Windows 7上使用Java 1.7.03.使用了最新版本的SLF4J(v1.6.6)和Logback(v1.0.6).Stdout和stderr被重定向到null设备.

但是,现在小心,事实证明JUL花费了大部分时间,getSourceClassName()因为JUL默认在输出中打印源类名,而Logback则没有.所以我们比较苹果和橘子.我必须再次进行测试并以类似的方式配置日志记录实现,以便它们实际输出相同的内容.但是我确实怀疑SLF4J + Logback仍然会出现在顶部但远离上面给出的初始数字.敬请关注.

顺便说一句:测试是我第一次使用SLF4J或Logback.愉快的经历.当你开始时,JUL肯定不那么热情了.

测试性能(第2部分)

(nolan600于2012年7月8日添加的部分)

事实证明,如何在JUL中配置模式并不是真正重要的,即它是否包含源名称.我尝试了一个非常简单的模式:

java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
Run Code Online (Sandbox Code Playgroud)

并没有改变上述时间.我的探查器显示,getSourceClassName()即使这不是我的模式的一部分,记录器仍然花了很多时间来打电话.模式并不重要.

因此,我在性能问题上得出结论,至少对于基于测试模板的日志语句,JUL(慢速)和SLF4J + Logback(快速)之间的实际性能差异似乎大约为10倍.就像赛奇说的那样.

我还可以看到另一件事,即SLF4J的getLogger()呼叫比JUL的同样昂贵得多.(如果我的探查器准确,则为95 ms vs 0.3 ms).这是有道理的.SLF4J必须在底层日志记录实现的绑定上做一些时间.这不会吓到我.这些调用在应用程序的生命周期中应该是少见的.牢度应该在实际的日志调用中.

定论

(nolan600于2012年7月8日添加的部分)

谢谢你的所有答案.与我最初的想法相反,我最终决定将SLF4J用于我的API.这是基于许多事情和您的意见:

  1. 它提供了在部署时选择日志实施的灵活性.

  2. 在应用程序服务器内运行时,JUL配置缺乏灵活性的问题.

  3. 如上所述,SLF4J肯定要快得多,特别是如果你将它与Logback结合使用.即使这只是一个粗略的测试,我也有理由相信在SLF4J + Logback上的优化要比JUL更多.

  4. 文档.SLF4J的文档更加全面和精确.

  5. 模式灵活性.当我进行测试时,我开始让JUL模仿Logback的默认模式.此模式包含线程的名称.事实证明,JUL无法开箱即用.好吧,直到现在我还没有错过它,但我认为这不应该是日志框架中遗漏的东西.期!

  6. 今天大多数(或许多)Java项目使用Maven,因此添加依赖项并不是那么重要,特别是如果该依赖项相当稳定,即不会不断更改其API.这似乎适用于SLF4J.此外,SLF4J罐子和朋友的体积都很小.

所以发生的奇怪事情是,在与SLF4J合作之后我真的对JUL感到非常不满.我仍然感到遗憾的是,JUL必须采用这种方式.JUL远非完美,但有点像这样的工作.只是不太好.Properties作为一个例子可以说同样的事情,但我们不考虑抽象,所以人们可以插入他们自己的配置库和你有什么.我认为原因是它Properties刚好超出了标准,而今天的JUL却恰恰相反......而在过去,由于它不存在,所以它在零时出现.

Cek*_*eki 192

免责声明:我是log4j,SLF4J和logback项目的创始人.

偏爱SLF4J有客观原因.首先,SLF4J允许最终用户自由选择底层日志框架.此外,更精明的用户往往更喜欢使用logback来提供超越log4j的功能,而jul则落后了.对于某些用户而言,功能方面的jul可能就足够了,但对于许多其 简而言之,如果日志记录对您很重要,您可能希望将SLF4J与logback一起用作底层实现.如果记录不重要,那么jul很好.

但是,作为oss开发人员,您需要考虑用户的偏好而不仅仅是您自己的偏好.因此你应该采用SLF4J并不是因为确信SLF4J比jul好,而是因为大多数Java开发人员目前(2012年7月)更喜欢SLF4J作为他们的日志API.如果您最终决定不关心民意,请考虑以下事实:

  1. 那些喜欢jul的人这样做是出于方便,因为jul与JDK捆绑在一起.据我所知,没有其他客观论据支持jul
  2. 你自己对jul的偏好只是一个偏好.

因此,在这种情况下,将"硬性事实"置于公众舆论之上虽然看似勇敢,但却是一种逻辑谬误.

如果仍然不相信,JB Nizet提出了另一个有力的论点:

除了最终用户可能已经为他自己的代码或使用log4j或logback的另一个库完成了这个自定义.jul是可扩展的,但是必须扩展logback,jul,log4j和God只知道哪个其他日志框架,因为他使用了四个使用四个不同日志框架的库是麻烦的.通过使用SLF4J,您可以让他配置他想要的日志框架,而不是您选择的日志框架.请记住,一个典型的项目使用无数的库,而不仅仅是你的.

如果无论出于何种原因你讨厌SLF4J API并使用它会消除你工作的乐趣,那么一定要去jul毕竟,有办法将jul重定向到SLF4J.

顺便说一句,jul参数化比SLF4J慢至少10倍,最终产生明显的差异.

  • 我在这里,使用 SLF4J,我*仍然*必须处理其他库使用的所有其他日志记录框架。使用 SLF4J 并不能解决异构记录器的问题,只会让问题变得更糟。https://xkcd.com/927/ (7认同)
  • 我的帖子的本质是不同的开发者有不同的偏好,似乎无可争议.是? (3认同)
  • 此外,jul 在 1.8 中添加了对“Supplier”生成日志消息的支持,这在创建日志消息方面提供了无限的灵活性,而不会牺牲禁用的日志记录性能 - SLF4J 是否有类似的功能解决方案或仅格式化字符串日志记录? (3认同)
  • 但老实说,我很想看到 2018 年 Java 11(或最终的任何结果)的基准测试,以及异步模式下 log4j2 的基准测试。 (3认同)
  • 哈哈,昨天我不得不调试一个使用 SLF4J 的项目。日志记录功能没有执行任何操作,并且无法确定原因。java.util.logging 也失败了。因此,我求助于编写一个以附加模式打开文件的类(这与日志记录一样,Java 变成了一个涉及四分之一个对象的难题)。现在我正在调试另一个 Java 项目,但日志记录也不起作用!我已经删除了另一个项目,所以我最终将从头开始重写我的文件附加器。我不会尝试将 SLF4J 添加到项目中。 (3认同)
  • @Ceki你可能想详细说明你的免责声明,所以它提到了你在log4j,slf4j和logback项目中的当前角色.原因很自然地可以解释你的偏见. (2认同)
  • 大多数 Java 开发人员更喜欢 SLF4J 作为他们的日志 API 的说法是否有一些支持? (2认同)
  • jul 的另一个客观论据是,如果您希望 Java 应用程序的下载大小尽可能小,那么如果您使用 jul 而不是捆绑 SLF4J 或其他任何东西,它会更小。如今,Java 似乎主要只用于企业软件,所以人们往往会忘记这一点...... (2认同)
  • SLF4J 强制对我的最终用户(作为库)进行短暂依赖。这种暂时的依赖性可能会与他们正在使用的其他库或由应用程序服务器提供的库的版本发生冲突。在我看来,这个答案没有解决的问题是:OSS 库、内部库/工具和应用程序之间有区别吗?SLF4J 应该成为其中任何一个的首选吗?因为很多图书馆似乎仍然更喜欢 JUL。 (2认同)

Joa*_*uer 32

  1. java.util.logging在Java 1.4中引入.之前有用于记录的原因,这就是为什么存在许多其他日志记录API的原因.那些在Java 1.4之前大量使用的API因此具有很好的市场份额,当1.4发布时它不会降到0.

  2. JUL没有开始那么好,很多你提到的东西在1.4中差很多而且只在1.5中变得更好(我猜在6中也是如此,但我不太确定).

  3. JUL不适合在同一JVM中具有不同配置的多个应用程序(想想不应该交互的多个Web应用程序).Tomcat需要通过一些箍来实现这一点(如果我理解正确的话,有效地重新实现JUL).

  4. 您无法始终影响库使用的日志记录框架.因此,使用SLF4J(实际上只是一个非常薄的API层,高于其他库)有助于保持整个日志世界的一致性(因此您可以决定底层日志框架,同时仍在同一系统中进行库日志记录).

  5. 图书馆不容易改变.如果用于使用logging-library-X的以前版本的库它不能轻易切换到logging-library-Y(例如JUL),即使后者显然是优等的:该库的任何用户都需要学习新的日志框架和(至少)重新配置他们的日志记录.这是一个很大的禁忌,特别是当它给大多数人带来明显的收益时.

尽管如此,我认为JUL 至少是目前其他日志框架的有效替代品.

  • 好吧,谁忽略历史注定要重复它;-)历史在软件开发中非常相关.如果标准API至少*与第三方库一样好,那么人们不会移动太快并且用标准API*替换现有的第三方库.而且他们最初没有(并且在某些情况下可能仍然没有). (3认同)
  • @ nolan6000:关于这个短语的详细信息,我对这些细节知之甚少,并不是我正在做的事情.即使JUL现在与第三方框架相提并论,惯性和现有基础设施仍然是不切换的有力理由.例如,如果库X在版本1.1中使用了slf4j,那么在1.2(甚至2.0)中切换到JUL将是许多用户的主要问题(他们已经正确配置了旧系统,并且必须重新执行此操作才能获得明显的收益) . (3认同)
  • 这里的第 3 点仍然是最相关的 - 正是因为 jul 是平台的一部分,所以使其立即处于劣势。任何其他日志框架都可以在具有独立配置的不同类加载器中拥有不同的框架或同一框架的不同版本,但 jul 位于引导类路径上,因此根据定义,每个 JVM 必须是一组配置。您特别_不希望_将日志框架与 JVM 捆绑在一起。 (3认同)

JB *_*zet 28

恕我直言,使用像slf4j这样的日志外观的主要优点是你让库的最终用户选择他想要的具体日志记录实现,而不是将你的选择强加给最终用户.

也许他已经在Log4j或LogBack(特殊格式化程序,appender等)上投入了时间和金钱,并且更喜欢继续使用Log4j或LogBack,而不是配置jul.没问题:slf4j允许这样做.使用Log4j比jul更明​​智吗?也许,也许不是.但你不在乎.让最终用户选择他喜欢的东西.

  • 除了最终用户可能已经为他自己的代码或使用log4j或LogBack的另一个库完成了这个自定义.jul是可扩展的,但是必须扩展LogBack,jul,log4j和God只知道哪个其他日志框架,因为他使用了4个使用4个不同日志框架的库是麻烦的.通过使用slf4j,您可以让他配置他想要的日志框架.不是你选择的那个.请记住,典型的项目使用无数的库,而不仅仅是你的. (12认同)

Old*_*eon 6

我开始像我怀疑的那样,使用JUL,因为这是最容易立即开始的.然而,多年来,我已经希望我花了一点时间选择.

我现在的主要问题是我们有大量的"库"代码在许多应用程序中使用,它们都使用JUL.每当我在Web服务类型的应用程序中使用这些工具时,日志记录就会消失或变得无法预测或奇怪.

我们的解决方案是为库代码添加一个外观,这意味着库日志调用没有改变,但是动态地重定向到可用的任何日志记录机制.当包含在POJO工具中时,它们被定向到JUL,但当部署为Web应用程序时,它们被重定向到LogBack.

我们的遗憾 - 当然 - 库代码不使用参数化日志记录,但现在可以在需要时对其进行改造.

我们使用slf4j来构建外观.

  • 您不只是使用 slf4j 发行版中的“重定向 java.util.logging 到 slf4j”包的任何原因? (2认同)
  • 我们做到了,但收效甚微,因为迁移到slf4j的主要好处是有效的参数化日志记录。如果我们从一开始就使用它,那么我们现在就没有任何工作要做。 (2认同)
  • 我同意这是 slf4j 的悬而未决的成果。 (2认同)