[slf4j-user] Recommendation for library writers: use thiscopy and paste wrapper around SLF4j to avoid default static initialization

Joachim Durchholz jo at durchholz.org
Sat Mar 18 19:20:47 CET 2017


Am 18.03.2017 um 18:02 schrieb Adam Gent:
> In a framework setting it is very difficult to predict when the
> logging will be initialized since you often don't control main.

That's not a problem: Either you are responsible for main, then you set 
up logging, or you are not responsible for it, then leave that unhandled.
Forcing a logging policy just because the library thinks it should 
control logging is violating separation of concerns: Libraries shouldn't 
care.

(There is one exception, if latencies are involved. Just emit a dummy 
log message in the library initialization code to force logger 
initialization.)

> Besides the framework may want to actually switch from a NOP (or
> Simple Logger) while it is initializing to a full blown
> LogFactory.getLogger() after the bootstrap process has finished

SLF4J used to do noop logging before it was fully initialized, today it 
is simply collecting the log messages and emitting them until the 
backend is ready.

> It is also actually fairly difficult to unit test logging code since
> you really don't control the bootstrap process especially since many
> tests like Spring framework require a special JUnit runner (@RunWith).

There are two answers to that.

1) Consider logging to be the debugger substitute for code that is 
running unattended. In that case, you have no functional requirements, 
you add log messages as you find you need to investigate, and you do not 
have a need to unit test logging.
If a message is too important to be lost, it is not logging but an audit 
trail; you do not want to use a logging framework.
(I am aware that not everybody will subscribe to that view. However, I 
have yet to see substantial counterarguments.)

2) Mock the logger calls. You don't want to verify the exact output 
anyway, which is subject to all kinds of configuration formatting; you 
may not even want to validate that a message has a specific wording, 
just that a logger.warn call happens with a specific set of substitution 
parameters.
(I think mocking covers all the use cases that aren't covered by (1).)

> I'm trying to recommend a pattern for libraries that want to do some
> logging perhaps only for diagnostic and development purposes but
> logging is not a critical component of the library (perhaps even the
> logging is too opaque anyway for normal users).

In that case, my recommendation is: Just use SLF4J as it comes out of 
the box. (I have yet to see a different recommendation.)

> Particularly if this library is high performance or maybe used by
> android applications. In such case the library would default to the
> NOPLogger and thus not initialize.

How would the library know whether it's called in Android?
Should it even care?

> The reality is 99% of libraries that do logging is completely
> irrelevant to the consumer of the library. The logging is mostly
> trace.
 > In a environment like Android this adds up to be nontrivial
> amount of performance loss (as well as callstack and method count
> issues).

Your proposal does not change anything about the performance of trace 
messages.

> Finally as another person alluded there is also a chicken and egg
> problem If I'm trying to bootstrap my application to get ready to
> configure logging and perhaps use a parsing library (json, yaml
> whatever) or configuration library and that library uses logging... we
> now have a problem.

That's a solved problem: SLF4J is collecting log messages during 
startup, and emitting them once the backend is ready.

Also, a production system shouldn't use anything that requires a lot of 
setup to work. Text files and message queues should be fine, database 
connections are a bit too fragile.
(General principle: Use a more robust mechanism for logging than for 
what your application does.)

> Also after  looking at 20 of our various
> log4j.xml/logback.xml/log4j2.xml config code bases the only loggers we
> care about are our own and maybe the parent framework (Spring). Thats
> it.

That's two logging configurations; in the code, you still have a single 
logger.
Oh. Right. In
   Logger logger = LoggerFactory.getLogger(...);
what's called "Logger" is really just a namespace.
There is no real configuration going on for it, it's just a name plus 
two internal data fields (which aren't initializated until they are used).
I.e. setting up a "Logger" object is a very cheap operation; 
initializing SLF4J can take a while because it needs to pull all the 
configuration files together, but your application isn't going to do 
that in a background thread so nothing else happens during that time 
anyway. (That's why "backend isn't ready yet" is *that* much of a 
problem in practice.)


More information about the slf4j-user mailing list