Page history Edit this page How do I edit this website?
Original MediaWiki page
This page describes content relating to the core SciJava software libraries. Click the logo for details.

Logging

The content of this page has not been vetted since shifting away from MediaWiki. If you’d like to help, check out the how to help guide!

If you are a Java developer, then it is very likely you love logging. At least, that’s the conclusion new Java developers inevitably must draw, based on the fact that 97.3% of all Java libraries in the wild are logging frameworks. Given the jungle of logging complexity out there, it’s easy to get confused about how logging works for any given project, and in fact how logging works in general across the Java software ecosystem. Fortunately, this friendly wiki page is here to help you understand logging once and for all.

What is logging?

Logging is a technique for reporting messages and/or events during program execution. It can be useful for many purposes: debugging, informing the user of things, keeping a permanent record of what transpired, etc.

See Wikipedia’s article on Java logging frameworks for further reading.

See also this nice article: The State of Logging in Java (May 2018).

Why use a logging framework? What’s wrong with System.out.println?

First of all, System.out.println is rather verbose. But more importantly, it is a bit inflexible: you cannot explicitly specify metadata such as the “severity” or “level” of the message, an associated exception stack trace, the source of the message, or other details. And you cannot customize the logging behavior based on context (e.g., log certain kinds of messages to a file, while others go to stdout).

For these reasons, nearly everyone and their dog either uses a logging framework, or invents their own. The SciJava Common project has opted to do both: the SciJava application framework provides a LogService to abstract and encapsulate all logging. The default core implementation of this service is called StderrLogService, and simply emits all log messages to the standard error stream. This is done to avoid dependencies on any external logging frameworks or facades, as well as to offer a sufficiently rich logging API.

What about SLF4J?

The SLF4J project is a great idea: rather than yet another logging framework, it is a facade for logging frameworks. That is: it is an interface-driven API which can be backed by any logging framework you wish, via a dedicated binding library. In theory, if everyone just used SLF4J, they’d simply add a compile-time dependency on org.slf4j:slf4j-api, and ship the binding of their choice at runtime so that their application would then log using, say, Apache Log4j or java.util logging or Logback. Because every developer wants to decide that for themselves, right?

Unfortunately, SLF4J suffers from a couple of downsides:

Still, SLF4J is a nice thing, so the SciJava project does provide a LogService implementation backed by SLF4J, which zealous developers can use to redirect all SciJava logging to the SLF4J framework, which they can then further redirect using an SLF4J binding of their choosing!

How does logging work in ImageJ2?

ImageJ2 uses the SciJava logging framework for all core logging. And the vanilla ImageJ2 distribution uses the StderrLogService which emits those messages to the standard error stream. The default log level is INFO.

That said, there is a problem with using the standard output and/or error streams in a GUI-driven application: such messages are not visible by default. To work around this fact, many GUI-driven applications implement their own UI component for log messages, typically a “Log window” of some sort. This solution is how the original ImageJ addresses the logging problem on the GUI side: plugins are expected to call the static IJ.log and IJ.handleException methods to emit log messages and report exceptions to the user, respectively.

ImageJ2 runs using the ImageJ legacy user interface by default, meaning it inherits this Log window. But in addition, it also provides a separate Console window which logs all messages to stdout and stderr, so that no potentially valuable console output is hidden from the user. The Console window automatically becomes visible whenever any output to stderr occurs. Output to stderr is shown in red, to differentiate it from output to stdout.

Therefore, as long as logging messages end up on stdout or stderr somehow (which they do by default), all is well, and the user will see them. But if you configure your own logging implementation which suppresses the stderr behavior in favor of, e.g., logging to a file somewhere, then you are on your own!

How does logging work in Fiji?

The Fiji distribution of ImageJ is a more complicated beast, because it ships many plugins, each of which may do its own thing with respect to logging. Rather than herd cats, Fiji simply ships each plugin’s required logging framework so that it can function unimpeded.

However, ultimately, the Fiji distribution needs to ship one, and only one, SLF4J binding; otherwise, SLF4J-based logging does not function properly. As of this writing, Fiji ships the logback-classic binding, meaning that SLF4J-based logging can be configured in all the usual Logback ways. Fiji ships a default Logback configuration in the scijava-config library, which sends INFO level events to stdout, and more serious levels (WARN, ERROR and FATAL) to stderr. If you want to override the configuration, you will need to delete or modify that JAR file, replacing the logback.xml with one of your own design. The reason logback-classic was chosen is because the Bio-Formats project explicitly depends on it; rather than work around that, Fiji goes with the flow and converges on the same dependency.

Controlling the logging level

If you are using the standard SciJava LogService implementation, you can control the logging level via the scijava.log.level system property.

E.g., in Java code:

System.setProperty("scijava.log.level", "debug");

Or via the command line when running a specific class:

java -Dscijava.log.level=debug ... myorg.MyMainClass

Or via the command line when starting ImageJ2:

ImageJ-[linux64|macosx|win32.exe|win64.exe] -Dscijava.log.level=debug

You can even customize the logging behavior per package/class hierarchy. For example, to switch on debug level logging for classes in the org.scijava.plugin package and subpackages only:

System.setProperty("scijava.log.level:org.scijava.plugin", "debug");

If you prefer environment variables, setting the DEBUG environment variable will cause SciJava logging to default to DEBUG level instead of the usual WARN.

Using the LogService from your code

The LogService is accessible from a SciJava plugin in the same way as any other service: as a field annotated with @Parameter. See the Writing plugins guide for details.