Synopsis

This post explains how to use the ImageJ2/Fiji logging services (SciJava/LogService) in your Jython plugin with a working example.

TL;DR - view the example section for a quick start.

This work is licensed under CC BY-SA 4.0

Logging in ImageJ

Legacy ImageJ logging is performed using the IJ.log(String message) method. Logging using this results in a separate “Log” window that records all logged messages. All modern logging capabilities are missing (filters, sources, etc.)

Modern ImageJ2/Fiji logging is based on SciJava’s LogService. ImageJ2 ships with a logback-classic implementation. Upon ImageJ2 initializaton, a LogService is automatically initialized. To ask for the instance in a Java class, parameter annotation should be used:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Copied from: https://imagej.net/develop/plugins#the-context
@Plugin
public class MyPlugin {
 
  // This @Parameter notation is 'asking' the Context
  // for an instance of LogService.
  @Parameter
  private LogService logService;
 
  public void log(String message) {
    // Just use the LogService!
    // There is no need to construct it, since the Context
    // has already provided an appropriate instance.
    logService.info(message);
  }
}

Jython does not seem to support Java annotation OOB. To ask for LogService instance, use ImageJ2’s scripting parameters syntax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Must be run in a explicit ImageJ2 'script'
#		Will work:
#			man Jython programs under the /plugins directory of ImageJ2
#		Won't work:
#			Jython modules under the /jars/Lib directory of ImageJ2

# The following line provides the LogService instance as `sjlogservice`
#@ LogService sjlogservice
sjlogservice.info("INFO from root Logger")
sjlogservice.subLogger("sublog:a").info("INFO from sublog:a logger")

Example: modern logging in Fiji/ImageJ2

My ImageJ2 update site provides a minimal Jython module for logging. Install the y3628.sjlogging Jython submodule from my update site (path = jars/Lib/y3628/) before proceeding.

Say we have a plugin organized as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Fiji.app/
├── plugins/
│   └── my_plugin_main.py
└── jars/
    └── Libs/
        ├── my_jython_module/
        │   ├── fancy_stuff1.py
        │   └── fancy_stuff2.py
        └── y3628/
            └── sjlogging.py

In the main program my_plugin_main.py, initialize sjlogging once:

1
2
3
4
# Initialization - must be in the main program under plugins/
from y3628 import sjlogging
#@ LogService sjlogservice
sjlogging.init(sjlogservice)

Then, your can initialize an arbitrary number of Logger in both the main program under plugins/ and submodules under jars/Libs/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from y3628 import sjlogging as sjlog
# Instantiate SJLoggers - available in all package components
log_main = sjlog.SJLogger("main")
log_sub = sjlog.SJLogger("main:sub")
log2 = sjlog.SJLogger("secondary")
# Refer to sjloggings.py for usage details. A small example below:

# Logging in different ways for level 'INFO'
log_main.log(3, record)
log_main.log('INFO', record)
log_main.log('I', record)
log_main.info(record)

# Logging in different sources
log_sub.log(3, "info from sublogger of main")
log2.log(3, "info from logger2")

Logs will be forwarded to the Console window of ImageJ2. By default, unless you log messages at the ERROR/WARN levels, the Console window will not pop on its own. Also, logs from different sources are separated beautifully in the Console/Log dialog.

For a complete view of the sjlogging module used in a real plugin, refer to my plugin punctaTracker on Github. You are more than welcome to raise new issues about the sjlogging module there also.