diff --git a/README.md b/README.md
index 6d2e395cc05183961798863d5f5fe4b2b0f747fe..7bb033de4334e297a6ba20986350624b0f683ac1 100644
--- a/README.md
+++ b/README.md
@@ -2,31 +2,33 @@
 
 The Simple Logging Facade for Java (SLF4J) serves as a simple facade
 or abstraction for various logging frameworks (e.g. java.util.logging,
-logback, reload4j, log4j 2.x) allowing the end user to plug in the desired logging
-framework at deployment time.
+logback, reload4j, log4j 2.x, logevents, penna, rainbowgum, tinylog)
+allowing the end user to plug in the desired logging framework at
+deployment time.
 
 More information can be found on the [SLF4J website](http://www.slf4j.org).
 
-# Build Status
-[![Build Status](https://travis-ci.org/qos-ch/slf4j.svg)](https://travis-ci.org/qos-ch/slf4j)
-
 # Search org.slf4j artifacts on Maven Central
-[![Maven Central](https://img.shields.io/badge/Search%20org%2Eslf4j%20artifacts%20on%20Maven%20Central-2.0.x-green)](https://search.maven.org/search?q=g:org.slf4j%20AND%20v:2.0.%3F) 
+[![Maven Central](https://img.shields.io/badge/Search%20org%2Eslf4j%20artifacts%20on%20Maven%20Central-2.0.x-green)](https://central.sonatype.com/search?namespace=org.slf4j) 
 
 
 # In case of problems
 
 In case of problems please do not hesitate to post an e-mail message
-on the slf4j-user@qos.ch mailing list.  However, please do not
-directly e-mail SLF4J developers. The answer to your question might
-be useful to other users. Moreover, there are many knowledgeable users
-on the slf4j-user mailing lists who can quickly answer your
-questions.
+on the [slf4j-user@qos.ch](https://mailman.qos.ch/cgi-bin/mailman/listinfo/slf4j-user)
+mailing list or to start a <a
+href="https://github.com/qos-ch/slf4j/discussions">discussion</a> on
+github.  However, please do not directly e-mail SLF4J developers. The
+answer to your question might be useful to other users. Moreover,
+there are many knowledgeable users on the slf4j-user mailing lists who
+can quickly answer your questions.
 
 # Urgent issues
 
-For urgent issues do not hesitate to [champion a release](https://github.com/sponsors/qos-ch/sponsorships?tier_id=77436).
-In principle, most championed issues are solved within 3 business days ensued by a release.
+For urgent issues do not hesitate to [champion a
+release](https://github.com/sponsors/qos-ch/sponsorships?tier_id=77436).
+In principle, most championed issues are solved within 3 business days
+ensued by a release.
 
 # How to build SLF4J
 
@@ -43,14 +45,13 @@ process:
 1. Start a discussion on the [slf4j-dev mailing
 list](http://www.slf4j.org/mailing-lists.html) about your proposed
 change. Alternately, file a [bug
-report](http://www.slf4j.org/bug-reporting.html) to initiate the
-discussion. Note that we ask pull requests to be linked to a [Jira
-ticket](https://jira.qos.ch/).
+report](https://github.com/qos-ch/slf4j/issues) on github to initiate the
+discussion. 
 
 2. Fork qos-ch/slf4j. Ideally, create a new branch from your fork for
 your contribution to make it easier to merge your changes back.
 
-3. Make your changes on the branch you hopefully created in Step 2. Be
+3. Make your changes on the branch created in Step 2. Be
 sure that your code passes existing unit tests. Please add unit tests
 for your work if appropriate. It usually is.
 
@@ -61,10 +62,8 @@ will be automatically rejected by the [DCO GitHub
 check](https://probot.github.io/apps/dco/) application.
 
 5. Push your changes to your fork/branch in GitHub. Don't push it to
-your master! If you do it will make it harder to submit new changes
+your master! If you do, it will make it harder to submit new changes
 later.
 
 6. Submit a pull request to SLF4J from your commit page on GitHub.
 
-7. Did we mention that you will be asked to link your pull request
-with a Jira ticket?
diff --git a/integration/pom.xml b/integration/pom.xml
index 0c0baf6e341374791ba802202ba8fbbe5d37ce2b..e17af23c34dbaac81bd80c392dc4aca5a1fc9557 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/jcl-over-slf4j/pom.xml b/jcl-over-slf4j/pom.xml
index 13e18c7f26fb21b78d50fdacb12af3125cdd7525..b20a2071b36c2f5f97e42088599d341b95677a1a 100755
--- a/jcl-over-slf4j/pom.xml
+++ b/jcl-over-slf4j/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
@@ -19,7 +19,7 @@
 
   <licenses>
     <license>
-      <name>Apache License, Version 2.0</name>
+      <name>Apache-2.0</name>
       <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
       <distribution>repo</distribution>
     </license>
diff --git a/jul-to-slf4j/pom.xml b/jul-to-slf4j/pom.xml
index 85165a8b52f30a1235e7b2a392fd1e2c26307eef..bb7983db04a70e00472cfb4e6d767c686190b7e6 100755
--- a/jul-to-slf4j/pom.xml
+++ b/jul-to-slf4j/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/log4j-over-slf4j/pom.xml b/log4j-over-slf4j/pom.xml
index 93cd0c175449c730d5b0da97c228aab0a9c0c425..9ddbeec53a9486f1840bb5cf8490bb0896bde7d1 100755
--- a/log4j-over-slf4j/pom.xml
+++ b/log4j-over-slf4j/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
@@ -21,8 +21,8 @@
 
   <licenses>
     <license>
-      <name>Apache Software Licenses</name>
-      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <name>Apache-2.0</name>
+      <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
     </license>
   </licenses>
 
diff --git a/osgi-over-slf4j/pom.xml b/osgi-over-slf4j/pom.xml
index d8429117255e0895c7574642cbfd3240bbb094f8..b8ca3ab8dd4ea2fca34c90f51840dd56dbf7cef4 100755
--- a/osgi-over-slf4j/pom.xml
+++ b/osgi-over-slf4j/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/parent/pom.xml b/parent/pom.xml
index 0ec87e8995d1528b38b861d68102334b1bfac1e6..2f2a2d73a43b657f8c121a92cdc215a312b1e9d3 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-bom</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
 
@@ -26,7 +26,7 @@
 
   <properties>
     <!-- yyyy-MM-dd'T'HH:mm:ss'Z' -->
-    <project.build.outputTimestamp>2024-08-10T09:01:00Z</project.build.outputTimestamp>
+    <project.build.outputTimestamp>2025-02-25T16:36:00Z</project.build.outputTimestamp>
     <latest.1.version>1.7.36</latest.1.version>
     <!-- java.util.ServiceLoader requires Java 6 -->
     <jdk.version>8</jdk.version>
diff --git a/pom.xml b/pom.xml
index de3b614c0ff336f8b62ebc5b516d602c1f27ce7e..e4a7bd14729d1e7355039416ff6824245bf0dcc0 100755
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-bom</artifactId>
-  <version>2.0.16</version>
+  <version>2.0.17</version>
   <packaging>pom</packaging>
 
   <url>http://www.slf4j.org</url>
@@ -16,8 +16,8 @@
 
   <licenses>
     <license>
-      <name>MIT License</name>
-      <url>http://www.opensource.org/licenses/mit-license.php</url>
+      <name>MIT</name>
+      <url>https://opensource.org/license/mit</url>
       <distribution>repo</distribution>
     </license>
   </licenses>
diff --git a/slf4j-api/pom.xml b/slf4j-api/pom.xml
index 29874af922dd5b354b2d5378b1ef4afd35e1aaad..9bcf3ecb5090450c3c2d5780b133f0445b8378ef 100755
--- a/slf4j-api/pom.xml
+++ b/slf4j-api/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
index 9ad6b8a13f7af2a8bf2762f4243a1ab90aa34359..0dc7a72c75e06bec68953073a9f7d4864ac73025 100755
--- a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
@@ -47,6 +47,7 @@ import org.slf4j.helpers.Reporter;
 import org.slf4j.helpers.SubstituteLogger;
 import org.slf4j.helpers.SubstituteServiceProvider;
 import org.slf4j.helpers.Util;
+import org.slf4j.spi.MDCAdapter;
 import org.slf4j.spi.SLF4JServiceProvider;
 
 /**
@@ -195,6 +196,7 @@ public final class LoggerFactory {
             reportMultipleBindingAmbiguity(providersList);
             if (providersList != null && !providersList.isEmpty()) {
                 PROVIDER = providersList.get(0);
+                earlyBindMDCAdapter();
                 // SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
                 PROVIDER.initialize();
                 INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
@@ -215,6 +217,19 @@ public final class LoggerFactory {
         }
     }
 
+    /**
+     * The value of PROVIDER.getMDCAdapter() can be null while PROVIDER has not yet initialized.
+     *
+     * However, SLF4JServiceProvider implementations are expected to initialize their internal
+     * MDCAdapter field in their constructor or on field declaration.
+     */
+    private static void earlyBindMDCAdapter() {
+        MDCAdapter mdcAdapter = PROVIDER.getMDCAdapter();
+        if(mdcAdapter != null) {
+            MDC.setMDCAdapter(mdcAdapter);
+        }
+    }
+
     static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) {
         String explicitlySpecified = System.getProperty(PROVIDER_PROPERTY_KEY);
         if (null == explicitlySpecified || explicitlySpecified.isEmpty()) {
diff --git a/slf4j-api/src/main/java/org/slf4j/MDC.java b/slf4j-api/src/main/java/org/slf4j/MDC.java
index 6b0feded01e1a7e6e6cdf65c2ab434fc3e6385d1..8625af3b93ec7fecbdc81402642c2f354029cf32 100644
--- a/slf4j-api/src/main/java/org/slf4j/MDC.java
+++ b/slf4j-api/src/main/java/org/slf4j/MDC.java
@@ -28,10 +28,7 @@ import java.io.Closeable;
 import java.util.Deque;
 import java.util.Map;
 
-import org.slf4j.helpers.BasicMDCAdapter;
-import org.slf4j.helpers.NOPMDCAdapter;
-import org.slf4j.helpers.Reporter;
-import org.slf4j.helpers.Util;
+import org.slf4j.helpers.*;
 import org.slf4j.spi.MDCAdapter;
 import org.slf4j.spi.SLF4JServiceProvider;
 
@@ -68,7 +65,7 @@ public class MDC {
     static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
     private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
     static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
-    static MDCAdapter mdcAdapter;
+    static MDCAdapter MDC_ADAPTER;
 
     /**
      * An adapter to remove the key when done.
@@ -88,17 +85,30 @@ public class MDC {
     private MDC() {
     }
 
-    static {
+    private static MDCAdapter getMDCAdapterGivenByProvider() {
         SLF4JServiceProvider provider = LoggerFactory.getProvider();
-        if (provider != null) {
+        if(provider != null) {
+            // If you wish to change the mdc adapter, setting the MDC.MDCAdapter variable might be insufficient.
+            // Keep in mind that the provider *might* perform additional internal mdcAdapter assignments that
+            // you would also need to replicate/adapt.
+
             // obtain and attach the MDCAdapter from the provider
-            // If you wish to change the adapter, Setting the MDC.mdcAdapter variable might not be enough as
-            // the provider might perform additional assignments that you would need to replicate/adapt.
-            mdcAdapter = provider.getMDCAdapter();
+
+            final MDCAdapter anAdapter = provider.getMDCAdapter();
+            emitTemporaryMDCAdapterWarningIfNeeded(provider);
+            return anAdapter;
         } else {
             Reporter.error("Failed to find provider.");
             Reporter.error("Defaulting to no-operation MDCAdapter implementation.");
-            mdcAdapter = new NOPMDCAdapter();
+            return new NOPMDCAdapter();
+        }
+    }
+
+    private static void emitTemporaryMDCAdapterWarningIfNeeded(SLF4JServiceProvider provider) {
+        boolean isSubstitute = provider instanceof SubstituteServiceProvider;
+        if(isSubstitute) {
+            Reporter.info("Temporary mdcAdapter given by SubstituteServiceProvider.");
+            Reporter.info("This mdcAdapter will be replaced after backend initialization has completed.");
         }
     }
 
@@ -121,10 +131,10 @@ public class MDC {
         if (key == null) {
             throw new IllegalArgumentException("key parameter cannot be null");
         }
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        mdcAdapter.put(key, val);
+        getMDCAdapter().put(key, val);
     }
 
     /**
@@ -177,10 +187,10 @@ public class MDC {
             throw new IllegalArgumentException("key parameter cannot be null");
         }
 
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        return mdcAdapter.get(key);
+        return getMDCAdapter().get(key);
     }
 
     /**
@@ -198,20 +208,20 @@ public class MDC {
             throw new IllegalArgumentException("key parameter cannot be null");
         }
 
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        mdcAdapter.remove(key);
+        getMDCAdapter().remove(key);
     }
 
     /**
      * Clear all entries in the MDC of the underlying implementation.
      */
     public static void clear() {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        mdcAdapter.clear();
+        getMDCAdapter().clear();
     }
 
     /**
@@ -222,10 +232,10 @@ public class MDC {
      * @since 1.5.1
      */
     public static Map<String, String> getCopyOfContextMap() {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        return mdcAdapter.getCopyOfContextMap();
+        return getMDCAdapter().getCopyOfContextMap();
     }
 
     /**
@@ -240,23 +250,40 @@ public class MDC {
      * @since 1.5.1
      */
     public static void setContextMap(Map<String, String> contextMap) {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        mdcAdapter.setContextMap(contextMap);
+        getMDCAdapter().setContextMap(contextMap);
     }
 
     /**
      * Returns the MDCAdapter instance currently in use.
-     * 
+     *
+     * Since 2.0.17, if the MDCAdapter instance is null, then this method set it to use
+     * the adapter returned by the SLF4JProvider. However, in the vast majority of cases
+     * the MDCAdapter will be set earlier (during initialization) by {@link LoggerFactory}.
+     *
      * @return the MDcAdapter instance currently in use.
      * @since 1.4.2
      */
     public static MDCAdapter getMDCAdapter() {
-        return mdcAdapter;
+        if(MDC_ADAPTER == null) {
+            MDC_ADAPTER = getMDCAdapterGivenByProvider();
+        }
+        return MDC_ADAPTER;
     }
 
-
+    /**
+     * Set MDCAdapter instance to use.
+     *
+     * @since 2.0.17
+     */
+    static void setMDCAdapter(MDCAdapter anMDCAdapter) {
+        if(anMDCAdapter == null) {
+            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
+        }
+        MDC_ADAPTER = anMDCAdapter;
+    }
 
     /**
      * Push a value into the deque(stack) referenced by 'key'.
@@ -266,10 +293,10 @@ public class MDC {
      * @since 2.0.0
      */
     static public void pushByKey(String key, String value) {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        mdcAdapter.pushByKey(key, value);
+        getMDCAdapter().pushByKey(key, value);
     }
     
     /**
@@ -280,10 +307,10 @@ public class MDC {
      * @since 2.0.0
      */
     static public String popByKey(String key) {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        return mdcAdapter.popByKey(key);
+        return getMDCAdapter().popByKey(key);
     }
 
     /**
@@ -295,9 +322,9 @@ public class MDC {
      * @since 2.0.0
      */
     public Deque<String>  getCopyOfDequeByKey(String key) {
-        if (mdcAdapter == null) {
+        if (getMDCAdapter() == null) {
             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
         }
-        return mdcAdapter.getCopyOfDequeByKey(key);
+        return getMDCAdapter().getCopyOfDequeByKey(key);
     }
 }
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java
index 2c59530d5f291376054ad48fc462a028c92b40f1..ad03eaef0435f4af6f5a102f8f70b37e907a0534 100755
--- a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteServiceProvider.java
@@ -7,8 +7,18 @@ import org.slf4j.spi.SLF4JServiceProvider;
 
 public class SubstituteServiceProvider implements SLF4JServiceProvider {
     private final SubstituteLoggerFactory loggerFactory = new SubstituteLoggerFactory();
-    private final IMarkerFactory markerFactory = new BasicMarkerFactory();
-    private final MDCAdapter mdcAdapter = new BasicMDCAdapter();
+
+    // LoggerFactory expects providers to initialize markerFactory as early as possible.
+    private final IMarkerFactory markerFactory;
+
+    // LoggerFactory expects providers to initialize their MDCAdapter field
+    // as early as possible, preferably at construction time.
+    private final MDCAdapter mdcAdapter;
+
+    public SubstituteServiceProvider() {
+        markerFactory = new BasicMarkerFactory();
+        mdcAdapter = new BasicMDCAdapter();
+    }
 
     @Override
     public ILoggerFactory getLoggerFactory() {
@@ -36,6 +46,5 @@ public class SubstituteServiceProvider implements SLF4JServiceProvider {
 
     @Override
     public void initialize() {
-
     }
 }
diff --git a/slf4j-api/src/test/java/org/slf4j/basicTests/NoBindingTest.java b/slf4j-api/src/test/java/org/slf4j/basicTests/NoBindingTest.java
index 8e1dd16c32383b1a2f710fb61ec2cf93366d4317..9a1a54e51808b8f3730604a439e105b9fd4197d2 100644
--- a/slf4j-api/src/test/java/org/slf4j/basicTests/NoBindingTest.java
+++ b/slf4j-api/src/test/java/org/slf4j/basicTests/NoBindingTest.java
@@ -29,6 +29,8 @@ import static org.junit.Assert.assertTrue;
 
 import java.util.Random;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,11 +39,21 @@ import org.slf4j.Marker;
 import org.slf4j.MarkerFactory;
 import org.slf4j.helpers.BasicMarker;
 import org.slf4j.helpers.NOPLogger;
+import org.slf4j.helpers.Reporter;
 
 public class NoBindingTest {
 
     int diff = new Random().nextInt(10000);
 
+    @Before
+    public void setUp() throws Exception {
+        System.setProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY, "debug");
+    }
+    @After
+    public void tearDown() throws Exception {
+        System.clearProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY);
+    }
+
     @Test
     public void testLogger() {
         Logger logger = LoggerFactory.getLogger(NoBindingTest.class);
diff --git a/slf4j-ext/pom.xml b/slf4j-ext/pom.xml
index b95691600104ac6d046193d9fefacf988d1ea3b1..9c8a4eeaa17f58157a4ac39bf63b2baf94e64e12 100755
--- a/slf4j-ext/pom.xml
+++ b/slf4j-ext/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-jdk-platform-logging/pom.xml b/slf4j-jdk-platform-logging/pom.xml
index ece632eae6a1d0e598be4711557dcc876a6ad68d..1d94a2ac87812fe84616f1f22a00731f73235417 100644
--- a/slf4j-jdk-platform-logging/pom.xml
+++ b/slf4j-jdk-platform-logging/pom.xml
@@ -8,7 +8,7 @@
   <parent>
     <artifactId>slf4j-parent</artifactId>
     <groupId>org.slf4j</groupId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java b/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
index 194436d7e4688154cc3f6abbaffab122743d1437..5b29688fa7afc2b1eba2292d4bd5ff52d066300f 100644
--- a/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
+++ b/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
@@ -115,16 +115,11 @@ class SLF4JPlatformLogger implements System.Logger {
      * @param thrown
      * @param params
      */
-    private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
-        if (jplLevel == Level.OFF)
-            return;
+    private void log(final Level jplLevel, final ResourceBundle bundle, final String msg, final Throwable thrown, final Object... params) {
 
-        if (jplLevel == Level.ALL) {
-            performLog(org.slf4j.event.Level.TRACE, bundle, msg, thrown, params);
-            return;
-        }
+        final Level jplLevelReduced = fixExtremeLevels(jplLevel);
 
-        org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
+        org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevelReduced);
         boolean isEnabled = slf4jLogger.isEnabledForLevel(slf4jLevel);
 
         if (isEnabled) {
@@ -132,6 +127,27 @@ class SLF4JPlatformLogger implements System.Logger {
         }
     }
 
+    /**
+     * <p>Level.OFF and Level.ALL levels are not supposed to be used when calling log printing methods.
+     * </p>
+     *
+     * <p>We compensate for such incorrect usage by transforming Level.OFF as Level.ERROR and
+     * Level.ALL as Level.TRACE.
+     * </p>
+     *
+     * @param jplLevel
+     * @return
+     */
+    private Level fixExtremeLevels(Level jplLevel) {
+        if (jplLevel == Level.OFF)
+            return  Level.ERROR;
+
+        if (jplLevel == Level.ALL)
+            return Level.TRACE;
+
+        return jplLevel;
+    }
+
     private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
         String message = getResourceStringOrMessage(bundle, msg);
         LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel);
diff --git a/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java b/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
index 8778fd2fa1a3da1ff2af15d5813010a4a3598e28..9f8ce652ad85c5179992c5b9add857bde31863e5 100644
--- a/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
+++ b/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
@@ -118,5 +118,18 @@ public class SLF4JPlatformLoggingTest {
         assertTrue(results.get(line++).contains(this.getClass().getName()));
     }
 
+    @Test
+    public void extremeLevels() throws IOException {
+        LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
+        assertEquals(EXPECTED_FINDER_CLASS, finder.getClass().getName());
+        Logger systemLogger = finder.getLogger("extremeLevels", null);
+        systemLogger.log(Level.OFF, "hello");
+        systemLogger.log(Level.ALL, "world");
+
+        List<String> results = SPS.stringList;
+        assertEquals(1, results.size());
+        assertEquals("ERROR extremeLevels - hello", results.get(0));
+
+    }
 
 }
diff --git a/slf4j-jdk14/pom.xml b/slf4j-jdk14/pom.xml
index 24047662b8ca938468da5afb6e12e401ab1ab046..262ce44df3a4e1cf6ff340a402f94bbdc32106bf 100755
--- a/slf4j-jdk14/pom.xml
+++ b/slf4j-jdk14/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-jdk14/src/main/java/org/slf4j/jul/JDK14LoggerAdapter.java b/slf4j-jdk14/src/main/java/org/slf4j/jul/JDK14LoggerAdapter.java
index dbc52da5208580cf3e9cb4710917a507610fd0a6..98a0fed44c697d00c200bb9aa1cf729c15d84f75 100755
--- a/slf4j-jdk14/src/main/java/org/slf4j/jul/JDK14LoggerAdapter.java
+++ b/slf4j-jdk14/src/main/java/org/slf4j/jul/JDK14LoggerAdapter.java
@@ -55,6 +55,8 @@ public final class JDK14LoggerAdapter extends LegacyAbstractLogger implements Lo
 
     transient final java.util.logging.Logger logger;
 
+    static int NOT_FOUND = -1;
+
     // WARN: JDK14LoggerAdapter constructor should have only package access so
     // that only JDK14LoggerFactory be able to create one.
     JDK14LoggerAdapter(java.util.logging.Logger logger) {
@@ -181,26 +183,10 @@ public final class JDK14LoggerAdapter extends LegacyAbstractLogger implements Lo
     final private void fillCallerData(String callerFQCN, LogRecord record) {
         StackTraceElement[] steArray = new Throwable().getStackTrace();
 
-        int selfIndex = -1;
-        for (int i = 0; i < steArray.length; i++) {
-            final String className = steArray[i].getClassName();
-
-            if (barrierMatch(callerFQCN, className)) {
-                selfIndex = i;
-                break;
-            }
-        }
-
-        int found = -1;
-        for (int i = selfIndex + 1; i < steArray.length; i++) {
-            final String className = steArray[i].getClassName();
-            if (!(barrierMatch(callerFQCN, className))) {
-                found = i;
-                break;
-            }
-        }
+        int furthestIndex = findFurthestIndex(callerFQCN, steArray);
 
-        if (found != -1) {
+        if (furthestIndex != NOT_FOUND) {
+            int found = furthestIndex+1;
             StackTraceElement ste = steArray[found];
             // setting the class name has the side effect of setting
             // the needToInferCaller variable to false.
@@ -209,7 +195,24 @@ public final class JDK14LoggerAdapter extends LegacyAbstractLogger implements Lo
         }
     }
 
-    static String SELF = JDK14LoggerAdapter.class.getName();
+    // find the furthest index which matches any of the barrier classes
+    // We assume that the actual caller is at most MAX_SEARCH_DEPTH calls away
+    private int findFurthestIndex(String callerFQCN, StackTraceElement[] steArray) {
+
+        final int maxIndex = Math.min(MAX_SEARCH_DEPTH, steArray.length);
+        int furthestIndex = NOT_FOUND;
+
+        for (int i = 0; i < maxIndex; i++) {
+            final String className = steArray[i].getClassName();
+            if (barrierMatch(callerFQCN, className)) {
+                furthestIndex = i;
+            }
+        }
+        return furthestIndex;
+    }
+
+   static final int MAX_SEARCH_DEPTH = 12;
+   static String SELF = JDK14LoggerAdapter.class.getName();
 
     static String SUPER = LegacyAbstractLogger.class.getName();
     static String SUPER_OF_SUPER = AbstractLogger.class.getName();
diff --git a/slf4j-jdk14/src/main/java/org/slf4j/jul/JULServiceProvider.java b/slf4j-jdk14/src/main/java/org/slf4j/jul/JULServiceProvider.java
index 5e9a74edd6c4fc8bfb9c1bd66bef64bfc49caa79..15f492f1d0ee3a542c232877de2579e48812b80a 100755
--- a/slf4j-jdk14/src/main/java/org/slf4j/jul/JULServiceProvider.java
+++ b/slf4j-jdk14/src/main/java/org/slf4j/jul/JULServiceProvider.java
@@ -17,8 +17,16 @@ public class JULServiceProvider implements SLF4JServiceProvider {
     public static String REQUESTED_API_VERSION = "2.0.99"; // !final
 
     private ILoggerFactory loggerFactory;
-    private IMarkerFactory markerFactory;
-    private MDCAdapter mdcAdapter;
+    // LoggerFactory expects providers to initialize markerFactory as early as possible.
+    private final IMarkerFactory markerFactory;
+    // LoggerFactory expects providers to initialize their MDCAdapter field
+    // as early as possible, preferably at construction time.
+    private final MDCAdapter mdcAdapter;
+
+    public JULServiceProvider() {
+        markerFactory = new BasicMarkerFactory();
+        mdcAdapter = new BasicMDCAdapter();
+    }
 
     @Override
     public ILoggerFactory getLoggerFactory() {
@@ -42,7 +50,5 @@ public class JULServiceProvider implements SLF4JServiceProvider {
     @Override
     public void initialize() {
         loggerFactory = new JDK14LoggerFactory();
-        markerFactory = new BasicMarkerFactory();
-        mdcAdapter = new BasicMDCAdapter();
     }
 }
diff --git a/slf4j-log4j12/pom.xml b/slf4j-log4j12/pom.xml
index a57233f23479786e0828b7a98b95664c54d3db57..d8f363c4dc266c4d8a5d74c49e646877b0b61939 100755
--- a/slf4j-log4j12/pom.xml
+++ b/slf4j-log4j12/pom.xml
@@ -8,7 +8,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
   
@@ -24,7 +24,7 @@
     <relocation>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-reload4j</artifactId>
-      <version>2.0.16</version>      
+      <version>2.0.17</version>      
     </relocation>
   </distributionManagement>
 
diff --git a/slf4j-migrator/pom.xml b/slf4j-migrator/pom.xml
index 5a44dec61f54256d63980900cca306030963cff9..a6aa38e9a67e7809df5a0bb99836eac5f09a927a 100755
--- a/slf4j-migrator/pom.xml
+++ b/slf4j-migrator/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-nop/pom.xml b/slf4j-nop/pom.xml
index d38a43a3b4603a22c41a47220a858e92606923fe..1078b9840ecb848bcfbb8dd24f7a9cd64017e8b3 100755
--- a/slf4j-nop/pom.xml
+++ b/slf4j-nop/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-nop/src/main/java/org/slf4j/nop/NOPServiceProvider.java b/slf4j-nop/src/main/java/org/slf4j/nop/NOPServiceProvider.java
index 515cf23b27be096e9c84418f545426d634341e3f..e4b85c8d7c2d583886ff744415613e23e0ae0249 100755
--- a/slf4j-nop/src/main/java/org/slf4j/nop/NOPServiceProvider.java
+++ b/slf4j-nop/src/main/java/org/slf4j/nop/NOPServiceProvider.java
@@ -18,9 +18,18 @@ public class NOPServiceProvider implements SLF4JServiceProvider {
     public static String REQUESTED_API_VERSION = "2.0.99"; // !final
 
     private final ILoggerFactory loggerFactory = new NOPLoggerFactory();
-    private final IMarkerFactory markerFactory = new BasicMarkerFactory();
-    private final MDCAdapter mdcAdapter = new NOPMDCAdapter();
 
+    // LoggerFactory expects providers to initialize markerFactory as early as possible.
+    private final IMarkerFactory markerFactory;
+    
+    // LoggerFactory expects providers to initialize their MDCAdapter field
+    // as early as possible, preferably at construction time.
+    private final MDCAdapter mdcAdapter;
+
+    public NOPServiceProvider() {
+        markerFactory = new BasicMarkerFactory();
+        mdcAdapter = new NOPMDCAdapter();
+    }
     public ILoggerFactory getLoggerFactory() {
         return loggerFactory;
     }
@@ -39,7 +48,6 @@ public class NOPServiceProvider implements SLF4JServiceProvider {
     }
 
     public void initialize() {
-
     }
 
    
diff --git a/slf4j-nop/src/test/java/org/slf4j/nop/MultithreadedInitializationTest.java b/slf4j-nop/src/test/java/org/slf4j/nop/MultithreadedInitializationTest.java
index dabef97c33a6779f6ce7b77d66097f03353e3b85..793f3d104635e27c3df9cbe754963413939a5c7e 100755
--- a/slf4j-nop/src/test/java/org/slf4j/nop/MultithreadedInitializationTest.java
+++ b/slf4j-nop/src/test/java/org/slf4j/nop/MultithreadedInitializationTest.java
@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2004-2016 QOS.ch
  * All rights reserved.
- *
+ * <p>
  * Permission is hereby granted, free  of charge, to any person obtaining
  * a  copy  of this  software  and  associated  documentation files  (the
  * "Software"), to  deal in  the Software without  restriction, including
@@ -9,10 +9,10 @@
  * distribute,  sublicense, and/or sell  copies of  the Software,  and to
  * permit persons to whom the Software  is furnished to do so, subject to
  * the following conditions:
- *
+ * <p>
  * The  above  copyright  notice  and  this permission  notice  shall  be
  * included in all copies or substantial portions of the Software.
- *
+ * <p>
  * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
  * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
  * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
@@ -20,21 +20,9 @@
  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
  */
 package org.slf4j.nop;
 
-import static org.junit.Assert.assertEquals;
-import static org.slf4j.helpers.Reporter.SLF4J_INTERNAL_VERBOSITY_KEY;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -43,6 +31,15 @@ import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactoryFriend;
 import org.slf4j.helpers.StringPrintStream;
 
+import java.io.PrintStream;
+import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.slf4j.helpers.Reporter.SLF4J_INTERNAL_VERBOSITY_KEY;
+
 public class MultithreadedInitializationTest {
 
     static int NUM_LINES_IN_SLF4J_CONNECTED_WITH_PROVIDER_INFO = 1;
@@ -76,7 +73,7 @@ public class MultithreadedInitializationTest {
         System.out.println("THREAD_COUNT=" + THREAD_COUNT);
         LoggerAccessingThread[] accessors = harness();
 
-        for (LoggerAccessingThread accessor : accessors) {
+        for(LoggerAccessingThread accessor : accessors) {
             EVENT_COUNT.getAndIncrement();
             accessor.logger.info("post harness");
         }
@@ -91,13 +88,13 @@ public class MultithreadedInitializationTest {
     private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
         LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
         final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
-        for (int i = 0; i < THREAD_COUNT; i++) {
+        for(int i = 0; i < THREAD_COUNT; i++) {
             threads[i] = new LoggerAccessingThread(barrier, i);
             threads[i].start();
         }
 
         barrier.await();
-        for (int i = 0; i < THREAD_COUNT; i++) {
+        for(int i = 0; i < THREAD_COUNT; i++) {
             threads[i].join();
         }
         return threads;
@@ -123,7 +120,9 @@ public class MultithreadedInitializationTest {
             logger.info("in run method");
             EVENT_COUNT.getAndIncrement();
         }
-    };
+    }
+
+    ;
 
 //    public static class StringPrintStream extends PrintStream {
 //
diff --git a/slf4j-reload4j/pom.xml b/slf4j-reload4j/pom.xml
index 8b1d5850f5cb2d002cf2694b016f93772114af5f..7312c8b65265fd39b3656d2f5b8e1688e2c8b7c3 100644
--- a/slf4j-reload4j/pom.xml
+++ b/slf4j-reload4j/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java
index ed023b31e206c072e04059ddaf77fa9bfd98f9b0..b7990085742c2b9e7f723c48be77b956831deb15 100644
--- a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java
+++ b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java
@@ -19,10 +19,17 @@ public class Reload4jServiceProvider implements SLF4JServiceProvider {
     public static String REQUESTED_API_VERSION = "2.0.99"; // !final
 
     private ILoggerFactory loggerFactory;
-    private IMarkerFactory markerFactory;
-    private MDCAdapter mdcAdapter;
+
+    // LoggerFactory expects providers to initialize markerFactory as early as possible.
+    private final IMarkerFactory markerFactory;
+
+    // LoggerFactory expects providers to have a valid MDCAdapter field
+    // as early as possible, preferably at construction time.
+    private final MDCAdapter mdcAdapter;
 
     public Reload4jServiceProvider() {
+        markerFactory = new BasicMarkerFactory();
+        mdcAdapter = new Reload4jMDCAdapter();
         try {
             @SuppressWarnings("unused")
             Level level = Level.TRACE;
@@ -34,8 +41,6 @@ public class Reload4jServiceProvider implements SLF4JServiceProvider {
     @Override
     public void initialize() {
         loggerFactory = new Reload4jLoggerFactory();
-        markerFactory = new BasicMarkerFactory();
-        mdcAdapter = new Reload4jMDCAdapter();
     }
 
     @Override
diff --git a/slf4j-simple/pom.xml b/slf4j-simple/pom.xml
index ebe26f47ab450842639c031616da6d7aa5bd7ba0..466243d7d7c2ea73da012f680c67d825cf9ea6c8 100755
--- a/slf4j-simple/pom.xml
+++ b/slf4j-simple/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.16</version>
+    <version>2.0.17</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff --git a/slf4j-simple/src/main/java/org/slf4j/simple/SimpleServiceProvider.java b/slf4j-simple/src/main/java/org/slf4j/simple/SimpleServiceProvider.java
index 795200dc5d66cb4f48b62f475ab46613906db700..a73a4a9a0ada69bcddb093c257a8d79745d29b39 100755
--- a/slf4j-simple/src/main/java/org/slf4j/simple/SimpleServiceProvider.java
+++ b/slf4j-simple/src/main/java/org/slf4j/simple/SimpleServiceProvider.java
@@ -17,8 +17,16 @@ public class SimpleServiceProvider implements SLF4JServiceProvider {
     public static String REQUESTED_API_VERSION = "2.0.99"; // !final
 
     private ILoggerFactory loggerFactory;
-    private IMarkerFactory markerFactory;
-    private MDCAdapter mdcAdapter;
+    // LoggerFactory expects providers to initialize markerFactory as early as possible.
+    private final IMarkerFactory  markerFactory;
+    // LoggerFactory expects providers to initialize their MDCAdapter field
+    // as early as possible, preferably at construction time.
+    private final MDCAdapter mdcAdapter;
+
+    public SimpleServiceProvider() {
+        markerFactory = new BasicMarkerFactory();
+        mdcAdapter =  new NOPMDCAdapter();
+    }
 
     public ILoggerFactory getLoggerFactory() {
         return loggerFactory;
@@ -41,10 +49,8 @@ public class SimpleServiceProvider implements SLF4JServiceProvider {
 
     @Override
     public void initialize() {
-
         loggerFactory = new SimpleLoggerFactory();
-        markerFactory = new BasicMarkerFactory();
-        mdcAdapter = new NOPMDCAdapter();
+
     }
 
 }