自迁移出 MediaWiki 以来,本页内容尚未经过审查。如果您愿意帮忙,请查看帮助指南!
如果您是一名 Java 开发人员,那么您很可能“喜欢”日志记录。至少,这是新 Java 开发人员不可避免地必须得出的结论,因为 97.3% 的 Java 库都是日志框架。考虑到日志记录的复杂性,很容易对任何给定项目的日志记录工作方式以及实际上整个 Java 软件生态系统中的日志记录工作方式感到困惑。幸运的是,这个友好的 wiki 页面可以帮助您一劳永逸地了解日志记录。
什么是日志记录?
日志记录是一种在程序执行期间报告消息和/或事件的技术。它可用于多种用途:调试、通知用户事情、永久记录所发生的事情等。
进一步阅读请参阅Wikipedia’s article on Java logging frameworks。
另请参阅这篇好文章:The State of Logging in Java(2018 年 5 月)。
为什么使用日志框架? System.out.println 有什么问题吗?
首先,System.out.println相当冗长。但更重要的是,它有点不灵活:您无法显式指定元数据,例如消息的“严重性”或“级别”、关联的异常堆栈跟踪、消息的来源或其他详细信息。并且您无法根据上下文自定义日志记录行为(例如,将某些类型的消息记录到文件中,而其他消息则记录到标准输出)。
由于这些原因,几乎每个人和他们的狗要么使用日志框架,要么发明自己的日志框架。 SciJava Common 项目选择了两者:SciJava 应用程序框架提供了LogService 来抽象和封装所有日志记录。该服务的默认核心实现称为StderrLogService,并且简单地将所有日志消息发送到标准错误流。这样做是为了避免依赖于任何外部日志框架或外观,并提供足够丰富的日志 API。
SLF4J 怎么样?
SLF4J项目是一个好主意:它不是另一个日志框架,而是日志框架的外观。也就是说:它是一个接口驱动的 API,可以通过专用的绑定库由您希望的任何日志框架支持。理论上,如果每个人都只使用 SLF4J,他们只需添加对 org.slf4j:slf4j-api 的编译时依赖项,并在运行时传送他们选择的绑定,以便他们的应用程序将使用 Apache Log4j 或 java.util logging 或 Logback 进行日志记录。因为每个开发人员都想自己决定,对吗?
不幸的是,SLF4J 有几个缺点:
- API 非常少。特别是,无法在运行时以与框架无关的方式设置日志级别。
- 它要求包含日志记录绑定,从而使应用程序部署复杂化not explicitly declared in the project’s dependency tree。
- 通过发明新的日志记录标准,就有了now one more competing standard。
尽管如此,SLF4J 仍然是一件好事,因此 SciJava 项目确实提供了LogService implementation backed by SLF4J,热心的开发人员可以使用它将所有 SciJava 日志记录重定向到 SLF4J 框架,然后他们可以使用他们选择的 SLF4J 绑定further redirect!
ImageJ2 中的日志记录如何工作?
ImageJ2 对所有核心日志记录使用 SciJava 日志记录框架。普通 ImageJ2 发行版使用 StderrLogService 将这些消息发送到标准错误流。默认日志级别为 INFO。
也就是说,在 GUI 驱动的应用程序中使用标准输出和/或错误流存在一个问题:默认情况下此类消息不可见。为了解决这个问题,许多 GUI 驱动的应用程序为日志消息实现了自己的 UI 组件,通常是某种“日志窗口”。此解决方案是原始 ImageJ 解决 GUI 端日志记录问题的方法:插件应调用静态 IJ.log 和 IJ.handleException 方法分别发出日志消息并向用户报告异常。
ImageJ2 默认使用 ImageJ 旧版用户界面运行,这意味着它继承了此日志窗口。但除此之外,它还提供了一个单独的控制台窗口,将所有消息记录到 stdout 和 stderr,这样就不会向用户隐藏任何可能有价值的控制台输出。每当发生任何 stderr 输出时,控制台窗口都会自动变为可见。 stderr 的输出显示为红色,以区别于 stdout 的输出。
因此,只要日志消息以某种方式最终出现在 stdout 或 stderr 上(默认情况下),一切都很好,用户将看到它们。但是,如果您配置自己的日志记录实现来抑制 stderr 行为,以支持例如记录到某处的文件,那么您就得靠自己了!
斐济的伐木工作如何?
ImageJ 的Fiji发行版是一个更复杂的野兽,因为它附带了许多插件,每个插件都可以在日志记录方面做自己的事情。 Fiji 不是 herd cats,而是简单地提供每个插件所需的日志框架,以便它可以畅通无阻地运行。
然而,最终,斐济发行版需要运送一个,而且“仅”一个,SLF4J 绑定;否则,基于 SLF4J 的日志记录将无法正常工作。截至撰写本文时,斐济发布了 logback-classic 绑定,这意味着基于 SLF4J 的日志记录可以是 configured in all the usual Logback ways。 Fiji 在 scijava-config 库中提供了默认的 Logback 配置,它将 INFO 级别事件发送到 stdout,并将更严重的级别(WARN、ERROR 和 FATAL)发送到 stderr。如果您想覆盖配置,则需要删除或修改该 JAR 文件,将 logback.xml 替换为您自己的设计之一。选择logback-classic的原因是因为Bio-Formats项目明确依赖于它;斐济并没有解决这个问题,而是随波逐流,并集中在相同的依赖关系上。
控制日志记录级别
如果您使用标准 SciJava LogService 实现,则可以通过 scijava.log.level 系统属性控制日志记录级别。
例如,在 Java 代码中:
System.setProperty("scijava.log.level", "debug");
或者在运行特定类时通过命令行:
java -Dscijava.log.level=debug ... myorg.MyMainClass
或者在启动 ImageJ2 时通过命令行:
ImageJ-[linux64|macosx|win32.exe|win64.exe] -Dscijava.log.level=debug
您甚至可以自定义每个包/类层次结构的日志记录行为。例如,仅打开 org.scijava.plugin 包和子包中的类的调试级别日志记录:
System.setProperty("scijava.log.level:org.scijava.plugin", "debug");
如果您更喜欢环境变量,设置 DEBUG 环境变量将导致 SciJava 日志记录默认为 DEBUG 级别,而不是通常的 WARN 级别。
在代码中使用 LogService
LogService 可以通过 SciJava 插件访问,其方式与任何其他 service 相同:作为用 @Parameter 注释的字段。有关详细信息,请参阅Writing plugins指南。