Developers who are used to a logging tool, such as log4j, are compelled to use the same logging setup in an OSGi framework. After overcoming the initial first obstacles relating to codebase separation and resource visibility, the developers succeed in making it work. The result would typically look like the following diagram:
Such structures would work. However, they usually require a complex configuration to be set up and maintained. A better option is the use of a common logging service.
The OSGi Compendium specifications define a set of Log Service interfaces that are intended to provide a common logging service for an OSGi framework.
The Log Service applies separation of concerns by splitting functionality into the following two services:
Let's take a closer look at those.
The Log Service interface exposes a simple, but expandable, logging API for use by the bundles that need to send log events. Those bundles would all depend on a single logging interface.
The Log Service interface method declarations are variations of log():
log(int level, String message)
For logging a message at a log level.
The log levels are declared as constants in the same interface:
To also pass an exception with the log entry, the signature with the Throwable
parameter is used:
log(int level, String message, Throwable exception)
The same methods are also provided with a ServiceReference
as the first parameter:
log(ServiceReference sr, int level, String message) log(ServiceReference sr, int level, String message, Throwable exception)
In this case, the log message is registered as relating to the bundle with the provided service reference (instead of relating to the bundle invoking the log()
method).
The Log Service is used just like any other service on an OSGi framework.
To get access to a Log Service instance using the service locator, the look-up is done with the class name:
LogService log = null; ServiceReference ref = context.getServiceReference( LogService.class.getName()); if (ref != null) { log = (LogService) context.getService(ref); }
Using iPOJO, the LogService
is declared as a field of the service:
LogService log;
Then the field is declared for injection in the service component declaration in the iPOJO configuration:
<requires field="log" />
We will go through this again in a bit, when adding logging to our services.
What was just mentioned is all that's required when developing the bundle. At runtime, a Log Service implementation is needed.
In this section, we'll look at the service provider side of the Log Service in OSGi. It is not strictly necessary to know how it works. However, it's interesting to go through it for completeness.
According to the OSGi compendium specifications, the Log Service provider is to abide to the following setup:
The Log Service implementation packs received logs as Log Events and posts them with the Log Server Reader implementation.
The Log Service Reader exposes a means to access held log events, as well as the ability to register Log Listeners.
Registered Log Listener implementations receive log events and process them as they see fit. Some listeners may write the log entries to a file, others may forward them to an external logging component.