Repository: Unleash/unleash-client-java
Pull request #186 (2023-01-02 13:36:02)

Task/detect multiple instantiations
## What
This PR introduces a `ConcurrentHashMap<String, LongAdder>` for keeping track of the number of instantiations per client identifier (set to be apiKey, appName, instanceId). If the client instantiates Unleash with the same identifier twice a log ERROR line will be printed. This should allow us to more easily help customers using this SDK that have problems with their applications connecting way too often.

## Discussion
It will be worth it to replace the DefaultUnleash constructor with a builder that keeps track of configs seen and static fields with already instantiated clients to hopefully avoid the excessive instantiation altogether, but that would require making the DefaultUnleash constructors private and would constitute a major release. We should plan for including the builder + constructor access change for the next major release. Which should come when we implement dependent feature toggles.

## Interesting files
DefaultUnleash.java
DefaultUnleashTest.java


Patch:
diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml
index 1bc2b341..8dda5dc4 100644
--- a/.github/workflows/pull_requests.yml
+++ b/.github/workflows/pull_requests.yml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        version: [8,11]
+        version: [8,11,17]
     steps:
       - name: Checkout
         uses: actions/checkout@v3
@@ -19,19 +19,3 @@ jobs:
           distribution: 'temurin'
       - name: Build, test, coverage
         run: ./mvnw clean test jacoco:report coveralls:report
-  java17:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v3
-      - name: Setup Java
-        uses: actions/setup-java@v3
-        with:
-          java-version: 17
-          cache: 'maven'
-          distribution: 'temurin'
-      - name: Build, test, coverage
-        run: ./mvnw clean test jacoco:report coveralls:report
-        env:
-          MAVEN_OPTS: "--add-opens java.base/java.net=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
-
diff --git a/README.md b/README.md
index 9479572e..e397d323 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,20 @@ UnleashConfig config = UnleashConfig.builder()
 Unleash unleash = new DefaultUnleash(config);
 ```
 
+**Note:** you should only create one unleash instance per configuration. To help detecting misconfigurations, an error message will appear if you create multiple Unleash instances from the same configuration values. If you want your application to fail in this situation you can use the constructor:
+```java
+public DefaultUnleash(
+        UnleashConfig unleashConfig,
+        IFeatureRepository featureRepository,
+        Map<String, Strategy> strategyMap,
+        UnleashContextProvider contextProvider,
+        EventDispatcher eventDispatcher,
+        UnleashMetricService metricService,
+        boolean failOnMultipleInstantiations) {
+    // ...
+}
+```
+
 ### Awesome feature toggle API
 
 It is really simple to use unleash.
diff --git a/pom.xml b/pom.xml
index 2d674788..154de385 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,9 +140,15 @@
         </dependency>
 
         <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-slf4j-impl</artifactId>
-            <version>${version.log4j2}</version>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.3.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.3.5</version>
             <scope>test</scope>
         </dependency>
 
@@ -156,15 +162,20 @@
             </resource>
         </resources>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.3.0</version>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-site-plugin</artifactId>
-                <version>3.10.0</version>
+                <version>3.12.0</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.10.0</version>
+                <version>3.10.1</version>
                 <configuration>
                     <source>1.8</source>
                     <target>1.8</target>
@@ -173,7 +184,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M5</version>
+                <version>3.0.0-M7</version>
                 <configuration>
                     <forkCount>1</forkCount>
                 </configuration>
@@ -181,7 +192,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>3.2.2</version>
+                <version>3.3.0</version>
                 <configuration>
                     <archive>
                         <manifest>
@@ -194,7 +205,7 @@
             <plugin>
                 <groupId>org.sonatype.plugins</groupId>
                 <artifactId>nexus-staging-maven-plugin</artifactId>
-                <version>1.6.8</version>
+                <version>1.6.13</version>
                 <extensions>true</extensions>
                 <configuration>
                     <serverId>ossrh</serverId>
@@ -207,7 +218,7 @@
             <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
-                <version>0.8.7</version>
+                <version>0.8.8</version>
                 <executions>
                     <execution>
                         <id>prepare-agent</id>
@@ -284,7 +295,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>3.2.0</version>
+                <version>3.3.0</version>
                 <executions>
                     <execution>
                         <id>move-client-specification</id>
@@ -313,7 +324,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-release-plugin</artifactId>
-                <version>2.5.3</version>
+                <version>3.0.0-M6</version>
                 <configuration>
                     <mavenExecutorId>forked-path</mavenExecutorId>
                     <useReleaseProfile>false</useReleaseProfile>
@@ -323,7 +334,7 @@
             <plugin>
                 <groupId>com.diffplug.spotless</groupId>
                 <artifactId>spotless-maven-plugin</artifactId>
-                <version>2.27.1</version>
+                <version>2.28.0</version>
                 <configuration>
                     <formats>
                         <format>
@@ -374,17 +385,17 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-project-info-reports-plugin</artifactId>
-                <version>3.2.1</version>
+                <version>3.4.1</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>3.3.1</version>
+                <version>3.4.1</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M5</version>
+                <version>3.0.0-M7</version>
             </plugin>
         </plugins>
     </reporting>
diff --git a/src/main/java/io/getunleash/DefaultUnleash.java b/src/main/java/io/getunleash/DefaultUnleash.java
index c9d8d5f6..f0a2e51c 100644
--- a/src/main/java/io/getunleash/DefaultUnleash.java
+++ b/src/main/java/io/getunleash/DefaultUnleash.java
@@ -15,6 +15,8 @@ import io.getunleash.util.ConstraintMerger;
 import io.getunleash.util.UnleashConfig;
 import io.getunleash.variant.VariantUtil;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
 import java.util.function.BiPredicate;
 import java.util.stream.Collectors;
 import org.slf4j.Logger;
@@ -22,6 +24,8 @@ import org.slf4j.LoggerFactory;
 
 public class DefaultUnleash implements Unleash {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUnleash.class);
+
+    private static ConcurrentHashMap<String, LongAdder> initCounts = new ConcurrentHashMap<>();
     private static final List<Strategy> BUILTIN_STRATEGIES =
             Arrays.asList(
                     new DefaultStrategy(),
@@ -60,7 +64,8 @@ public class DefaultUnleash implements Unleash {
                 buildStrategyMap(strategies),
                 unleashConfig.getContextProvider(),
                 new EventDispatcher(unleashConfig),
-                new UnleashMetricServiceImpl(unleashConfig, unleashConfig.getScheduledExecutor()));
+                new UnleashMetricServiceImpl(unleashConfig, unleashConfig.getScheduledExecutor()),
+                false);
     }
 
     // Visible for testing
@@ -71,6 +76,24 @@ public class DefaultUnleash implements Unleash {
             UnleashContextProvider contextProvider,
             EventDispatcher eventDispatcher,
             UnleashMetricService metricService) {
+        this(
+                unleashConfig,
+                featureRepository,
+                strategyMap,
+                contextProvider,
+                eventDispatcher,
+                metricService,
+                false);
+    }
+
+    public DefaultUnleash(
+            UnleashConfig unleashConfig,
+            IFeatureRepository featureRepository,
+            Map<String, Strategy> strategyMap,
+            UnleashContextProvider contextProvider,
+            EventDispatcher eventDispatcher,
+            UnleashMetricService metricService,
+            boolean failOnMultipleInstantiations) {
         this.config = unleashConfig;
         this.featureRepository = featureRepository;
         this.strategyMap = strategyMap;
@@ -78,6 +101,24 @@ public class DefaultUnleash implements Unleash {
         this.eventDispatcher = eventDispatcher;
         this.metricService = metricService;
         metricService.register(strategyMap.keySet());
+        this.initCounts.compute(
+                config.getClientIdentifier(),
+                (key, inits) -> {
+                    if (inits != null) {
+                        String error =
+                                String.format(
+                                        "You already have %d clients for Unleash Configuration [%s] running. Please double check your code where you are instantiating the Unleash SDK",
+                                        inits.sum(), key);
+                        if (failOnMultipleInstantiations) {
+                            throw new RuntimeException(error);
+                        } else {
+                            LOGGER.error(error);
+                        }
+                    }
+                    LongAdder a = inits == null ? new LongAdder() : inits;
+                    a.increment();
+                    return a;
+                });
     }
 
     @Override
diff --git a/src/main/java/io/getunleash/FakeUnleash.java b/src/main/java/io/getunleash/FakeUnleash.java
index 9b4e384c..477d4087 100644
--- a/src/main/java/io/getunleash/FakeUnleash.java
+++ b/src/main/java/io/getunleash/FakeUnleash.java
@@ -3,6 +3,7 @@ package io.getunleash;
 import static java.util.Collections.emptyList;
 
 import io.getunleash.lang.Nullable;
+
 import java.util.*;
 import java.util.function.BiPredicate;
 import java.util.stream.Collectors;
@@ -10,33 +11,34 @@ import java.util.stream.Collectors;
 public class FakeUnleash implements Unleash {
     private boolean enableAll = false;
     private boolean disableAll = false;
+    private Map<String, Boolean> excludedFeatures = new HashMap<>();
     private Map<String, FeatureToggle> features = new HashMap<>();
     private Map<String, Variant> variants = new HashMap<>();
 
     @Override
     public boolean isEnabled(String toggleName, boolean defaultSetting) {
         if (enableAll) {
-            return true;
+            return excludedFeatures.getOrDefault(toggleName, true);
         } else if (disableAll) {
-            return false;
+            return excludedFeatures.getOrDefault(toggleName, false);
         } else {
             return more().getFeatureToggleDefinition(toggleName)
-                    .map(FeatureToggle::isEnabled)
-                    .orElse(defaultSetting);
+                .map(FeatureToggle::isEnabled)
+                .orElse(defaultSetting);
         }
     }
 
     @Override
     public boolean isEnabled(
-            String toggleName,
-            UnleashContext context,
-            BiPredicate<String, UnleashContext> fallbackAction) {
+        String toggleName,
+        UnleashContext context,
+        BiPredicate<String, UnleashContext> fallbackAction) {
         return isEnabled(toggleName, fallbackAction);
     }
 
     @Override
     public boolean isEnabled(
-            String toggleName, BiPredicate<String, UnleashContext> fallbackAction) {
+        String toggleName, BiPredicate<String, UnleashContext> fallbackAction) {
         if (!features.containsKey(toggleName)) {
             return fallbackAction.test(toggleName, UnleashContext.builder().build());
         }
@@ -80,18 +82,35 @@ public class FakeUnleash implements Unleash {
     public void enableAll() {
         disableAll = false;
         enableAll = true;
+        excludedFeatures.clear();
         features.clear();
     }
 
+    public void enableAllExcept(String... excludedFeatures) {
+        enableAll();
+        for (String toggle : excludedFeatures) {
+            this.excludedFeatures.put(toggle, false);
+        }
+    }
+
     public void disableAll() {
         disableAll = true;
         enableAll = false;
+        excludedFeatures.clear();
         features.clear();
     }
 
+    public void disableAllExcept(String... excludedFeatures) {
+        disableAll();
+        for (String toggle : excludedFeatures) {
+            this.excludedFeatures.put(toggle, true);
+        }
+    }
+
     public void resetAll() {
         disableAll = false;
         enableAll = false;
+        excludedFeatures.clear();
         features.clear();
         variants.clear();
     }
diff --git a/src/main/java/io/getunleash/Unleash.java b/src/main/java/io/getunleash/Unleash.java
index a3e119c7..6e525979 100644
--- a/src/main/java/io/getunleash/Unleash.java
+++ b/src/main/java/io/getunleash/Unleash.java
@@ -20,7 +20,8 @@ public interface Unleash {
         return isEnabled(toggleName, context, (n, c) -> defaultSetting);
     }
 
-    default boolean isEnabled(String toggleName, BiPredicate<String, UnleashContext> fallbackAction) {
+    default boolean isEnabled(
+            String toggleName, BiPredicate<String, UnleashContext> fallbackAction) {
         return isEnabled(toggleName, UnleashContext.builder().build(), fallbackAction);
     }
 
diff --git a/src/main/java/io/getunleash/util/UnleashConfig.java b/src/main/java/io/getunleash/util/UnleashConfig.java
index fbd75895..6f8e2cfc 100644
--- a/src/main/java/io/getunleash/util/UnleashConfig.java
+++ b/src/main/java/io/getunleash/util/UnleashConfig.java
@@ -261,6 +261,22 @@ public class UnleashConfig {
         return this.backupFile;
     }
 
+    @Nullable
+    public String getApiKey() {
+        String auth = this.customHttpHeadersProvider.getCustomHeaders().get("Authorization");
+        if (auth == null) {
+            auth = this.customHttpHeaders.get("Authorization");
+        }
+        return auth;
+    }
+
+    public String getClientIdentifier() {
+        String api = getApiKey();
+        String appName = getAppName();
+        String instanceId = getInstanceId();
+        return String.format("apiKey:[%s] appName:[%s] instanceId:[%s]", api, appName, instanceId);
+    }
+
     public boolean isSynchronousFetchOnInitialisation() {
         return synchronousFetchOnInitialisation;
     }


Test patch:
diff --git a/src/test/java/io/getunleash/DefaultUnleashTest.java b/src/test/java/io/getunleash/DefaultUnleashTest.java
index f70f1f13..33140a31 100644
--- a/src/test/java/io/getunleash/DefaultUnleashTest.java
+++ b/src/test/java/io/getunleash/DefaultUnleashTest.java
@@ -2,8 +2,12 @@ package io.getunleash;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.*;
 
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
 import io.getunleash.event.EventDispatcher;
 import io.getunleash.metric.UnleashMetricService;
 import io.getunleash.repository.FeatureRepository;
@@ -16,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
 
 class DefaultUnleashTest {
     private DefaultUnleash sut;
@@ -144,4 +149,79 @@ class DefaultUnleashTest {
 
         verify(fallback).isEnabled(any(), any(), any());
     }
+
+    @Test
+    public void multiple_instantiations_of_the_same_config_gives_errors() {
+        ListAppender<ILoggingEvent> appender = new ListAppender();
+        appender.start();
+        Logger unleashLogger = (Logger) LoggerFactory.getLogger(DefaultUnleash.class);
+        unleashLogger.addAppender(appender);
+        UnleashConfig config =
+                UnleashConfig.builder()
+                        .unleashAPI("http://test:4242")
+                        .appName("multiple_connection_logging")
+                        .apiKey("default:development:1234567890123456")
+                        .instanceId("multiple_connection_logging")
+                        .build();
+        Unleash unleash1 = new DefaultUnleash(config);
+        // We've only instantiated the client once, so no errors should've been logged
+        assertThat(appender.list).isEmpty();
+        Unleash unleash2 = new DefaultUnleash(config);
+        // We've now instantiated the client twice, so we expect an error log line.
+        assertThat(appender.list).hasSize(1);
+        String id = config.getClientIdentifier();
+        assertThat(appender.list)
+                .extracting(ILoggingEvent::getFormattedMessage)
+                .containsExactly(
+                        "You already have 1 clients for Unleash Configuration ["
+                                + id
+                                + "] running. Please double check your code where you are instantiating the Unleash SDK");
+        appender.list.clear();
+        Unleash unleash3 = new DefaultUnleash(config);
+        // We've now instantiated the client twice, so we expect an error log line.
+        assertThat(appender.list).hasSize(1);
+        assertThat(appender.list)
+                .extracting(ILoggingEvent::getFormattedMessage)
+                .containsExactly(
+                        "You already have 2 clients for Unleash Configuration ["
+                                + id
+                                + "] running. Please double check your code where you are instantiating the Unleash SDK");
+    }
+
+    @Test
+    public void supports_failing_hard_on_multiple_instantiations() {
+        UnleashConfig config =
+                UnleashConfig.builder()
+                        .unleashAPI("http://test:4242")
+                        .appName("multiple_connection_exception")
+                        .apiKey("default:development:1234567890123456")
+                        .instanceId("multiple_connection_exception")
+                        .build();
+        String id = config.getClientIdentifier();
+        Unleash unleash1 = new DefaultUnleash(config);
+        assertThatThrownBy(
+                        () -> {
+                            Unleash unleash2 =
+                                    new DefaultUnleash(config, null, null, null, null, null, true);
+                        })
+                .isInstanceOf(RuntimeException.class)
+                .withFailMessage(
+                        "You already have 1 clients for Unleash Configuration ["
+                                + id
+                                + "] running. Please double check your code where you are instantiating the Unleash SDK");
+    }
+
+    @Test
+    public void client_identifier_handles_api_key_being_null() {
+        UnleashConfig config =
+                UnleashConfig.builder()
+                        .unleashAPI("http://test:4242")
+                        .appName("multiple_connection")
+                        .instanceId("testing_multiple")
+                        .build();
+        String id = config.getClientIdentifier();
+        assertThat(id)
+                .isEqualTo(
+                        "apiKey:[null] appName:[multiple_connection] instanceId:[testing_multiple]");
+    }
 }
diff --git a/src/test/java/io/getunleash/TestUtil.java b/src/test/java/io/getunleash/TestUtil.java
deleted file mode 100644
index 841ec659..00000000
--- a/src/test/java/io/getunleash/TestUtil.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.getunleash;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.LoggerConfig;
-
-public class TestUtil {
-    public static void setLogLevel(Level level) {
-        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
-        Configuration config = ctx.getConfiguration();
-        LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
-        loggerConfig.setLevel(level);
-        ctx.updateLoggers(); // This causes all Loggers to refetch information from their
-        // LoggerConfig.
-    }
-}
diff --git a/src/test/java/io/getunleash/example/UnleashUsageTest.java b/src/test/java/io/getunleash/example/UnleashUsageTest.java
index 69dff473..a8598285 100644
--- a/src/test/java/io/getunleash/example/UnleashUsageTest.java
+++ b/src/test/java/io/getunleash/example/UnleashUsageTest.java
@@ -3,17 +3,14 @@ package io.getunleash.example;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 
 import io.getunleash.DefaultUnleash;
-import io.getunleash.TestUtil;
 import io.getunleash.Unleash;
 import io.getunleash.util.UnleashConfig;
-import org.apache.logging.log4j.Level;
 import org.junit.jupiter.api.Test;
 
 public class UnleashUsageTest {
 
     @Test
     public void wire() {
-        TestUtil.setLogLevel(Level.ERROR); // Mute warn messages.
         UnleashConfig config =
                 new UnleashConfig.Builder()
                         .appName("test")
diff --git a/src/test/java/io/getunleash/repository/FeatureBackupHandlerFileTest.java b/src/test/java/io/getunleash/repository/FeatureBackupHandlerFileTest.java
index fe20af8a..6b9a540c 100644
--- a/src/test/java/io/getunleash/repository/FeatureBackupHandlerFileTest.java
+++ b/src/test/java/io/getunleash/repository/FeatureBackupHandlerFileTest.java
@@ -2,13 +2,11 @@ package io.getunleash.repository;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-import io.getunleash.TestUtil;
 import io.getunleash.util.UnleashConfig;
 import java.io.File;
 import java.io.Reader;
 import java.io.StringReader;
 import java.net.URISyntaxException;
-import org.apache.logging.log4j.Level;
 import org.junit.jupiter.api.Test;
 
 public class FeatureBackupHandlerFileTest {
@@ -92,8 +90,6 @@ public class FeatureBackupHandlerFileTest {
 
     @Test
     public void test_file_is_directory_should_not_crash() {
-        TestUtil.setLogLevel(Level.ERROR); // Mute warn messages.
-
         String backupFileIsDir = System.getProperty("java.io.tmpdir");
         UnleashConfig config =
                 UnleashConfig.builder()
diff --git a/src/test/java/io/getunleash/repository/ToggleBackupHandlerFileTest.java b/src/test/java/io/getunleash/repository/ToggleBackupHandlerFileTest.java
index 71f23501..68863154 100644
--- a/src/test/java/io/getunleash/repository/ToggleBackupHandlerFileTest.java
+++ b/src/test/java/io/getunleash/repository/ToggleBackupHandlerFileTest.java
@@ -2,11 +2,9 @@ package io.getunleash.repository;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-import io.getunleash.TestUtil;
 import io.getunleash.util.UnleashConfig;
 import java.io.*;
 import java.net.URISyntaxException;
-import org.apache.logging.log4j.Level;
 import org.junit.jupiter.api.Test;
 
 public class ToggleBackupHandlerFileTest {
@@ -115,8 +113,6 @@ public class ToggleBackupHandlerFileTest {
 
     @Test
     public void test_file_is_directory_should_not_crash() {
-        TestUtil.setLogLevel(Level.ERROR); // Mute warn messages.
-
         String backupFileIsDir = System.getProperty("java.io.tmpdir");
         UnleashConfig config =
                 UnleashConfig.builder()
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
deleted file mode 100644
index 9b9061b3..00000000
--- a/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="WARN">
-    <Appenders>
-        <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-        </Console>
-    </Appenders>
-    <Loggers>
-        <Root level="WARN">
-            <AppenderRef ref="Console"/>
-        </Root>
-    </Loggers>
-</Configuration>
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..a787811f
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+<configuration>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoders are assigned the type
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="error">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>
+