djf
djf

Reputation: 6757

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...

Upvotes: 2

Views: 966

Answers (3)

Martin
Martin

Reputation: 172

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”));
  }
}

Upvotes: 1

Michael Lipp
Michael Lipp

Reputation: 400

  • By now (Mar '19) there is (at least) Apache Felix Log that fully implements OSGi Logging 1.4.

  • The easiest way to explicitly use an OSGi Logger that I know of is through this logging facade. Declaring a logger is as simple as with slf4j or log4j and it doesn't require a SCR (or similar).

Upvotes: 0

Christian Schneider
Christian Schneider

Reputation: 19606

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.

Upvotes: 2

Related Questions