Hi everyone,
As part of the 2022-06 release, the SWTBot project will do a major release of version 4.0.0. In this version, SWTBot has removed its use of org.apache.log4j and is instead using the org.slf4j.api facade for its logging. See Bug 578065 for this change.
I am trying to adapt project Trace Compass to this change and I am having some issues, perhaps someone can help and/or this information will be useful to others.
Trace Compass was using the log4j Logger for its SWTBot tests, both to add its own messages and to rely on SWTBot's log4j debug messages.
The first issue when switching to SWTBot 4.0.0 is that the SWTBot update site no longer contains org.apache.log4j. So I first tried to change our target files to add org.apache.log4j from the Orbit update site. I also had to add an Import-Package dependency to org.slf4j in our swtbot.tests plug-ins MANIFEST.MF, to avoid 'missing ... indirectly referenced from .class files' errors, due to it now being used by SWTBot classes.
While the build was now successful, there was no debug logging from the SWTBot components because there was no slf4j binding installed. The console would show:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
At this point, needing a quick fix, I changed our target files to use the previous SWTBot release update site (3.1.0) that still uses org.apache.log4j, instead of using the latest snapshot (4.0.0) update site.
So, now I am trying to do a proper migration to SLF4J.
I updated our targets to use the SWTBot 4.0.0 snapshot and added org.slf4j.api Import-Package dependency to the swtbot.tests plug-ins MANIFEST.MF. I removed all use of org.apache.log4j.Logger in our test code and replaced it with getting a logger from the org.slf4j.LoggerFactory.
Of course there was no logging when running the tests because there was no binding installed.
~~~ org.slf4j.binding.simple ~~~
I first tried to add org.slf4j.binding.simple from Orbit to our targets.
In my JUnit Plug-In Test Debug Configuration I can see that org.slf4j.api and org.slf4j.binding.simple plug-ins are added. When I ran the test it did not log anything because the default log level was INFO.
So I added this line as static initializer in my test code:
System.setProperty("org.slf4j.simpleLogger.log.org.eclipse", "debug");
This works because it covers both our test code classes org.eclipse.tracecompass.* and the SWTBot classes org.eclipse.swtbot.*. But it's a bit clunky, and I wanted to configure other settings, like setting the log file to System.out instead of System.err.
I tried creating a simplelogger.properties file in my test plug-in, adding it to the Binary Build in the Build Configuration tab and adding it's location to the Classpath in the Runtime tab.
But when I run the test, the SimpleLoggerConfiguration code tries to load the properties file from:
Thread.currentThread().getContextClassLoader() (java.lang)
The current thread is PluginTestRunner.
This returns a ContextFinder (org.eclipse.osgi)
But the ContextFinder.basicFindClassLoaders() implementation returns the first in the list that is a Bundle class loader, and that's the one at index [5] which is for org.slf4j.api bundle. So it never gets to index [19] which is our tmf.ui.swtbot.tests bundle that has the simplelogger.properties resource.
So it doesn't find our test plug-in's simplelogger.properties file and I can't configure the logger.
I don't really understand how class loaders work so at this point I gave up on SimpleLogger.
~~~ org.slf4j.binding.log4j12 ~~~
I secondly tried to add org.slf4j.binding.log4j12 from Orbit to our targets.
In my JUnit Plug-In Test Debug Configuration I can see that org.slf4j.api and org.slf4j.binding.log4j12 plug-ins are added.
But org.slf4j.binding.log4j12 requires org.apache.log4j package and this is found in org.slf4j.log4j, so this plug-in also needs to be added.
And now when I run my test, the org.apache.log4j.Log4jLoggerFactory static initializer checks that there is no org.slf4j.impl.Log4jLoggerFactory loaded, and since there is, it throws the following exception and aborts the test:
SLF4J: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError.
SLF4J: See also
http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
Exception in thread "Plug-in Tests Runner" java.lang.ExceptionInInitializerError
at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:72)
Caused by: java.lang.IllegalStateException
at org.slf4j.impl.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:54)
It seems they are both mutually exclusive but I'm confused because org.apache.log4j is required by org.slf4j.binding.log4j12 ?!
~~~
So at this point I realize that I don't know what I'm doing wrong, and/or I don't know what I'm doing. Help?
I would like to figure this out so that we can help other projects that need to do a similar migration when they switch to SWTBot 4.0.0 after the upcoming release.
Thanks,
Patrick
SWTBot lead & Trace Compass committer