[slf4j-dev] SL4FJ light: ad-hoc bindings for friendlier OSGi/IoC integration

Simon Chemouil schemouil+slf4j at gmail.com
Tue Oct 16 20:05:51 CEST 2012


Hi,

We had a discussion earlier today with Martin Ellis and Ceki Gülcü on
IRC. Martin challenged my proposal to make sure that we guarantee
backwards compatibility, make it clear what we are exactly trying to
solve and find the possible alternatives. The full log is available
for interested parties [1].

The motivation:
* make static bindings optional for developers using frameworks that
already do binding (OSGi, Guice, Spring, etc) or want to roll their
own solution.
* make it easier to use OSGi features like having several
implementations available concurrently
* in general, make slf4j more modular.

The main problem:
We would like to separate the static binding logic from the general
API: however the org.slf4j package contains both the central
interfaces ILoggerFactory, Logger, IMarkerFactory and Marker and the
static binding logic. To keep backwards compatibility for both
"vanilla" Java and OSGi environments, we cannot easily split that
package into two different JARs.

The alternatives discussed:
(1) Do nothing. :-)
(2) We move static-binding related classes in the org.slf4j package
(LoggerFactory, MarkerFactory) to another package (e.g:
org.slf4j.factories). In order to provide backwards-compatibility, we
keep the factory classes in the org.slf4j that delegate to their moved
counterpart, but we deprecate them.
(3) We create a new package org.slf4j.api in which we put the central
interfaces ILoggerFactory, IMarkerFactory, Logger and Marker. In
org.slf4j the original interfaces are deprecated, emptied (except for
constants that delegate to the new interfaces) and extend the moved
interfaces.
(4) We make LoggerFactory/MarkerFactory return proxies to the actual
implementations, so that we can support implementation swapping. This
requires providing new SPI to define proxy factories. I won't
elaborate on this alternative because it adds complexity (it could
also be done as a complement of alternatives (2) or (3)).

(2) vs. (3):
Alternatives (2) and (3) differ on what gets deprecated, and on how
soon people can get the benefit of the lighter API without static
binding. In the call: Logger logger = LoggerFactory.get(..):
* with (2), LoggerFactory only gets deprecated. It can be solved by
simply changing the import from org.slf4j.LoggerFactory to
org.slf4j.factories.LoggerFactory
* with (3), Logger only gets deprecated. It can be solved by changing
the import from org.slf4j.Logger to org.slf4j.api.Logger.
In both case, we could provide support with the migrator tool to make
the proper import change, and in any case there will be no removal of
the classes without a lot of time, due notice, a major version change
and a FAQ entry ;). Even then, for non-OSGi users, a very simple
compatibility JAR can be dropped to support this ad vitam eternam.


Alternative (3) puts a bit more effort on implementation providers,
since they have to update the interfaces they implement. Still they
will have plenty of time to do so since the interfaces they already
implement will extend the new ones.


Packaging:
To keep full backwards compatibility while allowing interested parties
to take only what they need, the plan would be to keep the current
slf4j-api with the same contents, but provide two new lighter JARS:
slf4j-light-api and slf4j-static-bindings. (slf4j-api =
slf4j-light-api + slf4j-static-bindings)

Solution (2) (I hope you used fixed-width fonts for mails ;)).
 ---------------------             -----------------------
| slf4j-light-api     |           | slf4j-static-bindings |
|                     |           |                       |
| [org.slf4j]         |           | [org.slf4j.factories] |
| [org.slf4j.helpers] |           | [org.slf4j.spi]       |
|_____________________|           |_______________________|

Solution (3)
 ---------------------             -----------------------
| slf4j-light-api     |           | slf4j-static-bindings |
|                     |           |                       |
| [org.slf4j.api]     |           | [org.slf4j]           |
| [org.slf4j.helpers] |           | [org.slf4j.spi]       |
|_____________________|           |_______________________|


About implementation bindings:
Currently, implementations are registered by providing a class
org.slf4j.impl.Static{Logger,Marker}Binder. This is problematic in an
OSGi environment, when several implementation bundles are installed,
because multiple bundles export the same package/version with
different contents. The wiring of the package used by clients likely
depends on installation order of implementation bundles.

While this is a non mandatory extra-step for now, ideally the
implementation bundles should let users choose the binding system
used. It is much simpler to solve on this side, for instance by
keeping the org.slf4j.impl package but not exporting it in the OSGi
manifest, and providing the package in another bundle that only
exports it as a commodity for OSGi users wishing to use static
binding. Since this is entirely transparent, there could be a
Bundle-Activator entry in the implementation manifest to register the
implementation factories as OSGi services, and the same implementation
bundle could be dropped without change in both OSGi and non-OSGi
environments. We could also provide other bindings (e.g for Guice,
Spring, etc).


Summary:
We can remain backwards compatible until we feel it's time and do a
major version change (i.e, in years). For the sake of good practices,
we would mark interfaces @Deprecated but it's just a matter of
changing the import (can be done by the provided migrator tool). We
keep publishing JARs entirely compatible, but OSGi/Guice/Springs users
who want to can gain flexibility by dropping static binding
altogether. Those who want to use static bindings can as long as they
want.

So, this was supposed to be a short mail :). I hope it's clear, at least ;).

Cheers,

--
Simon


[1] https://gist.github.com/0811d39d8264dcf881dc


More information about the slf4j-dev mailing list