qos-ch / slf4j

Simple Logging Facade for Java
http://www.slf4j.org
MIT License
2.35k stars 990 forks source link

`slf4j-simple-impl` logging implementation artifact separate from its exposed service provider artifact. #366

Open garretwilson opened 1 year ago

garretwilson commented 1 year ago

It would be extremely useful to bundle the slf4j-simple logging implementation as a separate artifact (e.g. slf4j-simple-impl) so that it can be used without exposing it as an automatically detected service provider. (P.S. I didn't realize how cool the name slf4j-simple-impl is until I just wrote that.) This would involve simply splitting out the implementation code (and perhaps the actual SimpleServiceProvider class as well, without exposing it as a service) into a separate subproject—little if any actual code changes would be needed.

You can see how I've done something similar with Clogr's https://github.com/globalmentor/clogr/tree/main/clogr-logback and https://github.com/globalmentor/clogr/tree/main/clogr-logback-provider .

This would bring a couple of big wins, notably solving SLF4J-592: Mechanism for indicating provider to use for unit tests. Let me lay out the use cases:

Basis for Specialized Implementations

Splitting out the slf4j-simple implementation would allow others to create specialized versions. Without the separate artifact, a specialized version has to duplicate the slf4j-simple code, because otherwise simply referring to that dependency would expose a provider that would be detected by SLF4J, resulting in "dueling providers".

This is not a theoretical need. Read Solving the Java Aws Lambda logging problem, in which Frank Afriat (@fafriat) investigates how to create a logging implementation that is simple but plays well with AWS Lambda. In theory you would just write to stdout/stderr, but there are a couple of gotchas Frank had to work around. He had to fork slf4j-simple and make a customized version. I believe (although I haven't asked him) that had slf4j-simple-impl existed, he could have simply used that dependency, added a few tweaks, and then exposed his own provider rather than copying all the code as he has done.

Including/Selecting an Optional Logging Provider

If slf4j-simple-impl existed, I could include the dependency but not expose SimpleServiceProvider via the Java service provider mechanism, so that it wouldn't be detected by SLF4J automatically, requiring me to manually "enable it".

In SLF4J-592: Mechanism for indicating provider to use for unit tests (see also my Maven exclude/remove test dependency defined in parent POM and someone else's Is there any simple pattern of slf4j usage in unit tests?) I pondered how I want logging to work in unit tests. After months of thinking, I finally know:

  1. By default I want all logging in unit tests to be suppressed during the Maven build and in the IDE.
  2. However if something goes wrong, I want to send logging to stderr during the build, just by setting some flag.

I both cases, I don't want to affect the logging provider used by the artifacts actually being built.

With slf4j-simple-impl (assuming you put SimpleServiceProvider inside slf4j-simple-impl as well, but don't expose it as a Java service provider), I could simply include slf4j-simple-impl in test scope in my parent POM. All my tests would automatically get the slf4j-simple-impl logging implementation, but it wouldn't be used—it would lurking, ready to be enabled.

In my build I would set slf4j.provider to ….NOP_FallbackServiceProvider during unit/integration tests. This would address 1) above—all logging in unit tests would be suppressed. This would not conflict with or affect in any way the logging providers of the actual artifacts build for the various libraries and applications.

In my build I would add a Maven debug profile that I could invoke with -P debug. This would override slf4j.provider with ….SimpleServiceProvider for the unit/integration tests. Suddenly all my unit tests would start logging to stdout/stderr (whichever I have configured), helping me to track down problems in the build. This addresses 2) above.

I believe this would be a complete, comprehensive solution to the build unit/integration test logging question. And pulling it off would be so simple. (The only remaining question would be whether to include SimpleServiceProvider in slf4j-simple-impl but not expose it as a service; or leave it in slf4j-simple, requiring consumers to create their own provider wrapper class.)

Note that if you decide not to split out slf4j-simple-impl, I can easily write my own simple logging implementation—it would just be a shame to reinvent the wheel.

If you think this is a great idea, just let me know and I could probably file a pull request for it.

garretwilson commented 1 year ago

The only remaining question would be whether to include SimpleServiceProvider in slf4j-simple-impl but not expose it as a service; or leave it in slf4j-simple, requiring consumers to create their own provider wrapper class.

Here I think it might be best to leave SimpleServiceProvider in slf4j-simple to prevent confusion. This means that anyone using slf4j-simple-impl would need to write their own service provider class, but they would probably be doing that anyway, and it's just a simple wrapper. The win is that they don't have to re-implement the actual logging code.