osgi

OSGI LoggerFactory


In my OSGI endeavours I'm struggeling with another seemling simple problem with logging.

We've included logging to our bundle and it works. We're actually using pax-logging service to do the heavy lifting for us.

import org.ops4j.pax.logging.PaxLoggingService;
import org.osgi.service.component.*;

@Component( immediate=true )
public class ComponentImpl implements TimeService {

    @Reference
    private PaxLoggingService logs;

    @Activate
    public void activate(ComponentContext ctx)
    {
        // deprecated legacy interface
        logs.log(PaxLoggingService.LOG_INFO, "Activate called at " + getTime());
        logs.log(PaxLoggingService.LOG_INFO, "Activated component " + ctx.getProperties().get("component.id"));
    }
}

But two things are bothering us. Firstly, the using the public void log(int level, String message) method directly has been deprecated since OSGI v1.4. Secondly, we'd much rather log through the OSGI LogService.

However this doesn't seem to work just as readily. Our first attempt using the updated logging interface where you first construct a logger instance and then then log through that results in a Java AbstractMethodError:

@Component( immediate=true )
public class ComponentImpl implements TimeService {

    @Reference
    private PaxLoggingService logs;

    @Activate
    public void activate(ComponentContext ctx)
    {
        // fancy, new logging interface - throws exception
        Logger logger = logs.getLogger(ComponentImpl.class);
        logger.log(PaxLoggingService.LOG_INFO, "Activate called at " + getTime());
    }
}

Runtime exception (this also happened when we tried Apache Felix's LoggerService implementation)

java.lang.AbstractMethodError: org.ops4j.pax.logging.service.internal.PaxLoggingServiceImpl$1ManagedPaxLoggingService.getLogger(Ljava/lang/Class;)Lorg/osgi/service/log/Logger;
        at com.foobar.baz.osgi.testservice.ComponentImpl.activate(ComponentImpl.java:31)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        ...
        at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
        at aQute.launcher.Launcher.startBundles(Launcher.java:517)
        at aQute.launcher.Launcher.activate(Launcher.java:423)
        at aQute.launcher.Launcher.run(Launcher.java:301)
        at aQute.launcher.Launcher.main(Launcher.java:147)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at aQute.launcher.pre.EmbeddedLauncher.main(EmbeddedLauncher.java:47)

Ideally you wouldn't want to initialize the logger instance in the activate() anyway, but have it setup by the framework. The OSGI spec shows an example of this in section 112.3.12

@Component
public class MyComponent {
  @Reference(service=LoggerFactory.class)
  private Logger logger;
  @Activate
  void activate(ComponentContext context) {
    logger.trace(“activating component id {}”,
      context.getProperties().get(“component.id”));
  }
}

Unfortunately, the example doesn't work either. The reference doesn't get resolved and consequently the bundle never runs... I've been searching the web, but haven't found anything pertinent. It seems most people don't use the OSGI service interface for logging and just use slf4j (or another façade) instead; Pax will capture the log entries either way. So technically it makes no difference.

I think our problem is that nobody (neither PAX nor Felix) has implemented the OSGI LoggerFactory interface...


Solution

  • Currently the best practice for logging in OSGi is to use slf4j as front end.

    Logger log = LoggerFactory.getLogger(this.getClass());
    

    Simply use this in your classes. Pax-Logging provides the backend for it and it would also work with a logback backend.

    OSGi R7 provides some improved log service integration but I think this is not yet widely available in platforms.

    The advantage of using an @Reference for logging is that it eliminates timing issues at startup when maybe your logging backend is not yet available.

    The advantage of the slf4j integration like above is that it even works for hybrid jars that also need to work outside OSGi.