Using Pax Logging API without backend
This page contains information about OSGi internals and how pax-logging-api
bundle alone allows bundles to use any supported logging API/facade.
What happens during OSGi runtime startup
As mentioned in Download page, Pax Logging is usually installed as part of OSGi runtime (for example, Apache Karaf server). Usually pax-logging-api
and specific pax-logging-*
backend of choice are installed as early bundles with low start level.
To summarize how it works, let’s describe the most fundamental scenario, where only pax-logging-api
bundle is available.
pax-logging-api
bundle starts early and doesn’t require anything beyond what OSGi core provides. The narrowed down list of imports and exports is this (with a little formatting):
Import-Package:
javax.xml.parsers,
org.osgi.framework; version = [1.0.0,2.0.0),
org.osgi.framework.wiring; version = [1.0.0,2.0.0),
org.osgi.service.event; version = [1.0.0,2.0.0); resolution := optional,
org.osgi.service.log; version = [1.3.0,2.0.0),
org.osgi.util.tracker; version = [1.0.0,2.0.0),
org.w3c.dom
Export-Package:
org.apache.avalon.framework.logger; version = 4.3
org.apache.commons.logging; version = 1.2.0
org.apache.commons.logging; version = 1.1.3
org.apache.commons.logging; version = 1.0.4
org.apache.commons.logging.impl; version = 1.2.0
org.apache.commons.logging.impl; version = 1.1.3
org.apache.commons.logging.impl; version = 1.0.4
org.apache.juli.logging; version = 9.0.19
org.apache.juli.logging; version = 8.5.40
org.apache.juli.logging; version = 7.0.94
org.apache.juli.logging; version = 6.0.18
org.apache.juli.logging; version = 5.5.28
org.apache.log4j; version = 1.2.17
org.apache.log4j.config; version = 1.2.17
org.apache.log4j.helpers; version = 1.2.17
org.apache.log4j.or; version = 1.2.17
org.apache.log4j.pattern; version = 1.2.17
org.apache.log4j.spi; version = 1.2.17
org.apache.log4j.xml; version = 1.2.17
org.apache.logging.log4j; version = 2.12.0
org.apache.logging.log4j; version = 2.9.1
org.apache.logging.log4j.message; version = 2.12.0
org.apache.logging.log4j.message; version = 2.9.1
org.apache.logging.log4j.simple; version = 2.12.0
org.apache.logging.log4j.simple; version = 2.9.1
org.apache.logging.log4j.spi; version = 2.12.0
org.apache.logging.log4j.spi; version = 2.9.1
org.apache.logging.log4j.status; version = 2.12.0
org.apache.logging.log4j.status; version = 2.9.1
org.apache.logging.log4j.util; version = 2.12.0
org.apache.logging.log4j.util; version = 2.9.1
org.jboss.logging; version = 3.4.0.Final
org.jboss.logging; version = 3.3.0.Final
org.knopflerfish.service.log; version = 1.2.0
org.knopflerfish.service.log; version = 1.1.0
org.ops4j.pax.logging; version = 1.11.0
org.ops4j.pax.logging.avalon; version = 1.11.0
org.ops4j.pax.logging.slf4j; version = 1.11.0
org.ops4j.pax.logging.spi; version = 1.11.0
org.osgi.service.log; version = 1.3
org.slf4j; version = 1.7.26
org.slf4j; version = 1.6.6
org.slf4j; version = 1.5.11
org.slf4j; version = 1.4.3
org.slf4j.event; version = 1.7.26
org.slf4j.helpers; version = 1.7.26
org.slf4j.helpers; version = 1.6.6
org.slf4j.helpers; version = 1.5.11
org.slf4j.helpers; version = 1.4.3
org.slf4j.spi; version = 1.7.26
org.slf4j.spi; version = 1.6.6
org.slf4j.spi; version = 1.5.11
org.slf4j.spi; version = 1.4.3
As visible in Export-Package
all public packages from all supported logging APIs/facades are exported - sometimes with different versions. For some libraries it was easy to specify list of packages constituting public API, in some cases it wasn’t trivial (Log4J1…).
When bundle is in RESOLVED state, it can already provide the exported packages. So even before pax-logging-api
bundle transitions to ACTIVE state, other bundles may already get resolved and wire to the exported API packages. That’s enough for all the bundles to be able to invoke successfully code fragments like this:
org.slf4j.LoggerFactory.getLogger(name).info("INFO using SLF4J");
What actually happens?
Simple call to org.slf4j.Logger#info()
is not trivial underneath. Especially if org.slf4j
package is exported by Pax Logging API and if pax-logging-api
is not even started (ACTIVE). What’s going on underneath?
Internally, all logging APIs/facades are supported by singleton instance of internal instance of
org.ops4j.pax.logging.OSGIPaxLoggingManager
class.This class is used by all facades to obtain an instance of
org.ops4j.pax.logging.PaxLogger
internal logger. All facades provide specific loggers (e.g.,org.ops4j.pax.logging.slf4j.Slf4jLogger
implementingorg.slf4j.spi.LocationAwareLogger
) that delegate to this internalPaxLogger
.This internal logger is backed by
org.ops4j.pax.logging.internal.TrackingLogger
which is dynamically configured with currently available OSGi service oforg.ops4j.pax.logging.PaxLoggingService
interface.org.ops4j.pax.logging.PaxLoggingService
interface is provided by an OSGi service registered by one ofpax-logging-service
,pax-logging-logback
orpax-logging-log4j2
backends and simply delegate to framework specific loggers.However, if no Pax Logging backend is available
pax-logging-api
itself providesorg.ops4j.pax.logging.spi.support.FallbackLogFactory
class that’s used to obtain instances oforg.ops4j.pax.logging.PaxLogger
. This time, logger returned byFallbackLogFactory
is not tracking and dynamically awaiting actual logging implementation, this fallback logger actually does what stems from its name - provides fallback logging mechanism.
As you may have guessed, fallback logger is used in interregnum times, where no actual logging backend is available. There are two valid behaviors configurable for this fallback logger:
write logging events to standard output
write logging events to configured file
Configuration of fallback logger
Because fallback logger is used so early, we can’t even use Configuration Admin to configure its behavior. The only available configuration is through:
system properties (in Karaf, configured via
etc/system.properties
) -java.lang.System#getProperty()
bundle context properties (in Karaf, configured via
etc/config.properties
) -org.osgi.framework.BundleContext#getProperty()
System properties have higher priority than context properties.
These are the system/context properties that affect the behavior of fallback logger:
Property name | Description | Default value |
---|---|---|
| Specifies threshold for fallback logger used behind all facades handled by |
|
| Selects file-based fallback logger. The value should be writable filename. Turning on this logger will enable synchronization and register singleton stream used by all instances of |
|
Integration tests available in Pax Logging show different usages of fallback logger. The important thing is - before pax-logging-api
bundle becomes ACTIVE, it’ll only provide non-file based default logger. The reason is synchronization. The important aspect of fallback logging is that all actual logging frameworks (like Log4J2) have notion of status logger - some logger that used by the logging framework itself to provide details about internal behavior and possible problems! So even if real Log4J2 is used by user’s code, Log4J2 itself still needs a mechanism to log internal details about its operation.
So, file-based fallback logger needs synchronization and this happens via some OSGi services registered by pax-logging-api
bundle that’s already ACTIVE.
Fallback logger uses standard, hardcoded layout that’s writing information like this:
bundle-symbolic-name [category name] level : message
(optional stack trace)
For example (from org.ops4j.pax.logging.it.karaf.CleanIntegrationTest):