Package de.mnl.osgi.jul2osgi

A bundle that forwards java.util.logging (JUL) records to the OSGi logging service.

GitHub issues Maven Central


This bundle (the main bundle) works together with the bundle de.mnl.osgi.jul2osgi.lib (the library bundle), which provides a replacement for the default LogManager. Replacing the LogManager has the advantage that all interactions with JUL can be intercepted.

The library bundle must be put on the JVM bootstrap classpath or the framework classpath by the launcher because the Java runtime accesses the LogManager early during application boot. The replacement LogManager is made known to JUL by invoking the JVM with the corresponding system property: "java -Djava.util.logging.manager=de.mnl.osgi.jul2osgi.lib.LogManager ...". Usually, such system properties can be configured in the OSGi launcher. Until the main bundle is started, the LogManager records all JUL LogRecord in a buffer. The size of the buffer can be configured with the system property de.mnl.osgi.jul2osgi.bufferSize, which defaults to 100.

The main bundle is deployed as any other OSGi bundle

bnd launcher before 4.3.0

The library bundle can be put on the framework classpath with the -runpath instruction (bnd), the property is set with the -runvm: ... instruction.

Example configuration:

 -runpath: de.mnl.osgi.jul2osgi.lib;version='latest'
 -runvm: -Djava.util.logging.manager=de.mnl.osgi.jul2osgi.lib.LogManager

bnd launcher starting with 4.3.0

With 4.3.0 the handling of the -runpath instruction has changed. The library must therefore be added to the bootstrap classpath. Because this does not add the exported packages to the system capabilities, this must be done explicitly.

Example configuration:

 -runvm: -Xbootclasspath/a:${repo;de.mnl.osgi.jul2osgi.lib;latest}, \
 -runsystempackages: \
   de.mnl.osgi.jul2osgi.lib; version=1.4.2

Forwarding functionality

When the main bundle is started, it first requests an OSGI log service from the framework. When this service becomes available, the forwarder in the main bundle registers a callback with the LogManager. The LogManager uses the callback to send all recorded and future JUL LogRecords to the main bundle. The forwarder converts the information from the LogRecords to calls of one of the methods of an OSGi Logger.

JUL LogRecord properties are mapped in the following way:

  • logger name: used as logger name when requesting the OSGi Logger.
  • message: passed as message parameter (see below).
  • level: used to choose the method to call. FINE is mapped to a call to debug, anything below is mapped to a call to trace.
  • thrown: passed to the OSGi logger, becomes the exception property of the OSGi LogRecord.
The forwarder also attempts to determine the bundle within which the JUL Logger was created and use it as origin of the event when forwarding to OSGI logging. Only if determining the bundle fails will the log event appear to have been created by bundle de.mnl.osgi.jul2osgi.

Special handling is applied by the forwarder if recorded log entries are forwarded to the OSGi Logger. The implementation of the logger automatically derives a "thread info" property for the newly created OSGI LogRecord from the calling thread. There is no way to override this automatically created value, which is obviously wrong when a thread forwards log events that have been recorded while executing other threads. Therefore the current thread's name is added to the recorded information. When flushing the recorded log events later, the name of the flushing thread is temporarily set to the recorded name with "[recorded]" appended.

When using JUL, the message passed to the JUL Logger isn't necessarily what you see in your log. It is first used to lookup a mapping in the ResourceBundle associated with the LogRecord. Then, it is passed to a formatter that may insert representations of parameters associated with the LogRecord. This processing is supposed to take place during the final processing, i.e. in a JUL Handler.

The OSGi log service does not provide such sophisticated post-processing of log entries. The message text and the parameters from the JUL LogRecord are therefore processed before forwarding the message to the OSGi Logger. In this respect, the forwarder behaves like a JUL Handler.

Before passing the post-processed message to the OSGi Logger the forwarder applies another formatting operation. It invokes MessageFormat.format(String, Object...) with a format string, the post-processed message and the remaining information from the JUL LogRecord. This allows the remaining information to be added to the message sent to the OSGi log service.

The format method is invoked as:

format(logPattern, message, millis, sequenceNumber, sourceClassName, sourceMethodName, threadID)

In order to e.g. add the source class name and method to the log message the pattern "{3}.{4}: {0}" could be used.


Calls to the JUL Logger are filtered before further processing according to the result from (JUL) Logger.isLoggable(java.util.logging.Level) and by any configured JUL filter. Calls that pass this barrier are forwarded to OSGi logging.

OSGi logging filters the forwarded events according to the level configured for the originating bundle and OSGi logger before accepting the event.

If you want log events to be delivered for levels lower than the default levels, you must therefore lower the levels in both configurations. Be aware that the default levels for JUL and OSGi logging differ. JUL uses the default level INFO while OSGi logging uses the default level WARNING.

In order to avoid unnecessary processing of eventually discarded log events, filtering should preferably be configured using JUL. Anything that passes this first barrier can then be accepted by OSGi logging without further restrictions. Following this approach, the default level for bundles using JUL should thus best be set to LogLevel.TRACE (see bundle property below).

Bundle properties

The following bundle properties are used to configure the described behavior.
Bundle parameters
de.mnl.osgi.jul2osgi.logPattern The final formating pattern {0}
de.mnl.osgi.jul2osgi.adaptOSGiLevel If set to true, the OSGi log level for the originating bundle of a JUL log event will automatically ne set to LogLevel.TRACE. This results in the expected behavior that any log message that has been enabled in JUL is visible in the OSGi log. true