loggingosgiapache-karafblueprint-osgiblueprint

How can I log initialization of objects created by Blueprint (Aries) during container initialization in Karaf?


After reading about this, I felt like I understood it and now I am left baffled. Here is what I expect and what I did:

I expect to log into Karaf, reload my bundle, and run log:tail and eventually see a log message like this:

13:28:47.265 INFO [Blueprint] You just created a class called: MyClass.

Technologies used: - OSGI Container implemented by Apache Karaf - Blueprint implemented by Aries

  1. My OSGI bundle imports the pax logger from Karaf

    org.slf4j.*; provider=paxlogging

to my understanding, this means that a reference to to the singleton logger of Karaf will be provided at runtime to my application, which only uses an API.

  1. My classes use the SLF4J interface, so the dependency slf4j-api:slf4j-api:1.7.26 exists in my project.

  2. A class exists

Class serves a model

public class MyClass {
  private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
  public MyClass() {
    LOGGER.info("You just created a class called: {}", this);
  }
  @Override
  public String toString() { return "MyClass" };
}

I just followed the specifications for an OSGI LoggerFactory:

Consumers of this API must not implement this type https://osgi.org/specification/osgi.cmpn/7.0.0/service.log.html#org.osgi.service.log.LoggerFactory

  1. Aries creates one:

Blueprint XML

<?xml version="1.0" encoding="UTF-8" ?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

<description>
    A sample Blueprint XML to demonstrate 
    the initialization procedure in the hopes of reproducing a problem.
</description>

<bean id="myclass1" class=com.stack.MyClass/>
</blueprint>

Related


Solution

  • Got this figured out:

    The SLF4J API compiled in the bundle is part of the story. The rest is provided in Karaf / Felix by org.ops4j.pax.logging.pax-logging-api. This thing does stuff in the background:

    1. Setups up its own singleton logger factories.
    2. Immediately enables SLF4J API support and logs a message.
    3. One of the singleton logger factories is what SLF4J API needs to create loggers, the Slf4jLoggerFactory. It, which holds a static reference to the PaxLoggingManager object (Slf4jLoggerFactory.setPaxLoggingManager(manager), where manager is new OSGIPaxLoggingManager(bundleContext)). The method getLogger within this singleton returns a new Slf4jLogger(name, paxLogger) object (where name is usually a class name and the paxLogger is either from a FallbackLogFactory.createFallbackLog(FrameworkUtil.getBundle(Logger.class), name) or m_paxLogging.getLogger(name, Slf4jLogger.SLF4J_FQCN). Slf4jLogger

    So, it is necessary to bind to this specific Slf4jLoggerFactory (implements ILoggerFactory) so that all of the classes in the bundle get the correct reference when they call getLogger(class). The problem with Aries Blueprint, it seems, is that it lazily binds the SLF4J API to the implementation provided by headers org.ops4j.pax.logging.pax-logging-api. So, I followed Christian Schneider's advice and created a top-level reference in Blueprint that forced Blueprint to wait for the Pax Manager to be ready:

     <reference id="logService" interface="org.osgi.service.log.LogService" availability="mandatory" activation="eager"/>
    

    Then the other top-level managers can depend on this by using depends-on:

    <bean id="MyRegistry" class="com.foo.MyRegistry" scope="singleton" factory-method="getSingletonInstance" depends-on="logService">
    

    Of course, I needed to add the following to my OSGI MANIFEST.MF

    Import-Package:
    org.slf4j;version="[1.7.0,2.0.0)",
    org.osgi.service.log,