Skip to content

Instantly share code, notes, and snippets.

@magnet
Forked from martiell/gist:f0521848f65f8d714c40
Created October 16, 2012 15:10
Show Gist options
  • Save magnet/0811d39d8264dcf881dc to your computer and use it in GitHub Desktop.
Save magnet/0811d39d8264dcf881dc to your computer and use it in GitHub Desktop.
[13:45] <mart> so my question is really about what the aim of the proposal is. I'm wondering if you have a specific example of something that can't be implemented with the current design?
[13:46] <magnet_> no there is nothing I cannot do today in terms of functionality
[13:46] <magnet_> but there are things I can't prevent SLF4J to do
[13:46] <mart> I see.
[13:47] <mart> Do you know when/why slf4j stopped using fragment bundles for the impl?
[13:47] <magnet_> I know it did a few months ago looking at the archives
[13:48] <magnet_> it's all about making sure that there is a StaticLoggerBinding *before* any client has a chance to call LoggerFactory.getLogger() the first time
[13:48] <magnet_> the first call will trigger the loading of the static binding, so the class has to be available at that time
[13:48] <mart> and the impl bundle may not be resolved at that time?
[13:49] <magnet_> they cache the static binding to avoid doing it over and over
[13:49] <magnet_> if it's not here the first time, it will default to a no-op logger
[13:49] <magnet_> so the impl bundle must be resolved before the first call
[13:50] <mart> Suppose the impl bundle is available at container startup, can that happen?
[13:50] <magnet_> it can be installed before the API bundle, but they need each other to be "resolved"
[13:50] <mart> Just asking to check my understanding, here.
[13:51] <magnet_> the API bundle has Import-Package on org.slf4j.impl (e.g in LogBack)
[13:51] <magnet_> and the IMPL has Import-Package on org.slf4j (to implement the ifaces)
[13:51] <magnet_> it's a cyclic bundle dependency through package imports, and it's an anti-pattern
[13:51] <magnet_> that's why I want to make it optional
[13:51] <mart> so with that fragment bundle solution, both have to resolve at the same time, which (it seems to me) would mean that the api can't initialise in a state without an impl.
[13:52] <magnet_> you can't force the fragment to be present
[13:52] <magnet_> there is really no reason to make it a fragment
[13:53] <mart> ok, I understand it's not osgi-ish at all. But I'm trying to understand if/how it fails.
[13:54] <mart> presumably, the api bundle won't resolve without a fragment, and then the bundle that uses api won't resolve either?
[13:54] <magnet_> mhh just so I'm sure, what do you put in that fragment? The static binding system? The impl, :)
[13:55] <mart> yep, I think that's how it was.
[13:56] <magnet_> well there are several downsides with fragments: there's only one attempt at resolution (and no support for later install) and there can never be more than one fragment
[13:56] <magnet_> (iirc this was recently cleared in R5)
[13:56] <magnet_> s/cleared/made clear/
[13:57] <magnet_> my problem is not so much how they package their bundles, but the fact that Slf4j has logic it shouldn't (as an API)
[13:57] <mart> ok, so if you wanted to change the impl, you'd have to re-resolve the api, and in turn, everything that used the api.
[13:57] <magnet_> yes you'd have to do a refresh
[13:58] <magnet_> in OSGi, ideally I'd like to be able to use multiple versions of the API concurrently, and for each of them multiple implementations
[13:58] <mart> isn't that possible now?
[13:58] <magnet_> (multiple implementations it's not that useful for logging, but it should be possible because I don't want to make assumptions on how ppl will use it)
[13:59] <magnet_> well the static binding limits things a bit but they can be worked around if you totally discard the static binding system and use services
[14:00] <magnet_> you still end up with a useless dependency and stateful singletons
[14:00] <mart> what's the useless dependency there?
[14:00] <magnet_> the Import-Package on org.slf4j.impl
[14:00] <magnet_> if I use services and discard static binding, that dep. is totally useless
[14:01] <mart> ok.
[14:01] <magnet_> my proposal is simply to take that custom binding system and put it in the JAR because it taints my runtime.
[14:02] <magnet_> 1) I don't want devs in my team to start using LoggerFactory.getLogger() by mistake
[14:02] <magnet_> 2) I already have a more powerful, dynamic binding system (the service registry + a component framework)
[14:02] <magnet_> 3) and I forgot my third point ;)
[14:03] <magnet_> most of all, it's almost nothing to cut it and let "legacy" users do it like they always did.
[14:03] <mart> my thoughts are that static loggers are so pervasive, it seems like a big change to remove them from the standard api.
[14:03] <magnet_> ah, the 3) point is: "I don't want to fork slf4j"
[14:04] <mart> I know that even if I start injecting loggers, I can be fairly sure that I have some dependency that assumes static loggers.
[14:04] <magnet_> exactly
[14:04] <magnet_> that's why I want to make a compatibility bundle
[14:04] <mart> yep.
[14:04] <mart> I understood that part.
[14:05] <magnet_> but on the other hand, if we don't do anything because some code depends on it, all projects end up stagnating
[14:06] <mart> a concern I have is that there are questions all over the internet where people fail to understand that right now, they need two jars for slf4j. I worry a third would exacerbate that.
[14:06] <magnet_> In my project, I'm trying to enforce, through that, the fact that only a component should be allowed to log.
[14:06] <magnet_> the idea is to still provide a all-in-one jar like many projects do
[14:06] <magnet_> OSGi folks will know to depend on slf4j-api-light
[14:07] <mart> so the current slf4j-api would retain LoggerFactory?
[14:07] <magnet_> I think it's better to aim at a backwards compatible API jar that is all-in-one, and make new "lighter" bundles
[14:07] <magnet_> yes, probably, though I'd like to deprecate it and move it to another package
[14:08] <magnet_> (to have a route for backwards compat. without split packages)
[14:10] <mart> One thing I don't like is that it forces changes on people who aren't using OSGi (which is most people).
[14:10] <magnet_> I hoped to start a discussion (like this one) with my proposal
[14:12] <mart> Well, I hope the things you'll see me struggling with will help you with convincing people.
[14:12] <magnet_> If you use that all in one bundle, your only change would be a deprecation warning, and just changing your import line. I agree it's not that benign...
[14:13] <magnet_> I hope too :). I'm not sure anything will happen at all, but you never know. Also, if it can make other library designers avoid the same mistakes..;)
[14:13] <magnet_> It's too bad the Logger/MarkerFactory classes are in org.slf4j
[14:13] <magnet_> if they were in another package the change would be entirely transparent
[14:14] <magnet_> oh, but that's a nice idea
[14:14] <magnet_> instead of moving them, we could move the API instead
[14:14] <mart> There are other ways to enforce that a client doesn't use the LoggerFactory, for example, a checkstyle or findbugs config. I wonder whether the downsides of such an approach would outweigh changing the api.
[14:14] <mart> "move the api"?
[14:15] <magnet_> we could move ILoggerFactory / IMarkerFactory to org.slf4j.api
[14:15] <magnet_> make the existing versions implement those
[14:16] <magnet_> the real problem that makes the change not backwards compatible is the split package problem.
[14:16] <mart> hmm. trying to imagine.
[14:16] <magnet_> that's another way to solve it, but it's not perfect either.
[14:17] <mart> So the split-packages trick could be documented on the slf4j website.
[14:17] <mart> I agree it's not ideal, but it's an interesting alternative.
[14:18] <magnet_> yes, and it's nothing for the next version of LogBack or other bridges to implement this API instead
[14:18] <magnet_> (as in, clients would not notice)
[14:18] <mart> By that I mean that no solution (short of a time machine) is ideal, so we're discussing least bad options.
[14:19] <magnet_> by the time any solution is accepted, there may be a time machine ;)
[14:19] <mart> :D
[14:19] <magnet_> I like that solution better too
[14:19] <mart> I've seen Bill and Ted's Excellent Adventure. I'd totally take Beethoven to the mall.
[14:20] <magnet_> mhh, never watched it
[14:20] <mart> It was excellent, but I expect it's dated terribly. ;)
[14:20] <magnet_> but if they go meet Beethoven I will :)
[14:20] <mart> So, anyway, ILoggerFactory and IMarkerFactory move to org.slf4j.api ...
[14:21] <mart> what happens to LoggerFactory, in this case?
[14:21] <magnet_> yes, and implementations & bridges (most of them being controlled by ceki) update to those APIs
[14:21] <magnet_> well LoggerFactory doesn't move
[14:21] <mart> ok.
[14:21] <magnet_> it's just that we provide other packaging for DI users
[14:22] <magnet_> we keep the same slf4j-all-in-one JAR
[14:22] <mart> Ah, so the osgi bridge would just depend on org.slf4j.api?
[14:22] <magnet_> and we also provide a packaging without org.slf4j
[14:22] <magnet_> yes
[14:22] <magnet_> and the light API bundle woudln't depend on org.slf4j.impl anymore
[14:23] <magnet_> ideally we'd deprecate Logger, ILoggerFactory and IMarkerFactory but we're not forced to.
[14:23] <mart> I see, so Logger would move too?
[14:23] <mart> to org.slf4j.api?
[14:23] <magnet_> they will become empty ifaces implementing their counterpart in org.slf4j.api
[14:23] <magnet_> yes it has to
[14:24] <mart> ok, I'm following now.
[14:24] <magnet_> I understand it's not cool to have a deprecated type in hundreds of projects
[14:24] <magnet_> (even when the solution is just changing an import)
[14:25] <mart> you'd be amazed at how many people don't know sed. :)
[14:25] <magnet_> the question is, would you rather have Logger or LoggerFactory deprecated ;
[14:25] <magnet_> there's also a migrator project in slf4j that does the update from log4j and JCL
[14:25] <mart> gah. all that code using static loggers is using both.
[14:25] <magnet_> the migrator could be updated for that change
[14:26] <magnet_> there could also be SLF4J version 2.0.0 and people happy with the 1.x stream would stay there until ready
[14:26] <mart> problem then is that people need to figure out what's compatible with what.
[14:27] <magnet_> yes, i know. people don't version their dependencies neither at build or runtime, then software vendors are stuck
[14:27] <mart> I guess if you can always just drop in the compat jar, then it's not *so* bad.
[14:27] <magnet_> yes, and that jar can stay around forever
[14:28] <mart> so, would your project end up using three bundles: the (reduced) api; an osgi bridge and a backend?
[14:29] <magnet_> yes, for my setup i'd have: the light API, a bridge taking from the OSGi LogService to SLF4j, Logback as an Impl, and a smart iPojo injector for my components
[14:29] <magnet_> (smart injector because loggers would be removed if the iloggerfactory service is unregistered)
[14:30] <mart> let's list properties that may or may not hold of any solution:
[14:31] <mart> * it may (not) require a refresh of client bundles when the backend is updated.
[14:32] <mart> * it may (not) allow enforcement of "no LoggerFactory.getLogger calls" by setting dependencies/imports
[14:33] <mart> * it may (not) require deprecating classes/interfaces used by non-OSGi apps/libs
[14:34] <mart> * it may (not) allow the impl package to be a non-exported package.
[14:35] <mart> am I missing anything that we can use to evaluate a solution?
[14:35] <magnet_> mhh
[14:37] <magnet_> looks like you captured it
[14:38] <mart> something we haven't discussed is whether the LoggerFactory could be left as is, but return a proxy Logger, that uses the currently installed backend.
[14:39] <magnet_> but how would that proxy Logger know the backend has changed?
[14:39] <mart> BundleTracker? I'm thinking something that'd only be used in an OSGi environment.
[14:39] <magnet_> that would imply creating new SPI hooks in a non-OSGi environments
[14:39] <magnet_> ha, but then would you have OSGi specific code in LoggerFactoryN
[14:39] <magnet_> ?
[14:40] <mart> I was more thinking of an impl that tracked other impl bundles.
[14:41] <magnet_> and expose API in LoggerFactory to update the Logger proxy?
[14:41] <mart> * it may (not) require OSGi users to use split packages
[14:41] <mart> (missed one)
[14:42] <mart> no, no.
[14:42] <mart> The client just gets back an instance of the Logger interface, right?
[14:42] <magnet_> yep
[14:43] <mart> Suppose it's a proxy logger that updates when the backend is updated.
[14:43] <magnet_> yes but that means there is a special OSGiProxyLogger class somewhere that's using a BundleTracker or something
[14:44] <mart> let's say in a bundle called slf4j-proxy that contains a static binding.
[14:44] <magnet_> but LoggerFactory is already in the API, how would it be "overriden"?
[14:45] <magnet_> or would there be a new SPI class to provide ProxyFactories?
[14:45] <mart> suppose LoggerFactory doesn't change, and we just implement a static binding that returns proxy loggers
[14:45] <magnet_> hi ceki :)
[14:45] <ceki> hi
[14:45] <mart> heh, hi.
[14:46] <magnet_> mart, so LoggerFactory would just try to load a class by name? like it does with StaticLoggerBinding?
[14:46] <mart> yup.
[14:46] <magnet_> isn't that repeating the same mistake? ;)
[14:46] <ceki> is there a transcipt of your discussion?
[14:46] <magnet_> ceki, Arbalest
[14:47] <magnet_> it's the Eclipse logger, so I guess its logs are available somewhere
[14:47] <mart> magnet_: it's fixing one of our OSGi problems (the refresh problem), and not changing any api.
[14:48] <mart> magnet_: which problem do you most want to fix?
[14:48] <magnet_> mart, the dependency API -> impl.
[14:49] <magnet_> that solution adds a new dependency on API -> slf4j-proxy
[14:50] <mart> ok. it's not a very osgi-ish design.
[14:50] <mart> I'm still not sure exactly what problem that causes that creates the most pain for you.
[14:51] <mart> I get that "it's not the way we like to do things in OSGi", but I'm trying to understand which consequence of that causes most difficulty.
[14:51] <magnet_> I get your point
[14:53] <ceki> Arbalest ~sendlog
[14:53] <ceki> Arbalest, ~sendlog
[14:55] <magnet_> mart, I'm not blocked at what I'm trying to do. I'm trying to propose something that works better (or is cleaner) for OSGi users, and works the same for everyone else.
[14:56] <magnet_> that was my intro: I'm choosing a framework for a new OSGi project, so I'd rather avoid compromises so early ;)
[14:57] <magnet_> ceki, did you succeed at getting the logs?
[14:57] <ceki> nope
[14:57] <magnet_> do direct connections work for you?
[14:57] <ceki> i think so
[14:58] <magnet_> mhh maybe they don't for me ;)
[14:58] <ceki> i just conencted with Arbalest
[14:58] <magnet_> we recently moved at work and I don't how they set the firewall up
[14:58] <magnet_> ok
[14:59] <magnet_> maybe it will work from me to you
[14:59] <mart> magnet_: I do see the motivation. I agree that for OSGi, the factories should probably have been in a separate package. But it seems like a big change for everyone, and I'd think people would expect a very clear explanation of why it was made.
[15:00] <magnet_> did you get the request?
[15:00] <ceki> yes
[15:00] <ceki> don't know that to do with it
[15:00] <magnet_> no accept button? :)
[15:00] <mart> should we just gist it?
# after gist
<ceki> Once slf4j-api binds, it's irreversible
<magnet_> yes and no :)
<ceki> what else?
<mart> ceki: In OSGi, it's possible to 'refresh' bundles. You could change the logging backend, refresh everything, and use the new backend.
<magnet_> slf4j already works with OSGi, but the idea is it could work best if we used OSGi's binding system (services + a adhoc component framework) instead of slf4j's static bindings
<magnet_> so it does "too much" for OSGi -- and just enough for plain old Java
<mart> (So, no, it's not irreversable, but yes, you have to refresh everything that uses slf4j's to change it.)
<magnet_> i'm not sure how the refresh would behave with the LoggerFactory though
<magnet_> it refreshes the package wirings, but the local static state isn't supposed to just vanish
<ceki> i am still reading the logs
<mart> magnet_: Isn't the static state wiped when refreshing the other bundles?
<magnet_> mart, not sure, I would have to test
<magnet_> (I don't think so)
<mart> my understanding was that the bundle was stopped; re-resolved; and restarted.
<magnet_> yes
* kensanata (~user@fsf/member/kensanata) a rejoint #osgi
<magnet_> yes you're right.
<magnet_> the old static should be gc'd
<mart> which should include the loggers for the old slf4j impl.
<magnet_> yes
<ceki> martin has captured many of the questions i had
<ceki> questions/remarks
* kensanata est parti (Remote host closed the connection)
<magnet_> yep those were good questions, I understand there is reluctance to anything that endangers backwards compat for most users
<magnet_> but I also think we can be compatible and improve things a bit in terms of modularity :)
<ceki> i'll concede off the bat that slf4j modularity sucks
<mart> What are the advantages/drawbacks of leaving everything as is, but creating a new bundle that contains a subset of those in slf4j-api (including Logger)?
<mart> I guess a drawback is that things that depend on org.slf4j wouldn't be clear on whether they were depending on the reduced API or the full API.
<ceki> indeed
<magnet_> yes but tons of projects do that.
<magnet_> people spend 5 minutes getting the right dependency in their POM and then forget about it
<magnet_> the problem is when the contents of the artifacts change between versions
<magnet_> thats why I proposed to keep the same artifactId for the "all in one" JAR
<ceki> what would be in the all in one jar?
<magnet_> slf4j-light-api + slf4j-static-binding
<mart> which is the current slf4j-api, with some classes moved to a different package?
<ceki> so there would be slf4j-simple-all.jar, slf4j-log4j-all.jar, slf4j-jdk14-all.jar, etc... ?
<magnet_> yes, moved + a redirection class (that should be deprecated in an ideal world)
<magnet_> if you do still jdk14 builds yes that's going to multiply.
<mart> There wouldn't need to be one for each backend, though, right?
<magnet_> you can also end support of jdk14 after slf4j 2.0.0 considering it's been EOL'd years ago ;)
<mart> magnet_: Kindle is still 1.4, I think. :)
<mart> ish.
<magnet_> damn :)
<ceki> jdk14 stands for jul not jdk14
<mart> ah, yes. sorry
<ceki> jul = java.util.logging
<magnet_> yes
<magnet_> so.. no
<magnet_> there wouldn't be -all packages for those
<mart> When magnet_ says "all in one", I don't think he means the logging impl in there too.
<magnet_> yes only api side, not impl
<mart> "all in one" (here) means Logger and LoggerFactory
<mart> et. al. :)
<magnet_> a whiteboard would be nice :)
<ceki> you would separate the interfaces from static binding code
<magnet_> ceki, ideally, yes: impl should provide their static bindings aside
<magnet_> yes
<ceki> this would ge useful because?
<mart> oh, I misunderstood.
<ceki> s/ge/be/
<magnet_> ceki, because unfortunately OSGi doesn't support split packages
<magnet_> it means that you can't (or must hack around if) have two bundles exporting the same package
<magnet_> if we want to make the static binding code optional, and still need the interfaces, we must separate the packages so they end up in two bundles
<magnet_> those who want to use static binding still can
<magnet_> and those who don't can use the built-in mechanisms in OSGi (dynamic binding et all)
<magnet_> or not only OSGi: Guice, Spring, Plexus/Sisu for Maven, whatever
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment