So I have this OSGi project built with equinox with this gradle structure:
> Task :projects
------------------------------------------------------------
Root project 'project'
------------------------------------------------------------
Root project 'project'
No sub-projects
Included builds
+--- Included build ':callable-processor'
+--- Included build ':osgi-sun-misc'
+--- Included build ':path-collector'
+--- Included build ':robot-api'
+--- Included build ':robot-equinox'
+--- Included build ':robot-signal'
+--- Included build ':robot-instance'
+--- Included build ':utils'
\--- Included build ':reactive'
Main bundle is robot-equinox
.
On the root (project
), I have the following build.gradle
configuration:
// project
ext.baseVersion = '2.0.0'
ext.buildTag = "SNAPSHOT"
group 'project'
version "${baseVersion}-${buildTag}"
project {
quality = ALFA
}
configurations {
bundleInstall
bundleStart
misc
}
dependencies {
bundleInstall 'ch.qos.logback:logback-classic:1.3.5'
bundleInstall 'ch.qos.logback:logback-core:1.3.5'
bundleInstall 'org.slf4j:slf4j-api:2.0.5'
bundleInstall 'org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:1.3.5'
bundleInstall 'prodist:callable-processor'
bundleInstall 'prodist:osgi-sun-misc'
bundleInstall 'prodist:path-collector'
bundleInstall 'prodist:reactive'
bundleInstall 'prodist:robot-api'
bundleStart 'ch.qos.logback:logback-classic:1.3.5'
bundleStart 'ch.qos.logback:logback-core:1.3.5'
bundleStart 'org.slf4j:slf4j-api:2.0.5'
bundleStart 'prodist:robot-equinox'
}
And under robot-equinox
I have the following configuration:
dependencies {
implementation group: 'prodist', name: 'callable-processor'
implementation group: 'prodist', name: 'path-collector'
implementation group: 'prodist', name: 'robot-api'
implementation group: 'prodist', name: 'reactive'
implementation group: 'prodist', name: 'robot-instance'
implementation group: 'prodist', name: 'utils'
compileOnly group: 'org.osgi', name: 'osgi.core', version: '6.0.0'
testImplementation group: 'org.hamcrest', name: 'hamcrest', version: '2.2'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.2.4'
testImplementation group: 'org.reactivestreams', name: 'reactive-streams-tck', version: '1.0.3'
testImplementation group: 'org.testng', name: 'testng', version: '7.1.0'
testCompileOnly group: 'org.osgi', name: 'osgi.core', version: '6.0.0'
}
I'll give a sample of my log implementation on the main class, which is present under 'project'.
Application.java
package project.robot.equinox;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public Application ()
{
MDC.put("product.name", ProductInfo.getName());
MDC.put("product.version", ProductInfo.getVersion());
}
public void activate (BundleContext bundleContext)
{
logger.atTrace().log(_S("activate: enter"));
}
...
}
But when I start the Application, I'm getting this message on the osgi console:
osgi> SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
My expectation was that by installing both - slf4j and logback, slf4j would recognize logback implementation and would work. How can I solve this problem?
Ok, so I found a solution and I'm going to post it here so it help someone else facing this same issue.
In order to help SLF4J find his provider, I create a new bundle osgi-log
- a "log bundle" with this configuration:
group 'project'
version '2.0.0'
dependencies {
api group: 'org.slf4j', name: 'slf4j-api', version: '2.0.5'
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.3.5'
implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.3.5'
implementation group: 'org.ow2.asm', name: 'asm', version: '5.2'
implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.2'
implementation group: 'org.ow2.asm', name: 'asm-util', version: '5.2'
implementation group: 'org.apache.aries.spifly', name: 'org.apache.aries.spifly.dynamic.bundle', version: '1.3.5'
}
And then, in each bundle who uses log, I added the following dependency:
dependencies {
(...)
implementation group: 'project', name: 'osgi-log'
}
And when I started my application, the message about provider was gone and log was being registered as expected.
I came to conclusion that Apache Aries spifly offers a ServiceLoader to SLF4J and helps him find provider, which in my case is logback.