Uploaded image for project: 'JBoss Logging'
  1. JBoss Logging
  2. JBLOGGING-111

LoggerProvider configured with new ServiceLoader crash

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • 3.2.0.Final
    • 3.2.0.Beta1
    • None
    • None
      • Add a serviceLoader file in the META-INF of a jar using a different LoggerProvider than the default.
      • Load an application using the JBoss logging (ex. Hibernate based application)

      There is a new feature in the beta which uses the ServiceLoader to specify a LoggerProvider to be used by JBoss Logging.

      org.jboss.logging.LoggerProviders snippet :

      // Next try for a service provider
              try {
                  final ServiceLoader<LoggerProvider> loader = ServiceLoader.load(LoggerProvider.class, cl);
                  if (loader.iterator().hasNext()) {
                      return loader.iterator().next();
                  }
              } catch (Throwable ignore) {
                  // TODO consider printing the stack trace as it should only happen once
              }
      

      When you try to configure a provider (ex. org.jboss.logging.Slf4jLoggerProvider), the ServiceLoader crash silently and ignore the provider.

      java.util.ServiceConfigurationError: org.jboss.logging.LoggerProvider: Provider org.jboss.logging.Slf4jLoggerProvider could not be instantiated: java.lang.IllegalAccessException: Class java.util.ServiceLoader$LazyIterator can not access a member of class org.jboss.logging.Slf4jLoggerProvider with modifiers ""
      	at java.util.ServiceLoader.fail(ServiceLoader.java:207)
      	at java.util.ServiceLoader.access$100(ServiceLoader.java:164)
      	at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:353)
      	at java.util.ServiceLoader$1.next(ServiceLoader.java:421)
      	at org.jboss.logging.LoggerProviders.findProvider(LoggerProviders.java:70)
      	at org.jboss.logging.LoggerProviders.find(LoggerProviders.java:32)
      	at org.jboss.logging.LoggerProviders.<clinit>(LoggerProviders.java:29)
      	at org.jboss.logging.Logger.getLogger(Logger.java:2177)
      	at org.jboss.logging.Logger$1.run(Logger.java:2277)
      	at java.security.AccessController.doPrivileged(Native Method)
      	at org.jboss.logging.Logger.getMessageLogger(Logger.java:2241)
      	at org.jboss.logging.Logger.getMessageLogger(Logger.java:2228)
      	at org.hibernate.cfg.Configuration.<clinit>(Configuration.java:176)
              ...
      

      This is caused by the fact that all JBoss providers are not public classes and are instead package classes.

            [JBLOGGING-111] LoggerProvider configured with new ServiceLoader crash

            Thank you, dlloyd@redhat.com. You are correct that we are looking for another means of logger configuration. I have created an enhancement request (JBLOGGING-133) for this discussion.

            Steven Sharp (Inactive) added a comment - Thank you, dlloyd@redhat.com . You are correct that we are looking for another means of logger configuration. I have created an enhancement request ( JBLOGGING-133 ) for this discussion.

            David Lloyd added a comment -

            It sounds more like what you want is a way to use some configuration other than a system property in order to choose one of the preexisting providers. You are trying to use service loader as this configuration mechanism, though that's not really an appropriate solution for choosing a provider because it's meant for providing a provider.

            I think it's reasonable to open a new JIRA which is a feature request to support the specification of a preexisting provider on a class loader (aka per-application) basis. I don't think it's a good idea to make implementation classes public in order to facilitate that feature. But that discussion can be had in the new JIRA.

            David Lloyd added a comment - It sounds more like what you want is a way to use some configuration other than a system property in order to choose one of the preexisting providers. You are trying to use service loader as this configuration mechanism, though that's not really an appropriate solution for choosing a provider because it's meant for providing a provider. I think it's reasonable to open a new JIRA which is a feature request to support the specification of a preexisting provider on a class loader (aka per-application) basis. I don't think it's a good idea to make implementation classes public in order to facilitate that feature. But that discussion can be had in the new JIRA.

            jperkins-rhn, you mentioned that "The SPI is really meant to enable users to provide their own provider." To do this, you have to write your provider from scratch as all the abstract implementations are package private as well. The only documentation I could find for this feature, other than the code, was the hibernate documentation which is incorrect. In the code, the exception (ServiceConfigurationError) is silently eaten leaving developers wondering why their logging implementation of choice doesn't work.

            When we deploy our application, adding a system property is not possible. An ugly workaround is to create a delegate class in the org.jboss.logging package, but that is unmaintainable. The implementation classes should be public so the logging implementation can be chosen via the ServiceLoader. At least expose the result of the ServiceLoader.load() method so developers will know that it failed. Either this issue should be reopened or a new one should be created.

            Steven Sharp (Inactive) added a comment - jperkins-rhn , you mentioned that "The SPI is really meant to enable users to provide their own provider." To do this, you have to write your provider from scratch as all the abstract implementations are package private as well. The only documentation I could find for this feature, other than the code, was the hibernate documentation which is incorrect. In the code, the exception ( ServiceConfigurationError ) is silently eaten leaving developers wondering why their logging implementation of choice doesn't work. When we deploy our application, adding a system property is not possible. An ugly workaround is to create a delegate class in the org.jboss.logging package, but that is unmaintainable. The implementation classes should be public so the logging implementation can be chosen via the ServiceLoader. At least expose the result of the ServiceLoader.load() method so developers will know that it failed. Either this issue should be reopened or a new one should be created.

            Yeah it's in 3.2.0.Final. Fixed and thanks for pointing it out.

            James Perkins added a comment - Yeah it's in 3.2.0.Final. Fixed and thanks for pointing it out.

            I see there's a pull request sent, and looking at the pull request itself, it looks like it has been merged.
            Is it fixed?

            Ghislain Nadeau (Inactive) added a comment - I see there's a pull request sent, and looking at the pull request itself, it looks like it has been merged. Is it fixed?

            I also encountered this problem. I want to force the use of SLF4J, but system properties for this is a really bad idea. The service locator technique would be OK (although not optimal), but as of JBoss Logging 3.2.0 this doesn't work for the exact reason written in the original report.
            I really can't understand the concerns about making the default LoggerProvider implementations public...

            Mauro Molinari (Inactive) added a comment - I also encountered this problem. I want to force the use of SLF4J, but system properties for this is a really bad idea. The service locator technique would be OK (although not optimal), but as of JBoss Logging 3.2.0 this doesn't work for the exact reason written in the original report. I really can't understand the concerns about making the default LoggerProvider implementations public...

            I think the fix mentioned by David should be implemented, but my problem is that I want to force SLF4J without developing custom code. I want to use the LoggerProvider implemented by JBoss. But there is no way to do it right now.

            Frederic Allard (Inactive) added a comment - I think the fix mentioned by David should be implemented, but my problem is that I want to force SLF4J without developing custom code. I want to use the LoggerProvider implemented by JBoss. But there is no way to do it right now.

            The root cause here of the actual exception is simply that the service loader loop needs to be modified to look something like this:

            Iterator<LoggerProvider> it = serviceLoader.blah();
            for (;;) try {
                if (! it.hasNext()) break;
                LoggerProvider p = it.next();
                if (p is okay) break;
            } catch (ServiceConfigurationError ignored) {}
            

            The key is to put hasNext() inside a try/catch which in turn is inside the loop. This will skip failed providers and move on to workable ones.

            David Lloyd added a comment - The root cause here of the actual exception is simply that the service loader loop needs to be modified to look something like this: Iterator<LoggerProvider> it = serviceLoader.blah(); for (;;) try { if (! it.hasNext()) break ; LoggerProvider p = it.next(); if (p is okay) break ; } catch (ServiceConfigurationError ignored) {} The key is to put hasNext() inside a try/catch which in turn is inside the loop. This will skip failed providers and move on to workable ones.

            I can explain my use case and it's probably not the best solution to use the service loader explicitly, but I think you won't be able to fix our problem for backward compatibility on your side.

            We have different legacy services using log4j-1.2.6 which is not supported by jboss logging. In our recent services, we use hibernate and slf4j to decouple the log4j-1.2.6 from our services. This way, we will be able to upgrade in the future the version of log4j. The tricky part is that log4j is deployed in the lib directory of the production Web server and we need to use it for the enterprise global logging configuration (homemade console).

            The problem is in your priority to select the loggerProvider. You begin by looking if the Log4jFactory is in the classloader. And of course in our case, it will find log4j because it's provided by the server. But we use in reality SLF4J, which supports log4j-1.2.6 by putting the trace level logging in the debug level for example.

            It would be better if you started by looking for SLF4J because it's the abstraction layer.

            That way in our case, Hibernate would defer to slf4j instead of log4j and it would work without exception like : "TRACE not found."

            Frederic Allard (Inactive) added a comment - - edited I can explain my use case and it's probably not the best solution to use the service loader explicitly, but I think you won't be able to fix our problem for backward compatibility on your side. We have different legacy services using log4j-1.2.6 which is not supported by jboss logging. In our recent services, we use hibernate and slf4j to decouple the log4j-1.2.6 from our services. This way, we will be able to upgrade in the future the version of log4j. The tricky part is that log4j is deployed in the lib directory of the production Web server and we need to use it for the enterprise global logging configuration (homemade console). The problem is in your priority to select the loggerProvider. You begin by looking if the Log4jFactory is in the classloader. And of course in our case, it will find log4j because it's provided by the server. But we use in reality SLF4J, which supports log4j-1.2.6 by putting the trace level logging in the debug level for example. It would be better if you started by looking for SLF4J because it's the abstraction layer. That way in our case, Hibernate would defer to slf4j instead of log4j and it would work without exception like : "TRACE not found."

            Yeah, true. I'm a bit hesitant to make these public though. I'll reopen and see if I can think of a better way. The SPI is really meant to enable users to provide their own provider.

            James Perkins added a comment - Yeah, true. I'm a bit hesitant to make these public though. I'll reopen and see if I can think of a better way. The SPI is really meant to enable users to provide their own provider.

            A system property is not a good solution for a enterprise application packaged as a war or ear file.

            Frederic Allard (Inactive) added a comment - A system property is not a good solution for a enterprise application packaged as a war or ear file.

            The internal providers are not meant to be exposed and therefore as you see won't work with a service loader. If you need a provided provider you can explicitly set the org.jboss.logging.provider system property. The allowed values are:

            • jboss
            • jdk
            • log4j2
            • log4j
            • slf4j

            The service loader should only be used if one of the above use cases does not fit your needs.

            James Perkins added a comment - The internal providers are not meant to be exposed and therefore as you see won't work with a service loader. If you need a provided provider you can explicitly set the org.jboss.logging.provider system property. The allowed values are: jboss jdk log4j2 log4j slf4j The service loader should only be used if one of the above use cases does not fit your needs.

              jperkins-rhn James Perkins
              psychobaatezu Frederic Allard (Inactive)
              Votes:
              2 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: