Cannot create tests because of bad logback file configuration

Hi,

I tried to generate unit tests for my java project, but i got an error when the plugin tried to intialize my java class, need a specific log4j configuration file :
“(…) threw an exception emitterName is not set in the log4j2 configuration file (/tmp/diffblue/config/logback-to-local-file.xml), cannot continue while trying to load it. We need to load the class to generate a test.”

“emitterName” is a configuration property that my log library need to work, and i see in the logback-to-local-file.xml file that this property is not present.
So my question is : how this file (logback-to-local-file.xml) is generated by cover plugin ? can i configure the file content somewhere ? or else where can i put my log4j configuration file in order to be used by the cover plugin ?

Thank you in advance for your help.

Frederic

Hi Frederic

Thanks for reporting this – we’re investigating.

Mathew

Hi Frederic,

In order to diagnose this further it would be helpful to understand your environment a little better.
So if I may:

  • I am assuming that your project uses log4j2, if so what version are you using?
  • What JDK version are you using?
  • What version of IntelliJ are you using?
  • Is there any kind of stacktrace associated with the error that you can send?

Finally, I understand sharing code may be hard - but if you are able to describe how your code is using log4j2 that would be really helpful!

Many thanks!

andy

Hi Andy

Thanks for your support.

My environment is :

  • log4j 2.8.2
  • JDK 1.8
  • intelliJ 2020.2
  • stacktrace : Unable to load class: static initializer failed.: The static initializer *xxx.xxx.xxx.xxx*.LoggerFactory.getLog4JProperty(LoggerFactory.java:237) threw an exception emitterName is not set in the log4j2 configuration file (/tmp/diffblue/config/logback-to-local-file.xml), cannot continue while trying to load it. We need to load the class to generate a test.

To explain a bit more my context, i can say that the java class “LoggerFactory” is included in a library developed by the Team who manage the PAAS solution where my application is hosted, and so i don’t have the source code (but i probably could ask them to share it with me if necessary). This external library uses Log4j.

The error comes from the fact that the ‘emittername’ field is not known to log4j by default, but was added by this custom library.
But what I’m wondering is how the Cover plugin generates the default logback-to-local-file.xml file (which obviously is used instead of log4j2.xml), and if there is a way to pass it a file correctly configured for my context?

Thanks,
Frederic

Thanks Frederic, it would be really helpful to see the LoggerFactory source if that’s possible.

It’s not possible to configure logback-to-local-file.xml as we have to be very careful about not mixing user code with cover code, but is a mystery as to why it is being picked up since this is not a log4j configuration file at all - it is for logback.

Typically these locations are found on the classpath via a property setting - do you know if you are setting any config property for log4j2?

Regards

andy

Hi Andy

Here is part of the LoggerFactory class :

public class CustomLoggerFactory {
    public static final String LOG4J_VAR_EMITTER_NAME = "emitterName";
    public static final String LOG4J_VAR_EMITTER_TYPE = "emitterType";
    public static final String LOG4J_VAR_EMITTER_VERSION = "emitterVersion";
    public static final String LOG4J_VAR_Custom_SERVICE = "emitterCodeService";
    public static final String LOG4J_CONF_HOST_NAME = "hostName";
    public static final String LOG4J_VAR_CUSTOM_MAPPING_VERSION = "customMappingVersion";
    public static final String LOG4J_VAR_COMMON_MAPPING_VERSION = "commonMappingVersion";
    public static final String LOG4J_VAR_CONTEXT_CLASS = "contextClass";
    private static final ThreadLocal<CustomLoggerContext> Custom_LOGGER_CONTEXT_THREAD_LOCAL = new ThreadLocal<CustomLoggerContext>() {
        protected CustomLoggerContext initialValue() {
            return new CustomLoggerContext();
        }
    };
(...)
    private CustomLoggerFactory() {
    }

    private static void initProperties() {
        LoggerContext context = (LoggerContext)LogManager.getContext(Thread.currentThread().getContextClassLoader(), false);
        Configuration configuration = context.getConfiguration();
        localHost = (String)configuration.getProperties().get("hostName");
        StrLookup variableResolver = configuration.getStrSubstitutor().getVariableResolver();
        log4jConfigLocation = configuration.getConfigurationSource().getLocation();
        String emitterName = getLog4JProperty(variableResolver, "emitterName");
        String emitterType = getLog4JProperty(variableResolver, "emitterType");
        String emitterVersion = getLog4JProperty(variableResolver, "emitterVersion");
        String mappingCommonVersion = getLog4JProperty(variableResolver, "commonMappingVersion");
        String mappingCustomVersion = getLog4JProperty(variableResolver, "customMappingVersion", false);
(...)

    private static String getLog4JProperty(StrLookup variableResolver, String key, boolean isMandatory) {
        String res = variableResolver.lookup(key);
        if (res == null && isMandatory) {
            throw new LoggerRuntimeException(String.format("%s is not set in the log4j2 configuration file (%s), cannot continue", key, log4jConfigLocation));
        } else {
            return res;
        }
    }
(...)

As you can see, it seems that the ‘log4jConfigLocation’ was initialized with the logback configuration file…

And then here is the logback file generated by diffblue plugin :

<configuration debug="false">
<!-- Daily rolling file appender -->
<appender name="file-logger"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>{java.io.tmpdir}/diffblue/log/cover-plugin-server.log&lt;/File&gt; &lt;rollingPolicy class=&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&quot;&gt; &lt;FileNamePattern&gt; {java.io.tmpdir}/diffblue/log/cover-plugin-server.%d{yyyy-MM-dd}.gz
</FileNamePattern>
</rollingPolicy>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%level][%thread][%logger{16}] %message%n</pattern>
</encoder>
<!-- set level="DEBUG" or "TRACE" or "INFO" to enable more verbose output -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<!-- set level="DEBUG" or "TRACE" or "INFO" to enable more verbose output -->
<logger name="com.diffblue" additivity="false" level="DEBUG">
<appender-ref ref="file-logger"/>
</logger>
<!-- disable all logging that does not come from Diffblue code -->
<!-- <root level="OFF"/> -->
</configuration>

Thank you for help !

Frederic

Thanks Frederic - that helps. We are going to run some experiments our side and will get back to you.

Andy

Hi @RedDwarf we’ve investigated this and its quite difficult to fix because of the wide variety of logging arrangements that customers can have. We’re looking at ways we can give you more control so that you at least get tests.

Andy

Okay thank you Andy. Your plugin looks great so I’m looking forward to it :slight_smile:

Frederic

Hi Frederic,

Thank you for adding your code. I think I’ve managed to reproduce your issue, but I’ve had to make some assumptions.

I’ve assumed that your configuration is either not in a file called log4j2.xml or is not available on the classpath (i.e. in your src/main/resources folder). Could you try copying your configuration to that folder please.

Thanks in advance,
Simon

Hi,

I cannot test the plugin any more for the moment, i get this error :
Plugin ‘Diffblue Cover Community Edition - Unit Test Generator’ (version ‘2020.11.04-eval’) is not compatible with the current version of the IDE, because it requires build 202.* or older but the current build is IC-203.5981.155

Do i have to update my version of IntelliJ ? i don’t understand why it would have installed a wrong version of the plugin… :thinking:

And for your tip, i can tell you that i have already put my log4j2.xml file in the src/main/resources folder (i’ve tried src/test/resources also). I’ll test it again.

Thank you,
Frederic

Hi @RedDwarf
It looks like you have upgraded your version of IntelliJ to 2020.3. We support this version in our plugin version 2020.12.01 or later which you should be able to upgrade to.
Regards
Andy

Hi Frederic,

Can I ask, is this a Spring Boot/Spring Framework project?

Cheers,
Simon

Hi Andy, you were right, i upgraded the plugin to the newest version and it works again. Thank you.

But now i see less logs than with the previous version :
22:12:29.820 [INFO][pool-4-thread-1][c.d.i.p.ui.a] Commencing test creation - Analyzing 2 methods in total
22:12:29.840 [INFO][pool-4-thread-1][c.d.i.p.ui.a] Logfile location - /tmp/diffblue/log/cover-plugin.log
22:13:00.046 [INFO][pool-4-thread-1][c.d.i.p.ui.a] Creating tests for method - diffblue-method-reference:com.xxx.xxx.xxx.poc.kafkaconsumer.ws.controller.ManageConsumerController.startListener:()Lorg/springframework/http/ResponseEntity;ManageConsumerController.startListener
22:13:00.096 [WARN][pool-4-thread-1][c.d.i.p.ui.a] Unable to load class: static initializer failed. - Please check that the class can be loaded without any errors in the environment where you create and run your tests. See https://diff.blue/R006
22:13:00.153 [INFO][pool-4-thread-1][c.d.i.p.ui.a] Creating tests for method "diffblue-method-reference:com.xxx.xxx.xxx.poc.kafkaconsumer.ws.controller.ManageConsumerController.stopListener:()Lorg/springframework/http/ResponseEntity;
22:13:00.161 [WARN][pool-4-thread-1][c.d.i.p.ui.a] Unable to load class. - Please check that your test runtime classpath is complete; in particular, check that you do not have any dependency exclusions configured. See https://diff.blue/R005
22:13:00.290 [INFO][ApplicationImpl pooled thread 19][c.d.i.p.ui.a] Test creation complete - Created 0 tests in total for 0 methods from 0 source classes

Frederic

Hi Simon, yes it’s a spring boot project indeed.

We have excluded default spring boot logger in our parent pom :

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>${spring-boot.version}</version>
			<type>pom</type>
			<scope>import</scope>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

and then we have added log4j dependencies :

		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-bom</artifactId>
			<version>${log4j2.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-to-slf4j</artifactId>
			<version>${log4j2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>${log4j2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>${log4j2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-web</artifactId>
			<version>${log4j2.version}</version>
		</dependency>

Log4j2.version = 2.8.2

Frederic

Thanks Frederic,

I think we have tracked down what is happening and we no longer set the logger.config property; this will be released in the next version due out next week. Thank you for your patience.

Cheers,
Simon

Ok thank you Simon, it’s great if you found a solution! No problem for patience, I know very well that it is complicated to adapt to different development contexts, especially since mine is particular.

Cheers,
Frederic