SLF4J / SLF4J-592 [Open]
Programmatically specify fallback provider for unit tests.

==============================

Here's what changed in this issue in the last few minutes.

This issue has been created
This issue is now assigned to you.


View or comment on issue using this link
https://jira.qos.ch/browse/SLF4J-592

==============================
 Issue created
------------------------------

Garret Wilson created this issue on 21/Jun/23 17:10

Summary:              Programmatically specify fallback provider for unit tests.
Issue Type:           New Feature
Affects Versions:     2.0.7
Assignee:             SLF4J developers list
Components:           Core API
Created:              21/Jun/23 17:10
Priority:             Minor
Reporter:             Garret Wilson
Description:
  I originally described this problem in [Maven exclude/remove test dependency 
defined in parent POM|https://stackoverflow.com/q/73747106].
  
  In {{LoggerFactory}} SLF4J automatically falls back to a do-nothing provider 
if no logging implementations are on the classpath.
  
  {code:java}
  static final NOP_FallbackServiceProvider NOP_FALLBACK_SERVICE_PROVIDER = new 
NOP_FallbackServiceProvider();
  …
              List<SLF4JServiceProvider> providersList = findServiceProviders();
              reportMultipleBindingAmbiguity(providersList);
              if (providersList != null && !providersList.isEmpty()) {
                  PROVIDER = providersList.get(0);
                  // SLF4JServiceProvider.initialize() is intended to be called 
here and nowhere else.
                  PROVIDER.initialize();
                  INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
                  reportActualBinding(providersList);
              } else {
                  INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
                  Util.report("No SLF4J providers were found.");
                  Util.report("Defaulting to no-operation (NOP) logger 
implementation");
                  Util.report("See " + NO_PROVIDERS_URL + " for further 
details.");
  …
          if (INITIALIZATION_STATE == UNINITIALIZED) {
              synchronized (LoggerFactory.class) {
                  if (INITIALIZATION_STATE == UNINITIALIZED) {
                      INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                      performInitialization();
                  }
              }
          }
          switch (INITIALIZATION_STATE) {
          case SUCCESSFUL_INITIALIZATION:
              return PROVIDER;
          case NOP_FALLBACK_INITIALIZATION:
              return NOP_FALLBACK_SERVICE_PROVIDER;
  {code}
  
  In a lot of libraries, I want to run unit tests and see any logging output. 
Thus in my libraries I would need to have a logging implementation. But a 
library should not be specifying a logging implementation—a library may use the 
SLF4J API, but leave it to some later application to specify the actual SLF4J 
provider.
  
  Currently in my [Maven "root" parent 
POM|https://github.com/globalmentor/globalmentor-root/blob/main/pom.xml] I try 
to sidestep this by declaring the Logback simple implementation, but only in 
{{test}} scope:
  
  {code:xml}
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <scope>test</scope>
  </dependency>
  {code}
  
  This works fine except in my own applications, which use the above-mentioned 
root POM as their parent. One way or another they necessarily specify an SLF4J 
provider. As there is no way to remove the {{slf4j-simple}} provider from test 
scope, when I run tests for the application I get the {{SLF4J: Class path 
contains multiple SLF4J providers}} warning.
  
  Basically the goal here is to somehow declare a provider that only takes 
effect if there is no other provider present. One approach would be to enhance 
{{SLF4JServiceProvider}} to have something like an {{isFallback()}} method that 
would indicate it should only be used if no other providers are present. But 
this is just a degenerate case of a system of priorities, so really this would 
not be much different than adding a {{getPriority()}} integer or some enum of 
priorities. I doubt you're keen for this approach at all.
  
  Perhaps a cleaner, more surgical approach would be to add a static 
{{LoggerFactory.setFallbackProvider()}} method. Instead of {{static final 
NOP_FallbackServiceProvider NOP_FALLBACK_SERVICE_PROVIDER = new 
NOP_FallbackServiceProvider()}} you would have a static SLF4JServiceProvider 
fallbackProvider = new NOP_FallbackServiceProvider()}} internal variables. 
(Even the constant should have been declared to the interface 
{{SLF4JServiceProvider}} in the first place, in my opinion—there's no reason 
{{LoggerFactory}} needs to know the specific type of its fallback provider.)
  
  The new method {{LoggerFactory.setFallbackProvider()}} could check that 
{{INITIALIZATION_STATE == UNINITIALIZED}}, throwing an 
{{IllegalStateException}} if not, ensuring that this method is called before a 
logger is requested. Then you simply change this (and rename the enum value):
  
  {code:java}
          case NOP_FALLBACK_INITIALIZATION:
              return NOP_FALLBACK_SERVICE_PROVIDER;
  {code}
  
  to this:
  
  {code:java}
          case NOP_FALLBACK_INITIALIZATION:
              return fallbackProvider;
  {code}
  
  This should be 100% backwards compatible. It would allow any application to 
specify what logging implementation to use if none were provided. In my case, I 
could simply create a superclass for all my unit tests, and do something like:
  
  {code:java}
  LoggerFactory.setFallbackProvider(new MySystemOutLoggerProviderForTests())
  {code}
  
  Maybe this discussion will give you an idea for an even better approach.
  
  In fact, now that I think of it, it would probably be better in a testing 
environment, not to provide a fallback provider, but to _force_ a provider, 
overriding any that is already on the main compile classpath:
  
  {code:java}
  LoggerFactory.forceProvider(new MySystemOutLoggerProviderForTests())
  {code}
  
  You would would understandably be reluctant to allow that drastic of a change 
without lots of consideration.
  
  In any case, there remains a need to specify a logging provider in the 
context of unit tests without interfering with the main logging configuration 
and vice-versa.
  
  See also [Is there any simple pattern of slf4j usage in unit 
tests?|https://stackoverflow.com/q/3820290] (somebody else's question—I didn't 
write it) from 12 years ago.


==============================
 This message was sent by Atlassian Jira (v9.6.0#960000-sha1:a3ee8af)

_______________________________________________
slf4j-dev mailing list
slf4j-dev@qos.ch
https://mailman.qos.ch/cgi-bin/mailman/listinfo/slf4j-dev

Reply via email to