I have created a small reproducer project on GitHub: slf4j-experiment
Basically all I need is an OSGi bundle where some code is using slf4j-api
.
Example:
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(service = SomeService.class)
public class SomeServiceImpl implements SomeService {
private static final Logger LOG = LoggerFactory.getLogger(SomeServiceImpl.class);
@Override
public String doIt(String first, String second) {
LOG.info("SomeServiceImpl: doIt(..)");
return operation(first, second);
}
static String operation(String first, String second) {
return first + second;
}
@Activate
public void activate() {
LOG.info("SomeServiceImpl: activate");
}
@Deactivate
public void deactivate() {
LOG.info("SomeServiceImpl: deactivate");
}
}
And in my build.gradle
I have:
dependencies {
compileOnly 'org.osgi:osgi.annotation:7.0.0'
compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0'
implementation 'org.slf4j:slf4j-api:2.0.11'
runtimeOnly 'org.slf4j:slf4j-simple:2.0.11'
// ...
}
This is working as expected with slf4j 1.7.36
Now with the version 2.0.11
of slf4j, because of usage of service provider, there are new constraints modeled in the OSGi metadata and now I am stuck at:
Resolution failed. Capabilities satisfying the following requirements could not be found:
[<<INITIAL>>]
⇒ osgi.identity: (osgi.identity=slf4j.simple)
⇒ [slf4j.simple version=2.0.11]
⇒ osgi.wiring.package: (&(osgi.wiring.package=org.slf4j)(version>=2.0.0)(!(version>=3.0.0)))
⇒ [slf4j.api version=2.0.11]
⇒ osgi.extender: (&(osgi.extender=osgi.serviceloader.processor)(version>=1.0.0)(!(version>=2.0.0)))
It seems that I need an additional bundle, maybe provided by SPI Fly, but I could not understand how…
Your example declares:
slf4j.api;version='[1.7.36,1.7.37)',\
slf4j.simple;version='[1.7.36,1.7.37)'
which is (I'm not a fan of gradle + OSGi + bnd) resolved to slf4j-api-1.7.36.jar from Maven central.
This is proper OSGi bundle with:
Import-Package: org.slf4j.impl;version=1.6.0
Export-Package: org.slf4j;version=1.7.36, org.slf4j.spi;version=1.7.36
, org.slf4j.helpers;version=1.7.36, org.slf4j.event;version=1.7.36
However slf4j-api-2.0.11.jar has:
Require-Capability: osgi.extender;filter:="(&(osgi.extender=osgi.service
loader.processor)(version>=1.0.0)(!(version>=2.0.0)))"
And indeed - it's satisfied by Aries SPI-Fly with:
Provide-Capability: osgi.extender;osgi.extender="osgi.serviceloader.re
gistrar";version:Version="1.0",osgi.extender;osgi.extender="osgi.serv
iceloader.processor";version:Version="1.0";uses:="org.apache.aries.sp
ifly"
SLF4J got these headers (requirements) in this commit.
SPI-Fly is OSGi's response to Java Service Loader, transforming your bundle's code on the fly so proper thread context class loader is set.
This is actually a proper requirement from SLF4J API 2, because they switched from static binding of logger implementation to service discovery of /META-INF/services/org.slf4j.spi.SLF4JServiceProvider
service.
However, OSGi is a tough beast. For logging I recommend Pax Logging project, which not only exports proper org.slf4j
packages, but it also allows you to dynamically configure the logging backend and have it configured with OSGi Configuration admin.