<div dir="ltr">The issue I've come across that's similar to this is integrating logging with Spring Boot or other initialization style frameworks. The issue there was that Spring Boot wants to defer logging initialization until it's loaded up enough of its own configuration related code, but that code also wants to use logging. Eager initialization of the logging system in Logback and Log4j both cause issues in these sorts of environments. In Log4j, we ended up using a similar pattern to the ServiceLoader files, but we added some additional metadata there like API version compatibility.</div><div class="gmail_extra"><br><div class="gmail_quote">On 18 March 2017 at 10:18, Adam Gent <span dir="ltr"><<a href="mailto:adam.gent@snaphop.com" target="_blank">adam.gent@snaphop.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Fri, Mar 17, 2017 at 6:47 PM, Ceki Gülcü <<a href="mailto:ceki@qos.ch">ceki@qos.ch</a>> wrote:<br>
><br>
> Hi Adam,<br>
><br>
</span><span class="">> --------------<br>
> Quoting from <a href="https://www.slf4j.org/faq.html#optional_dependency" rel="noreferrer" target="_blank">https://www.slf4j.org/faq.<wbr>html#optional_dependency</a><br>
><br>
> It is reasonable to assume that in most projects Wombat will be one<br>
> dependency among many. If each library had its own logging wrapper, then<br>
> each wrapper would presumably need to be configured separately. Thus,<br>
> instead of having to deal with one logging framework, namely SLF4J, the user<br>
> of Wombat would have to detail with Wombat's logging wrapper as well. The<br>
> problem will be compounded by each framework that comes up with its own<br>
> wrapper in order to make SLF4J optional. (Configuring or dealing with the<br>
> intricacies of five different logging wrappers is not exactly exciting nor<br>
> endearing.)<br>
> --------------<br>
<br>
</span>Just to make my previous my points clear. My code (which is basically<br>
just use your own static method instead of LoggerFactory.getLogger())<br>
I'm proposing is to encourage users not to create their own wrappers<br>
but write their on LoggerFactory.getLogger() instead of reimplementing<br>
all of slf4j-api.<br>
<br>
This is not to make SLF4J optional. The code is to disable or<br>
intercept the static initialization of an API interface. I would<br>
actually argue that an API should never do static initialization but<br>
that ship has sailed. The framework or application should probably<br>
explicitly initialize SLF4J but I admit the way SLF4J is currently<br>
used and understood at this point it is probably easier. Consequently<br>
Just know that the logging framework is almost always the first thing<br>
to initialize (hence why one would really want to be configure it).<br>
<br>
To show how egregious this is just putting an annotation on a class or<br>
implementing an interface can cause of initialization of SLF4J which<br>
then kicks off the initialization of the logging framework which may<br>
or may not be configurable for interception.<br>
<span class=""><br>
><br>
><br>
> --------------<br>
> Quoting from: <a href="http://stackoverflow.com/a/11360517/100970" rel="noreferrer" target="_blank">http://stackoverflow.com/a/<wbr>11360517/100970</a><br>
> and <a href="http://stackoverflow.com/a/11359358/100970" rel="noreferrer" target="_blank">http://stackoverflow.com/a/<wbr>11359358/100970</a><br>
><br>
> Except the end user could have already done this customization for his own<br>
> code, or another library that uses log4j or LogBack. jul is extensible, but<br>
> having to extend LogBack, jul, log4j and God only knows which other logging<br>
> framework because he uses 4 libraries that use 4 different logging<br>
> frameworks is cumbersome. By using slf4j, you allow him to configure the<br>
> logging frameworks he wants. not the one you have chosen. Remember that<br>
> typical projects use myriads of libraries, and not just yours.<br>
> --------------<br>
><br>
<br>
</span>This is exactly my point. To control the initialization of our suite<br>
of applications we have to write custom code that intercepts log4j,<br>
log4j2, logback, etc all because there is no way to do it in SLF4J.<br>
SLF4J is not configurable!<br>
<br>
Why do we have all these different logging frameworks? Well because of<br>
legacy code or that is what that application prefers (e.g. zookeeper<br>
or solr or something). I'm talking in a microservice environment it is<br>
actually common to have a zoo of logging frameworks (obviously not in<br>
the same app). I think you misunderstood me before and thought I meant<br>
having all sorts of logging frameworks in the same application.<br>
<br>
I guess ideally SLF4J should offer some sort of configuration (it<br>
already has system.properties it reads to do things) to do what I'm<br>
talking about but for the time being library writers can do what I'm<br>
proposing particularly if logging isn't really a critical aspect of<br>
the library.<br>
<br>
Finally most libraries you do have to go look up information on how to<br>
turn on logging for that particularly library. It is just a nasty fact<br>
of life. Libraries don't use Markers or logger name hierarchy in a<br>
standard way. Even then they still often (particularly for high<br>
performance libraries) have some system property or something to<br>
actually enable or disable logging.<br>
<br>
I'm just trying to offer a best practice or standard to prevent the<br>
above of every library being different or worse having a their own<br>
complicated wrapper. Ideally SLF4J would offer more than just a best<br>
practice.<br>
<div class="HOEnZb"><div class="h5"><br>
><br>
> --<br>
> Ceki<br>
><br>
> On 3/17/2017 22:28, Adam Gent wrote:<br>
>><br>
>> Yes as noted in my not really javadoc comment:<br>
>><br>
>> /*<br>
>> * Simply prefix Internal in front of LoggerFactory:<br>
>> * So instead of:<br>
>> * Logger logger = LoggerFactory.getLogger(<wbr>MyClass.class);<br>
>> * It should be:<br>
>> * Logger logger = InternalLoggerFactory.<wbr>getLogger(MyClass.class);<br>
>> */<br>
>><br>
>> Obviously this creates tight coupling with LoggerService but that is<br>
>> OK because LoggerService is expected to be a copy and paste snippet<br>
>> (that is each library should have its own namespace LoggerService). It<br>
>> could also be multiple files. I just made it one file to encourage<br>
>> easy copy and paste.<br>
>><br>
>> I use LoggerService in my own message bus library (it is a library<br>
>> that is sort of like hystrix + guava eventbus + amqp + reactivestreams<br>
>> that I plan on open sourcing someday). This library does it for<br>
>> performance reasons.<br>
>><br>
>> I also have a configuration facade framework as well that has an<br>
>> opensource start here <a href="https://github.com/agentgt/configfacade" rel="noreferrer" target="_blank">https://github.com/agentgt/<wbr>configfacade</a> . Our<br>
>> internal version is far more sophisticated as well as actually<br>
>> production quality and is the one that uses its own LoggerService as<br>
>> well. This library does it for bootstrap reasons and to beat the<br>
>> logging frameworks initialization.<br>
>><br>
>><br>
>> I have seen other libraries that refuse to use SLF4J directly though<br>
>> because of its static binding. Many JBoss libraries IIRC such as<br>
>> RestEasy:<br>
>> <a href="https://bill.burkecentral.com/2012/05/22/write-your-own-logging-abstraction/" rel="noreferrer" target="_blank">https://bill.burkecentral.com/<wbr>2012/05/22/write-your-own-<wbr>logging-abstraction/</a><br>
>><br>
>> Of course these libraries could have just wrapped the LogFactory<br>
>> creation but instead implement their own logging wrapper which is a<br>
>> lot more code as well as not implementing logging parameter<br>
>> replacement at all or correctly ("{}").<br>
>><br>
>> -Adam<br>
>><br>
>> On Fri, Mar 17, 2017 at 4:36 PM, Ceki Gülcü <<a href="mailto:ceki@qos.ch">ceki@qos.ch</a>> wrote:<br>
>>><br>
>>><br>
>>> Hi Adam,<br>
>>><br>
>>> Using ServiceLoader.load(<wbr>LoggerService.class) is quite a good idea. I<br>
>>> think<br>
>>> we will have to go that way in future versions of SLF4J to be compatible<br>
>>> with Java 9.<br>
>>><br>
>>> I suppose your library code no longer invokes<br>
>>> org.slf4j.LoggerFactory.<wbr>getLogger(). How do you use LoggerService in<br>
>>> your<br>
>>> libraries?<br>
>>><br>
>>> On 3/17/2017 16:51, Adam Gent wrote:<br>
>>>><br>
>>>><br>
>>>> Many library writers want to use SLF4j but may want to avoid the<br>
>>>> default initialization and binding process. In fact if it is a library<br>
>>>> and not a framework I recommend something like what I'm proposing.<br>
>>>><br>
>>>> I have written a single very small copy and paste class that you can<br>
>>>> put in your own library to avoid default SLF4J initialization while<br>
>>>> allowing consumers of your library to pick whatever SLF4J<br>
>>>> initialization they like through the ServiceLoader mechanism. The<br>
>>>> class is meant to be copied and pasted such that you pick your own<br>
>>>> package (namespace) for the ServiceLoader part.<br>
>>>><br>
>>>> The code is available at this github gist:<br>
>>>><br>
>>>> <a href="https://gist.github.com/agentgt/28dc6e9724cb8b96ca08fc147476a7de" rel="noreferrer" target="_blank">https://gist.github.com/<wbr>agentgt/<wbr>28dc6e9724cb8b96ca08fc147476a7<wbr>de</a><br>
>>>><br>
>>>><br>
>>>> There are several reasons why if you are a library writer to consider<br>
>>>> this pattern:<br>
>>>><br>
>>>> * Often library users don't want to see the annoying default missing<br>
>>>> slf4 binding messages<br>
>>>> * Performance reasons. By defaulting to NOP you prevent accidental<br>
>>>> performance problems by a user of the library.<br>
>>>> * You allow for even greater logging configuration and separation<br>
>>>> than possible with SL4J. For example one library could be configured<br>
>>>> to use a Logback SLF4J and another Log4j legacy SLF4J.<br>
>>>> * If the library used to configured a down stream logging framework<br>
>>>> (ie logback) you might need interception (ie SubstituteLogger).<br>
>>>><br>
>>>> Thoughts?<br>
>>>><br>
>>>> I'm hoping perhaps the SL4J documentation recommend something like<br>
>>>> this for library writers.<br>
>>>><br>
>>>> -Adam<br>
>>>><br>
>>> ______________________________<wbr>_________________<br>
>>> slf4j-user mailing list<br>
>>> <a href="mailto:slf4j-user@qos.ch">slf4j-user@qos.ch</a><br>
>>> <a href="http://mailman.qos.ch/mailman/listinfo/slf4j-user" rel="noreferrer" target="_blank">http://mailman.qos.ch/mailman/<wbr>listinfo/slf4j-user</a><br>
>><br>
>><br>
>><br>
>><br>
> ______________________________<wbr>_________________<br>
> slf4j-user mailing list<br>
> <a href="mailto:slf4j-user@qos.ch">slf4j-user@qos.ch</a><br>
> <a href="http://mailman.qos.ch/mailman/listinfo/slf4j-user" rel="noreferrer" target="_blank">http://mailman.qos.ch/mailman/<wbr>listinfo/slf4j-user</a><br>
<br>
<br>
<br>
</div></div><span class="im HOEnZb">--<br>
CTO<br>
SnapHop (<a href="http://snaphop.com" rel="noreferrer" target="_blank">snaphop.com</a>)<br>
(twitter) @agentgt (linkedin) <a href="http://www.linkedin.com/in/agentgt" rel="noreferrer" target="_blank">http://www.linkedin.com/in/<wbr>agentgt</a><br>
(cell) <a href="tel:781-883-5182" value="+17818835182">781-883-5182</a><br>
</span><div class="HOEnZb"><div class="h5">______________________________<wbr>_________________<br>
slf4j-user mailing list<br>
<a href="mailto:slf4j-user@qos.ch">slf4j-user@qos.ch</a><br>
<a href="http://mailman.qos.ch/mailman/listinfo/slf4j-user" rel="noreferrer" target="_blank">http://mailman.qos.ch/mailman/<wbr>listinfo/slf4j-user</a></div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">Matt Sicker <<a href="mailto:boards@gmail.com" target="_blank">boards@gmail.com</a>></div>
</div>