Uploaded image for project: 'logback'
  1. logback
  2. LOGBACK-1196

Add service loader to ContextSelectorStaticBinder.

    Details

    • Type: Improvement
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 1.1.7
    • Fix Version/s: 1.3.0-alpha6
    • Component/s: logback-classic
    • Labels:
      None

      Description

      Logback needs a service loader mechanism for determining the ContextSelector, in addition to the precarious system property method currently used. This would be easy to add, in a fully backwards-compatible manner.

      Logback uses a ContextSelector to determine which context should be used when a logger is requested. It initializes the context selector through its specialized implementation of org.slf4j.impl.StaticLoggerBinder, which loads a default context and then delegates to contextSelectorBinder.init(defaultLoggerContext, KEY). ContextSelectorStaticBinder.init((LoggerContext defaultLoggerContext, Object key) determines the context selector to use with the following logic:

      1. If the "logback.ContextSelector" system property contains "JNDI", use a ContextJNDISelector.
      2. If the "logback.ContextSelector" system property contains anything else, assume the value is the name of a context selector class and try to instantiate that.
      3. Otherwise if there is no "logback.ContextSelector" system property, use a DefaultContextSelector.

      Unfortunately relying on a system property is precarious. It is hard to automatically wedge into this process without manually setting a system property before any SLF4J logger is retrieved! For our particular application it is unreasonable to ask the consumer to manually make some call or set some system property in order to have our specialized context selector be installed, and thus the current approach is too inflexible for our needs.

      A much smoother approach would be to use a Java service loader---and in fact com.qos.logback.classic.spi.Configurator already does exactly that for configuration. But this facility is not used for context selector installation.

      A service loader facility could easily be added transparently to the current logic, with full backwards compatibility! Here is the current relevant code in ContextSelectorStaticBinder.init((LoggerContext defaultLoggerContext, Object key):

      String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
      if (contextSelectorStr == null) {
          contextSelector = new DefaultContextSelector(defaultLoggerContext);
      } else if (contextSelectorStr.equals("JNDI")) {
          // if jndi is specified, let's use the appropriate class
          contextSelector = new ContextJNDISelector(defaultLoggerContext);
      } else {
          contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
      }
      

      All that needs to be done is to add a check for a service loader if no "logback.ContextSelector" system property before falling back to use a DefaultContextSelector. It would look something like this:

      String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
      if (contextSelectorStr == null) {
          contextSelector = new DefaultContextSelector(defaultLoggerContext);
      } else if (contextSelectorStr.equals("JNDI")) {
          // if jndi is specified, let's use the appropriate class
          contextSelector = new ContextJNDISelector(defaultLoggerContext);
      } else {
      	Iterator<ContextSelectorProvider> contextSelectorProviders =
      			ServiceLoader.load(ContextSelectorProvider.class);
      	if(contextSelectorProviders.hasNext()) {
      		contextSelector = contextSelectorProviders.next().createContextSelector(defaultLoggerContect);
      	} else {
      		contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
      	}
      }
      

      Here ch.qos.logback.classic.selector.ContextSelectorProvider would be a simple interface like this:

      public interface ContextSelectorProvider {
          public ContextSelector createContextSelector(LoggerContext defaultLoggerContext);
      }
      

      Now any library could provide a META-INF/services/ch.qos.logback.classic.selector.ContextSelectorProvider file with the name of the specific ContextSelectorProvider. By virtue of including this library, the custom ContextSelectorProvider would provide the custom ContextSelector to be installed as the default — no one would have to manually set a system property. If someone did happen to set the system property, a default context selector would be installed according to the current behavior, so this change would be completely backwards compatible.

      This tiny change would really help us out with our project, and I don't even mind writing the code. (In fact this issue probably contains all the code the is needed.) Is this something you would consider including? Thanks.

        Attachments

          Activity

            People

            • Assignee:
              logback-dev Logback dev list
              Reporter:
              garretwilson Garret Wilson
            • Votes:
              2 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated: