Logging in OSGi can be very tedious. OSGi provided a very robust framework for logging but it misses one point:
And this is where OSGi misses the point with its very sophisticated framework. There is a framework but there is no implementation of the last link in the chain: the output of the log.
Logging should be dead simple. But in OSGi it just isn't that simple. There is the LogService, the LogReader and the LogListener … and the whole time I just think “Hey, I just want to output some messages!”
Both popular OSGi frameworks (Eclipse Equinox and Apache Felix) have implementation for LogService and LogReader but the LogListener is not implemented for the end-user (the application developer).
So here is a trivial implementation of a LogListener , using System.out:
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.osgi.service.log.LogEntry; import org.osgi.service.log.LogListener; import org.osgi.service.log.LogReaderService; import org.osgi.service.log.LogService; public class ConsoleLogListener implements LogListener { private static final String LOG_LEVEL_INFO = "INFO "; private static final String LOG_LEVEL_WARN = "WARN "; private static final String LOG_LEVEL_ERROR = "ERROR"; private static final String LOG_LEVEL_DEBUG = "DEBUG"; private static final String[] LOG_LEVELS = { LOG_LEVEL_ERROR, LOG_LEVEL_WARN, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG }; private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int logLevel = LogService.LOG_INFO; public void bindLogReader(LogReaderService logReader) { logReader.addLogListener(this); } public void unbindLogReader(LogReaderService logReader) { logReader.removeLogListener(this); } @Override public void logged(LogEntry entry) { if (!isLoggable(entry)) { return; } String message = formatLogTime(entry.getTime()) + " " + formatLogLevel(entry.getLevel()) + " - " + entry.getBundle().getSymbolicName() + " - " + entry.getMessage(); if (entry.getServiceReference() != null) { message = message + " - [ " + entry.getServiceReference().getClass().getName() + " (" + entry.getServiceReference().getProperty("service.id") + ") ]"; } System.out.println(message); } private String formatLogTime(long time) { return dateFormat.format(new Date(time)); } private String formatLogLevel(int logLevel) { return LOG_LEVELS[--logLevel]; } private boolean isLoggable(LogEntry entry) { boolean loggable = false; if (logLevel <= entry.getLevel()) { loggable = true; } return loggable; } }
But that isn't doing the trick yet as it is nowhere registered in the logging framework. The easiest way to register it is to make a declarative component of it and bind the LogReader implementations to it, file OSGI-INF/service-log.xml.
<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="miworkplace.log.console"> <implementation class="miworkplace.log.ConsoleLogListener"/> <reference bind="bindLogReader" cardinality="0..n" interface="org.osgi.service.log.LogReaderService" name="LogReaderService" policy="dynamic" unbind="unbindLogReader"/> </scr:component>
Service-Component: OSGI-INF/service-log.xml