Live data from Hacker News

Why a Logging Framework Should Not Log

russet.org.uk

21–30 of 58 posts

Re: Why a Logging Framework Should Not Log

#21
post #4

This is a well understood problem and I think the design pattern SLF4J uses has become quite popular. The ability to allow consumers to bind at runtime the implementation they want by providing an api jar is kind of a neat idea. The thing I dislike the most is when libraries force me to use their logging runtime. If they need it just for tests they should add the 'test' scope and just use the 'api' jar as a 'compile'…

It doesn't force anything though, unless you statically refer to the runtime. It does force you to exclude their logging backend, but that is straight-forward enough.

Dependency exclusions is a code smell everywhere but java. I don't understand why someone is actively giving this advice.

Re: Why a Logging Framework Should Not Log

#22
My feeling is that SLF4J and in general the boot strap initialization is fundamentally flawed in Java because of the plethora of libraries that do static initialization. I have been meaning to blog about this for sometime. The reason is simple. Bootstrap configuration should load before logging but that is not the case.

In a large application it can be very difficult to control what boots up and the overall order of things being initialized particularly with SL4J, configuration frameworks like commons-configuration (archaius) and DI frameworks like Spring and Guice.

I understand that SLF4Js binding method is a static dependency to enable the very small case of Java JIT elision of method calls when using the NOOP logger.

But that is such a small case. If you have a library that is extremely hi performance do not use SLF4J for god sakes.

The reality is most large application projects should really create their own SLF4J binding so that they can control the initialization process particularly if you use a networking logging infrastructure such as AMQP. And this is fairly annoying because you will have to copy and paste the bridge library (ie because of the static nature it is impossibly to intercept the logging framework).

SLF4J should really do its binding dynamically (one option would be to use java.util.ServiceLoader). This is so that you could wrap and load existing bridges with out essentially having to copy and paste existing bridges.

Overall I would say if you have either a hi performance library, networking library, configuration library, or any other low level library please do not link to SLF4J. It is trivial to make your own one class logger that either uses system properties or what not to do its dispatching.

Some example libraries that do this correctly are the Kryo serializer, RESTEasy, and Tomcat (although flawed Tomcat does try).

Spring and most Apache projects some of the worse because they rely on Commons logging and the use static loggers on initialization (commons-config and most of Spring).

Re: Why a Logging Framework Should Not Log

#24
post #20

I really wish Java developers would just use java.util.logging and be done with it.

That's an all-or-nothing solution. As third-party libraries often use alternative loggers, and java.util.logging cannot be substituted on the classpath (because of the java.* namespace), then using JUL locks consumers out of using their own logging facade as it cannot be bridged to SLF4J.

Re: Why a Logging Framework Should Not Log

#25
post #17

Earlier quoted context omitted.

But it seems useful because you can decide what behavior your want for your project.

Right, it is configurable. But there is no reason why a configurable library can't have reasonable default behavior if no configuration is supplied. While still being configurable. If you're winding up as a buried dependency that end developers may not be aware of or care about, you want to do this.

What reasonable default behaviour? If you're writing a console application, dumping to stderr is probably not a good idea - writing random log files that the end-user/admin has no idea about is probably not a good idea - shutting up and doing nothing is probably not a good idea (Java code has a tendency to use logging to warn about non-fatal configuration errors, etc). And let's not get started on what verbosity to set by default.

Re: Why a Logging Framework Should Not Log

#26

The logging in java is supper idiotic. This is because everyone agrees that Java's logging is shit. So every library uses something else. I have a small project and have 4 logging systems (that I had to integrate). ps: if you are a business person and have an option on this, I don't care for it regardless of your pov.

Yes, this is exactly right. What a fucking nightmare. Every time I need to set up java logging for a new project, update a project, revisit an old project, I am dragged back to logging configuration hell.

Which logging package do I use? What is the logging config file called and where do I put it? How do I specify it? What do I do if different packages use different packages? A logging layer on TOP of other logging packages only makes things worse.

I'm sure many Java people remember the XML library hell. Each time you integrated another package that relied on XML configuration, you had to be sure to have the right version of the xerces library. Except that XML was simpler because XML itself didn't change that much. Logging is much worse.

To go on an even longer rant: Everything has become so fractured and distributed in the name of good modularity. Except that the interfaces are underspecified, or if adequately specified, they keep wiggling around. And good luck finding documentation to help you configure. It's got to match your version of every component, or you will likely fail. I am working on a project involving jetty, a Postgres database, a JDBC connection pool, and a logging system. Absolutely conventional stuff. I spent a couple of DAYS trying to do things the right way, doing XML config of everything. I absolutely could not make it work. I finally wrote my own trivial connection pool and configured everything in code. Took half an hour, done. I don't care that I can't swap out my logging system at runtime. That's a stupid thing to do anyway. When I need to change my connection pool, I will modify my code configuring it and re-deploy. The slightly improved flexibility is just not worth the fragility and maintenance headaches of doing things the "right" way.

Re: Why a Logging Framework Should Not Log

#27
post #16

Earlier quoted context omitted.

My framework is built in Clojure. The interpreter is always there. Think of it like a statistics library with R. The idea of a "main application" is itself not a clear one. There are a set of functions you can use. Whether you choose to launch directly over the library or import it is a matter of convenience.

I would say that the responsibility to define a backend falls either on the Clojure interpreter or the user of the interpreter, then. Java is not typically run on an interpreter shell and the defaults that normally are sensible for Java probably aren't appropriate there. It's a perfectly sensible default for a framework to warn you at runtime that it's not configured properly, and stderr is the appropriate channel fo…

The problem is that it is a Facade/API that is initializing and printing to STDERR.

I completely disagree with the idea of making slf4j-nop a required exclusion dependency (that is a I agree with @paulmd and not the OP) but a facade/api should have very little interaction with the boot process of Java. The default should be to do nothing and require either a java.util.ServiceLoader like mechanism or some other explicit configuration (or in SL4J's case adding a bridge).

That is slf4j-nop behavior should be the default (not the dependency). I understand that would confuse users but I think it would be the correct behavior. (also see my other comment for more reasons)

Re: Why a Logging Framework Should Not Log

#28
post #17

Earlier quoted context omitted.

Right, it is configurable. But there is no reason why a configurable library can't have reasonable default behavior if no configuration is supplied. While still being configurable. If you're winding up as a buried dependency that end developers may not be aware of or care about, you want to do this.

What reasonable default behaviour? If you're writing a console application, dumping to stderr is probably not a good idea - writing random log files that the end-user/admin has no idea about is probably not a good idea - shutting up and doing nothing is probably not a good idea (Java code has a tendency to use logging to warn about non-fatal configuration errors, etc). And let's not get started on what verbosity to s…

A good default is to write to STDERR if the severity level is above some threshold, otherwise do nothing.

That way the console can tell you about configuration errors, and by default the library will log nothing. If the library author considers it important to log somewhere, the library author is responsible for putting both an understandable warning up, and writing documentation.

Re: Why a Logging Framework Should Not Log

#29
I always found SLF4J's reasoning quite simple and logical and not at all an example of "those situations which show that software is difficult, and that often there are only compromises".

I think the core problem in this case is that the author is not clear if they are writing a library or a (command line) application.

The following workflow has always worked well for me:

1) Do you expect users of your project to use it by putting it in their classpath and calling its methods from their code? Then it's a library. You have no idea what else will be on the classpath and which context your library will be loaded in. (In fact, the user might not even be aware they are using your library thanks to transitive dependencies)

Libraries should never (if they can avoid it in any way) modify application-wide settings. So: Don't include any logger implementation at all, especially not the nop logger. (Please. Seriously. I've had to deal with misbehaving libraries in that regard and finding out why your logger settings are suddenly overwritten is a serious PITA.)

2) Do you expect your project to be used as a command line/standalone utility via java -jar ... or java -cp ...? Then you're writing an application. Do include a logger implementation and all necessary configuration. You can do it safely here as no one else should be using your project as a dependency.

3) Your project is a mix of 1) and 2) : Find out which parts of your project are library and which are application and split into sub-projects accordingly.

There are a few special cases I see, but not many:

4) You're writing unit tests for a library project: Do include a logger implementation, but make sure it's only visible to the test system. E.g., in Maven, you would add the logger implementation as a test-scoped dependency and put all configuration in src/test/resources.

5) You're writing a plugin/servlet/etc: Read up on your host system and find out if they provide logging or if it's your responsibillity. Proceed accordingly.

6) You're not using SLF4J but one of your dependencies does and so you get that message: Think about using SLF4J in your own project. Really. It's becoming almost a standard and usually you can keep using your old logging framework below it - but it will make things much easier later on. But if you don't want to, for some reason... 6a) If you're writing a library: Don't do anything at all. Do not include any logger implementation. See 1). The user of your library will be in a much better position to deal with the problem than you are. 6b) If you're writing an application: Include the slf4j-xxx implementation that corresponds to your logging framework: The log events from your dependency will be routed to the logging framework you're using and you can continue like you did before. If you're very certain you don't want any logging from any library using SLF4J, use the nop logger - however that is a very unusual case and a very inflexible solution.

Re: Why a Logging Framework Should Not Log

#30
post #21

Earlier quoted context omitted.

It doesn't force anything though, unless you statically refer to the runtime. It does force you to exclude their logging backend, but that is straight-forward enough.

Dependency exclusions is a code smell everywhere but java. I don't understand why someone is actively giving this advice.

The alternative is not specifying a nop dependencies and then inflicting on downstream uses the choice that I was not prepared to make.
Post reply on HN