Search code examples
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...

  • Is there a OSGI LogService implementation out there that does implement the LoggerFactory?
  • Is it an advantage to log through an 'implicit' channel (like importing slf4j) instead of using 'explicit' OSGI interfaces?

Solution

  • It should be:

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