[slf4j-user] Fail Silently on NOP implementation

Marshall Pierce marshall at mpierce.org
Thu Apr 28 16:42:11 UTC 2016


> On Apr 28, 2016, at 8:13 AM, Phillip Lord <phillip.lord at russet.org.uk> wrote:
> 
> Marshall Pierce <marshall at mpierce.org> writes:
> 
>> The job of slf4j-api is just to be the developer-side interface to logging. It
>> purposely delegates the decision of what to do with those logs to the end
>> user. I think it is a mistake to include policy mechanisms like these into the
>> API.
>> 
>> If you’re writing a library, don’t include a binding. If you’re writing a
>> tool, select a binding.
> 
> I do not understand this distinction. I have some software which can provides
> an interactive shell that I can use directly (i.e. a tool) or which can
> be used as a dependency by downstream applications (i.e. a library).
> Conclusion I should select a binding, and not include one.

Ah, well, it is a bit subtle, but it is there. Maybe it will be easier to draw parallels from a different language. Suppose you are writing yourself a figlet clone (phiglet, perhaps?) in C with the goal of being embeddable into other programs.

As a polite C library author, you allow your users to specify their own malloc() implementation: some people will want jemalloc, others will want their own custom arena allocator, etc. 

However, you also want to provide a /usr/bin/phiglet executable in addition to /usr/lib/libphiglet.so so that people can use your new, fancy, ascii-art-text capabilities. Let’s brush aside for the moment that executables and shared objects have different file formats (a distinction that doesn’t exist for the JVM). In the phiglet executable, you need to make decisions about allocators and such things. In libphiglet.so, you need to *not* make those decisions. If you wanted both to exist in the same file, you would need to do some runtime histrionics to do the right thing (setting the malloc impl, or not) depending on how it was invoked.

You have some requirements:
- Provide a library for use in other projects
- Provide a command-line (or whatever) tool that is usable stand-alone

Is it also a requirement that these both be resident in literally the same artifact? If so, you have a problem (and a very unusual requirement). If you provide more detail on how you expect your software to be consumed as a library and as a tool, perhaps we can find a solution that doesn’t involve classpath wizardry at runtime.

>> It is an error to not include a binding, and that’s why it prints to
>> stderr. If you’re writing a tool and you want silence, use nop. If
>> you’re not writing a tool, it’s not your decision.
> 
> I am writing both.

Yes, but it’s probably not the case that they must live in the same artifact. 

>> Regarding slf4j-silent / slf4j-strict: This is just another “policy via
>> classpath” mechanism. If you want to control the output of calls made via
>> slf4j-api, we already have a mechanism for this. What happens when both are on
>> the classpath? As a new user to SLF4J, would you really enjoy learning how to
>> use dependency excludes as a debugging tool during your “why won’t my logging
>> show up” journey rather than having a clear error message that informs you
>> that you’re doing it wrong, and pointing you towards the URL with more info?
> 
> Indeed. But, as a non-user of SLF4J would you really enjoy learning
> about how to turn off an error message for a library that you are not
> using, but which any of your transitive dependencies has chosen to use?

You ARE using it, because your transitive dependencies use it. If you choose to approach transitive dependencies as “less real” than your explicitly specified dependencies, you are going to have a sad time.

> In fact, given the approach above, all you would need is to change your
> advice: use slf4j-api when developing, but deploy your artifacts with
> slf4j-silent. The hello world default case would behave as now. In
> practice, I think, most library developers will need to use a different
> dev dependency environment anyway (with logging on) and during
> deployment (with logging off).

No, now all your users would need to exclude the slf4j-silent dependency or risk confusing silence when logging isn’t working. That is a very bad outcome.

In library development, running the library consists of running tests (where logging can be easily configured by adding a test-only dependency) or running other standalone programs that use the library (where, again, configuring logging is trivial and completely separate from the library). In what sense are you “running a library during development” that is not one of those cases?

>> This complicates logging, and the debugging thereof, without a clear gain.
> 
> The clear gain is that the SLF4J is not going to printing error messages
> to people who have not deliberately chosen to use it. I can understand,
> of course, why it might prefer the needs of its users over those who are
> not. But, it doesn't take too much googling to find quite a few people
> asking "where is this error message coming from, and what is SLF4J”.

You HAVE deliberately chosen to use it by using software that uses it.

>> The fundamental issue is that you’re trying to use the same artifact as both a
>> library and an end-user tool (which I’ll define as “a thing with a main method
>> or equivalent”). This is an antipattern because there are conflicting goals
>> for libraries and tools, as you’ve correctly identified. 
> 
> I'm writing in Clojure -- so all things have a main method. Also true of
> scala, groovy, javascript and, indeed, Java from 1.9 onwards with
> jshell. So, not an antipattern at all, just business as usual.
> 
> The JVM is a big ecosystem; bigger than Java.

Yes it is, but we disagree about its antipattern-ness: libraries and standalone tools have different responsibilities. Just because it’s possible doesn’t mean it’s a good idea. (It’s quite possible to do in Java, FWIW: just shove some main methods in various classes in your library. I’ve seen it more often than I would wish.) If you want to have both types of usage coexist in the exact same artifact, be prepared for sadness and complexity.

>> If your users will consume your code in two different ways, then it is
>> sensible to have two different artifacts.
> 
> 
> Well, by this argument, SLF4J is being consumed in two different ways.
> In which case, it should have two different artifacts. This seems, to
> me, a more sensible approach than suggesting that all downstream
> dependencies of SLF4J should have a "thing-with-slf4j-api" and
> "thing-with-slf4j-nop" artifacts.

I’m not sure what you mean here. There already ARE two parts to slf4j: the api and the set of different bindings.



More information about the slf4j-user mailing list