Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package slf4j for openSUSE:Factory checked 
in at 2026-10-01 16:59:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/slf4j (Old)
 and      /work/SRC/openSUSE:Factory/.slf4j.new.1253 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "slf4j"

Thu Oct  1 16:59:54 2026 rev:36 rq:1381649 version:2.0.20

Changes:
--------
--- /work/SRC/openSUSE:Factory/slf4j/slf4j.changes      2026-07-15 
16:45:25.254257134 +0200
+++ /work/SRC/openSUSE:Factory/.slf4j.new.1253/slf4j.changes    2026-10-01 
17:00:08.842941287 +0200
@@ -1,0 +2,30 @@
+Tue Sep 29 11:05:22 UTC 2026 - Fridrich Strba <[email protected]>
+
+- Upgrade to 2.0.20
+  * Changes
+    + Marker instances are slated to become immutable in future
+      releases. As such, methods in the Marker interface
+      adding/removing children are now marked as deprecated.
+- Changes of version 2.0.19
+  * Changes
+    + When the fluent API was used with XLogger/LoggerWrapper in the
+      slf4j-ext module, caller location was incorrectly reported. To
+      correct this, LoggerWrapper now delegates
+      makeLoggingEventBuilder() to the wrapped logger so that the
+      application caller can be correctly extracted.
+    + In slf4j-api/DefaultLoggingEventBuilder, a failing toString()
+      invocation on a key-value value, including StackOverflowError,
+      no longer aborts logging. The builder now substitutes [FAILED
+      toString()], matching the existing behaviour of
+      MessageFormatter for message arguments.
+    + The isLoggable() method in SLF4JPlatformLogger now maps
+      System.Logger.Level.ALL and Level.OFF the same way as log(),
+      that is ALL as TRACE and OFF as ERROR, instead of treating
+      both as always loggable.
+    + Published JAR files now package each module's own LICENSE.txt
+      under META-INF/. Previously the parent POM copied the
+      repository-root MIT license into every module, so Apache-2.0
+      modules such as jcl-over-slf4j and log4j-over-slf4j shipped
+      the wrong license text.
+
+-------------------------------------------------------------------

Old:
----
  slf4j-2.0.18.tar.xz

New:
----
  slf4j-2.0.20.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ slf4j.spec ++++++
--- /var/tmp/diff_new_pack.iuzI85/_old  2026-10-01 17:00:11.025032692 +0200
+++ /var/tmp/diff_new_pack.iuzI85/_new  2026-10-01 17:00:11.029032860 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           slf4j
-Version:        2.0.18
+Version:        2.0.20
 Release:        0
 Summary:        Simple Logging Facade for Java
 # the log4j-over-slf4j and jcl-over-slf4j submodules are ASL 2.0, rest is MIT

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.iuzI85/_old  2026-10-01 17:00:11.095035624 +0200
+++ /var/tmp/diff_new_pack.iuzI85/_new  2026-10-01 17:00:11.099035792 +0200
@@ -1,6 +1,6 @@
-mtime: 1783942362
-commit: 0421d53bb61b789b2e722848e1cfb1df567986661edf959920be1c727b785119
+mtime: 1790680239
+commit: 754f14deb2c29730a9a6be8ec0219a254cab268b51864c07d0398323181ed0c6
 url: https://src.opensuse.org/java-packages/slf4j
-revision: 0421d53bb61b789b2e722848e1cfb1df567986661edf959920be1c727b785119
+revision: 754f14deb2c29730a9a6be8ec0219a254cab268b51864c07d0398323181ed0c6
 projectscmsync: https://src.opensuse.org/java-packages/_ObsPrj
 

++++++ _service ++++++
--- /var/tmp/diff_new_pack.iuzI85/_old  2026-10-01 17:00:11.126036923 +0200
+++ /var/tmp/diff_new_pack.iuzI85/_new  2026-10-01 17:00:11.130037091 +0200
@@ -2,7 +2,7 @@
        <service name="tar_scm" mode="disabled">
                <param name="scm">git</param>
                <param name="url">https://github.com/qos-ch/slf4j.git</param>
-               <param name="revision">v_2.0.18</param>
+               <param name="revision">v_2.0.20</param>
                <param name="match-tag">v_*</param>
                <param name="versionformat">@PARENT_TAG@</param>
                <param name="versionrewrite-pattern">v_(.*)</param>

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-29 13:10:39.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ slf4j-2.0.18.tar.xz -> slf4j-2.0.20.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/integration/pom.xml 
new/slf4j-2.0.20/integration/pom.xml
--- old/slf4j-2.0.18/integration/pom.xml        2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/integration/pom.xml        2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/jcl-over-slf4j/pom.xml 
new/slf4j-2.0.20/jcl-over-slf4j/pom.xml
--- old/slf4j-2.0.18/jcl-over-slf4j/pom.xml     2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/jcl-over-slf4j/pom.xml     2026-09-22 16:37:49.000000000 
+0200
@@ -5,7 +5,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/jul-to-slf4j/pom.xml 
new/slf4j-2.0.20/jul-to-slf4j/pom.xml
--- old/slf4j-2.0.18/jul-to-slf4j/pom.xml       2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/jul-to-slf4j/pom.xml       2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/log4j-over-slf4j/pom.xml 
new/slf4j-2.0.20/log4j-over-slf4j/pom.xml
--- old/slf4j-2.0.18/log4j-over-slf4j/pom.xml   2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/log4j-over-slf4j/pom.xml   2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/osgi-over-slf4j/pom.xml 
new/slf4j-2.0.20/osgi-over-slf4j/pom.xml
--- old/slf4j-2.0.18/osgi-over-slf4j/pom.xml    2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/osgi-over-slf4j/pom.xml    2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/parent/pom.xml 
new/slf4j-2.0.20/parent/pom.xml
--- old/slf4j-2.0.18/parent/pom.xml     2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/parent/pom.xml     2026-09-22 16:37:49.000000000 +0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-bom</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
 
@@ -26,7 +26,7 @@
 
   <properties>
     <!-- yyyy-MM-dd'T'HH:mm:ss'Z' -->
-    
<project.build.outputTimestamp>2026-05-12T18:15:00Z</project.build.outputTimestamp>
+    
<project.build.outputTimestamp>2026-09-22T14:37:00Z</project.build.outputTimestamp>
     <latest.1.version>1.7.36</latest.1.version>
     <!-- java.util.ServiceLoader requires Java 6 -->
     <jdk.version>8</jdk.version>
@@ -95,8 +95,10 @@
         <filtering>true</filtering>
       </resource>
 
+      <!-- Use each module's own LICENSE.txt (e.g. Apache-2.0 for 
jcl-over-slf4j
+           and log4j-over-slf4j) rather than the repo-root MIT LICENSE. -->
       <resource>
-        <directory>..</directory>
+        <directory>${project.basedir}</directory>
         <targetPath>META-INF</targetPath>
         <includes>
           <include>LICENSE.txt</include>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/pom.xml new/slf4j-2.0.20/pom.xml
--- old/slf4j-2.0.18/pom.xml    2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/pom.xml    2026-09-22 16:37:49.000000000 +0200
@@ -6,7 +6,7 @@
 
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-bom</artifactId>
-  <version>2.0.18</version>
+  <version>2.0.20</version>
   <packaging>pom</packaging>
 
   <url>http://www.slf4j.org</url>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-api/pom.xml 
new/slf4j-2.0.20/slf4j-api/pom.xml
--- old/slf4j-2.0.18/slf4j-api/pom.xml  2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-api/pom.xml  2026-09-22 16:37:49.000000000 +0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/Marker.java 
new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/Marker.java
--- old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/Marker.java      
2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/Marker.java      
2026-09-22 16:37:49.000000000 +0200
@@ -71,6 +71,8 @@
      *                a reference to another marker
      * @throws IllegalArgumentException
      *                 if 'reference' is null
+     *
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public void add(Marker reference);
 
@@ -80,11 +82,13 @@
      * @param reference
      *                the marker reference to remove
      * @return true if reference could be found and removed, false otherwise.
+     *
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public boolean remove(Marker reference);
 
     /**
-     * @deprecated Replaced by {@link #hasReferences()}.
+     * @deprecated Markers are now immutable and no longer support children.
      */
     @Deprecated
     public boolean hasChildren();
@@ -93,6 +97,7 @@
      * Does this marker have any references?
      * 
      * @return true if this marker has one or more references, false otherwise.
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public boolean hasReferences();
 
@@ -101,6 +106,7 @@
      * marker. An empty iterator is returned when this marker has no 
references.
      * 
      * @return Iterator over the references of this marker
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public Iterator<Marker> iterator();
 
@@ -114,6 +120,7 @@
      * @throws IllegalArgumentException
      *                 if 'other' is null
      * @return Whether this marker contains the other marker.
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public boolean contains(Marker other);
 
@@ -124,6 +131,7 @@
      * 
      * @param name The marker name to test for inclusion.
      * @return Whether this marker contains the other marker.
+     * @deprecated Markers are now immutable and no longer support children.
      */
     public boolean contains(String name);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java 
new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java
--- old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java 
2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/helpers/BasicMarker.java 
2026-09-22 16:37:49.000000000 +0200
@@ -53,6 +53,11 @@
         return name;
     }
 
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
+    @Deprecated
     public void add(Marker reference) {
         if (reference == null) {
             throw new IllegalArgumentException("A null value cannot be added 
to a Marker as reference.");
@@ -70,23 +75,48 @@
         }
     }
 
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
+    @Deprecated
     public boolean hasReferences() {
         return (referenceList.size() > 0);
     }
 
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
     @Deprecated
     public boolean hasChildren() {
         return hasReferences();
     }
 
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
+    @Deprecated
     public Iterator<Marker> iterator() {
         return referenceList.iterator();
     }
 
+
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
+    @Deprecated
     public boolean remove(Marker referenceToRemove) {
         return referenceList.remove(referenceToRemove);
     }
 
+    /**
+     * @deprecated Markers are now immutable and no longer support children.
+     */
+    @Override
+    @Deprecated
     public boolean contains(Marker other) {
         if (other == null) {
             throw new IllegalArgumentException("Other cannot be null");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
 
new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
--- 
old/slf4j-2.0.18/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
      2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java
      2026-09-22 16:37:49.000000000 +0200
@@ -32,6 +32,7 @@
 import org.slf4j.event.KeyValuePair;
 import org.slf4j.event.Level;
 import org.slf4j.event.LoggingEvent;
+import org.slf4j.helpers.Reporter;
 
 /**
  * Default implementation of {@link LoggingEventBuilder}
@@ -254,12 +255,33 @@
         for(KeyValuePair kvp : keyValuePairList) {
             sb.append(kvp.key);
             sb.append('=');
-            sb.append(kvp.value);
+            // Same protection as MessageFormatter.safeObjectAppend: a failing
+            // toString() (including StackOverflowError) must not abort 
logging.
+            // See https://github.com/qos-ch/slf4j/issues/448
+            safeObjectAppend(sb, kvp.value);
             sb.append(' ');
         }
         return sb;
     }
 
+    /**
+     * Append {@code o} to {@code sb}, catching any {@link Throwable} thrown by
+     * {@link Object#toString()} and substituting {@code [FAILED toString()]}.
+     * Mirrors {@code MessageFormatter.safeObjectAppend}.
+     */
+    private static void safeObjectAppend(StringBuilder sb, Object o) {
+        if (o == null) {
+            sb.append("null");
+            return;
+        }
+        try {
+            sb.append(o.toString());
+        } catch (Throwable t) {
+            Reporter.error("Failed toString() invocation on an object of type 
[" + o.getClass().getName() + "]", t);
+            sb.append("[FAILED toString()]");
+        }
+    }
+
     private String mergeMessage(String msg, StringBuilder sb) {
         if(sb != null) {
             sb.append(msg);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-ext/pom.xml 
new/slf4j-2.0.20/slf4j-ext/pom.xml
--- old/slf4j-2.0.18/slf4j-ext/pom.xml  2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-ext/pom.xml  2026-09-22 16:37:49.000000000 +0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-ext/src/main/java/org/slf4j/ext/LoggerWrapper.java 
new/slf4j-2.0.20/slf4j-ext/src/main/java/org/slf4j/ext/LoggerWrapper.java
--- old/slf4j-2.0.18/slf4j-ext/src/main/java/org/slf4j/ext/LoggerWrapper.java   
2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-ext/src/main/java/org/slf4j/ext/LoggerWrapper.java   
2026-09-22 16:37:49.000000000 +0200
@@ -28,7 +28,9 @@
 import org.slf4j.Marker;
 //import org.slf4j.helpers.FormattingTuple;
 //import org.slf4j.helpers.MessageFormatter;
+import org.slf4j.event.Level;
 import org.slf4j.spi.LocationAwareLogger;
+import org.slf4j.spi.LoggingEventBuilder;
 
 /**
  * A helper class wrapping an {@link org.slf4j.Logger} instance preserving
@@ -62,6 +64,16 @@
     }
 
     /**
+     * Delegate fluent-API builder creation to the wrapped logger so that 
caller
+     * location is extracted correctly. See also
+     * https://github.com/qos-ch/slf4j/issues/464
+     */
+    @Override
+    public LoggingEventBuilder makeLoggingEventBuilder(Level level) {
+        return logger.makeLoggingEventBuilder(level);
+    }
+
+    /**
      * Delegate to the appropriate method of the underlying logger.
      */
     public boolean isTraceEnabled() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-ext/src/test/java/org/slf4j/dummyExt/XLoggerTest.java 
new/slf4j-2.0.20/slf4j-ext/src/test/java/org/slf4j/dummyExt/XLoggerTest.java
--- 
old/slf4j-2.0.18/slf4j-ext/src/test/java/org/slf4j/dummyExt/XLoggerTest.java    
    2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-ext/src/test/java/org/slf4j/dummyExt/XLoggerTest.java    
    2026-09-22 16:37:49.000000000 +0200
@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2004-2011 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
@@ -151,16 +151,29 @@
             assertEquals("" + (line + 1), li.getLineNumber());
         }
     }
-    
+
     @Test
     public void testNoDoubleSubstitution_Bug421() {
         XLogger logger = XLoggerFactory.getXLogger("UnitTest");
         logger.error("{},{}", "foo", "[{}]");
         verify(listAppender.list.get(0), "foo,[{}]");
-        
+
         logger.error("{},{}", "[{}]", "foo");
         verify(listAppender.list.get(1), "[{}],foo");
     }
-    
-    
+
+    @Test
+    public void callerExtraction_issue464() {
+        XLogger logger = 
XLoggerFactory.getXLogger("com.foo.callerExtraction_issue464");
+        logger.atInfo().log("hello");
+        assertEquals(1, listAppender.list.size());
+
+
+        LoggingEvent e = listAppender.list.get(0);
+        LocationInfo li = e.getLocationInformation();
+        assertEquals(this.getClass().getName(), li.getClassName());
+        assertEquals("callerExtraction_issue464", li.getMethodName());
+
+    }
+
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-jdk-platform-logging/pom.xml 
new/slf4j-2.0.20/slf4j-jdk-platform-logging/pom.xml
--- old/slf4j-2.0.18/slf4j-jdk-platform-logging/pom.xml 2026-05-12 
20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-jdk-platform-logging/pom.xml 2026-09-22 
16:37:49.000000000 +0200
@@ -8,7 +8,7 @@
   <parent>
     <artifactId>slf4j-parent</artifactId>
     <groupId>org.slf4j</groupId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
 
new/slf4j-2.0.20/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
--- 
old/slf4j-2.0.18/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
   2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java
   2026-09-22 16:37:49.000000000 +0200
@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2004-2021 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
@@ -42,7 +42,7 @@
 class SLF4JPlatformLogger implements System.Logger {
 
     static private final String PRESUMED_CALLER_BOUNDARY = 
System.Logger.class.getName();
-                    
+
     private final Logger slf4jLogger;
 
     public SLF4JPlatformLogger(Logger logger) {
@@ -59,12 +59,12 @@
 
     @Override
     public boolean isLoggable(Level jplLevel) {
-        if (jplLevel == Level.ALL)
-            return true;
-        if (jplLevel == Level.OFF)
-            return true;
+        // If called via log method, fixExtremeLevels has been called. 
System.Logger.Level.ALL and Level.OFF have
+        // been respectively mapped to Level.TRACE and Level.ERROR.
+        // However, if  isLoggable is called directly, we must fix the extreme 
levels here as well.
+        final Level jplLevelReduced = fixExtremeLevels(jplLevel);
 
-        org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
+        org.slf4j.event.Level slf4jLevel = 
jplLevelToSLF4JLevel(jplLevelReduced);
 
         return slf4jLogger.isEnabledForLevel(slf4jLevel);
     }
@@ -72,27 +72,27 @@
 
     /**
      * Transform a {@link Level} to {@link org.slf4j.event.Level}.
-     * 
+     *
      * This method assumes that Level.ALL or Level.OFF never reach this method.
-     * 
+     *
      * @param jplLevel
      * @return
      */
     private org.slf4j.event.Level jplLevelToSLF4JLevel(Level jplLevel) {
-        switch (jplLevel) {
-        case TRACE:
-            return org.slf4j.event.Level.TRACE;
-        case DEBUG:
-            return org.slf4j.event.Level.DEBUG;
-        case INFO:
-            return org.slf4j.event.Level.INFO;
-        case WARNING:
-            return org.slf4j.event.Level.WARN;
-        case ERROR:
-            return org.slf4j.event.Level.ERROR;
-        default:
-            reportUnknownLevel(jplLevel);
-            return null;
+        switch(jplLevel) {
+            case TRACE:
+                return org.slf4j.event.Level.TRACE;
+            case DEBUG:
+                return org.slf4j.event.Level.DEBUG;
+            case INFO:
+                return org.slf4j.event.Level.INFO;
+            case WARNING:
+                return org.slf4j.event.Level.WARN;
+            case ERROR:
+                return org.slf4j.event.Level.ERROR;
+            default:
+                reportUnknownLevel(jplLevel);
+                return null;
         }
     }
 
@@ -108,8 +108,8 @@
 
     /**
      * Single point of processing taking all possible parameters.
-     * 
-     * @param jplLevel 
+     *
+     * @param jplLevel
      * @param bundle
      * @param msg
      * @param thrown
@@ -122,7 +122,7 @@
         org.slf4j.event.Level slf4jLevel = 
jplLevelToSLF4JLevel(jplLevelReduced);
         boolean isEnabled = slf4jLogger.isEnabledForLevel(slf4jLevel);
 
-        if (isEnabled) {
+        if(isEnabled) {
             performLog(slf4jLevel, bundle, msg, thrown, params);
         }
     }
@@ -139,10 +139,10 @@
      * @return
      */
     private Level fixExtremeLevels(Level jplLevel) {
-        if (jplLevel == Level.OFF)
-            return  Level.ERROR;
+        if(jplLevel == Level.OFF)
+            return Level.ERROR;
 
-        if (jplLevel == Level.ALL)
+        if(jplLevel == Level.ALL)
             return Level.TRACE;
 
         return jplLevel;
@@ -151,18 +151,18 @@
     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);
-        if (thrown != null) {
+        if(thrown != null) {
             leb = leb.setCause(thrown);
         }
-        if (params != null && params.length > 0) {
+        if(params != null && params.length > 0) {
             // add the arguments to the logging event for possible processing 
by the backend
-            for (Object p : params) {
+            for(Object p : params) {
                 leb = leb.addArgument(p);
             }
             // The JDK uses a different formatting convention. We must invoke 
it now.
             message = MessageFormat.format(message, params);
         }
-        if (leb instanceof CallerBoundaryAware) {
+        if(leb instanceof CallerBoundaryAware) {
             CallerBoundaryAware cba = (CallerBoundaryAware) leb;
             cba.setCallerBoundary(PRESUMED_CALLER_BOUNDARY);
         }
@@ -176,7 +176,7 @@
     }
 
     private static String getResourceStringOrMessage(ResourceBundle bundle, 
String msg) {
-        if (bundle == null || msg == null)
+        if(bundle == null || msg == null)
             return msg;
         // ResourceBundle::getString throws:
         //
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
 
new/slf4j-2.0.20/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
--- 
old/slf4j-2.0.18/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
 2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-jdk-platform-logging/src/test/java/org/slf4j/jdk/platform/logging/test/SLF4JPlatformLoggingTest.java
 2026-09-22 16:37:49.000000000 +0200
@@ -24,9 +24,6 @@
  */
 package org.slf4j.jdk.platform.logging.test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.System.Logger;
@@ -41,6 +38,8 @@
 import org.junit.Test;
 import org.slf4j.LoggerFactory;
 
+import static org.junit.Assert.*;
+
 /**
  * The present test is fragile in the sense that it sets up SimpleLogger
  * with a StringPrintStream and reverts to the old stream when done.
@@ -129,7 +128,19 @@
         List<String> results = SPS.stringList;
         assertEquals(1, results.size());
         assertEquals("ERROR extremeLevels - hello", results.get(0));
-
     }
 
+    @Test
+    public void extremeLevels_isLoggableTest() throws IOException {
+        LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
+        assertEquals(EXPECTED_FINDER_CLASS, finder.getClass().getName());
+        Logger systemLogger = finder.getLogger("extremeLevels", null);
+
+        boolean offResult = systemLogger.isLoggable(Level.OFF);
+        assertTrue(offResult);
+
+
+        boolean allResult = systemLogger.isLoggable(Level.ALL);
+        assertFalse(allResult);
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-jdk14/pom.xml 
new/slf4j-2.0.20/slf4j-jdk14/pom.xml
--- old/slf4j-2.0.18/slf4j-jdk14/pom.xml        2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/slf4j-jdk14/pom.xml        2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-jdk14/src/test/java/org/slf4j/issue/CallerInfoTest.java 
new/slf4j-2.0.20/slf4j-jdk14/src/test/java/org/slf4j/issue/CallerInfoTest.java
--- 
old/slf4j-2.0.18/slf4j-jdk14/src/test/java/org/slf4j/issue/CallerInfoTest.java  
    2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-jdk14/src/test/java/org/slf4j/issue/CallerInfoTest.java  
    2026-09-22 16:37:49.000000000 +0200
@@ -77,6 +77,7 @@
 
         LogRecord logRecod = recordList.get(0);
         assertEquals(this.getClass().getName(), logRecod.getSourceClassName());
+        assertEquals("testCallerInfoWithFluentAPI", 
logRecod.getSourceMethodName());
     }
 
     @Test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-jdk14/src/test/java/org/slf4j/jul/FluentApiInvocationTest.java
 
new/slf4j-2.0.20/slf4j-jdk14/src/test/java/org/slf4j/jul/FluentApiInvocationTest.java
--- 
old/slf4j-2.0.18/slf4j-jdk14/src/test/java/org/slf4j/jul/FluentApiInvocationTest.java
       2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-jdk14/src/test/java/org/slf4j/jul/FluentApiInvocationTest.java
       2026-09-22 16:37:49.000000000 +0200
@@ -130,6 +130,35 @@
 
     }
 
+    /**
+     * Regression for https://github.com/qos-ch/slf4j/issues/448:
+     * a toString() that throws (including StackOverflowError) on an
+     * addKeyValue value must not abort logging; same as message args.
+     */
+    @Test
+    public void keyValuePairWithFailingToString() {
+        Object bad = new Object() {
+            @Override
+            public String toString() {
+                throw new IllegalStateException("boom");
+            }
+        };
+        logger.atDebug().addKeyValue("key", bad).log("msg with key/value");
+        assertLogMessage("key=[FAILED toString()] msg with key/value", 0);
+    }
+
+    @Test
+    public void keyValuePairWithStackOverflowInToString() {
+        Object overflow = new Object() {
+            @Override
+            public String toString() {
+                return super.toString() + this.toString();
+            }
+        };
+        logger.atDebug().addKeyValue("key", overflow).log("msg with 
key/value");
+        assertLogMessage("key=[FAILED toString()] msg with key/value", 0);
+    }
+
     private void assertLogMessage(String expected, int index) {
         LogRecord logRecord = listHandler.recordList.get(index);
         Assert.assertNotNull(logRecord);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-log4j12/pom.xml 
new/slf4j-2.0.20/slf4j-log4j12/pom.xml
--- old/slf4j-2.0.18/slf4j-log4j12/pom.xml      2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/slf4j-log4j12/pom.xml      2026-09-22 16:37:49.000000000 
+0200
@@ -8,7 +8,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
   
@@ -24,7 +24,7 @@
     <relocation>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-reload4j</artifactId>
-      <version>2.0.18</version>      
+      <version>2.0.20</version>      
     </relocation>
   </distributionManagement>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-migrator/pom.xml 
new/slf4j-2.0.20/slf4j-migrator/pom.xml
--- old/slf4j-2.0.18/slf4j-migrator/pom.xml     2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/slf4j-migrator/pom.xml     2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-nop/pom.xml 
new/slf4j-2.0.20/slf4j-nop/pom.xml
--- old/slf4j-2.0.18/slf4j-nop/pom.xml  2026-05-12 20:32:21.000000000 +0200
+++ new/slf4j-2.0.20/slf4j-nop/pom.xml  2026-09-22 16:37:49.000000000 +0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-reload4j/pom.xml 
new/slf4j-2.0.20/slf4j-reload4j/pom.xml
--- old/slf4j-2.0.18/slf4j-reload4j/pom.xml     2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/slf4j-reload4j/pom.xml     2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/slf4j-2.0.18/slf4j-simple/pom.xml 
new/slf4j-2.0.20/slf4j-simple/pom.xml
--- old/slf4j-2.0.18/slf4j-simple/pom.xml       2026-05-12 20:32:21.000000000 
+0200
+++ new/slf4j-2.0.20/slf4j-simple/pom.xml       2026-09-22 16:37:49.000000000 
+0200
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-parent</artifactId>
-    <version>2.0.18</version>
+    <version>2.0.20</version>
     <relativePath>../parent/pom.xml</relativePath>
   </parent>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/AcceptanceTest.java
 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/AcceptanceTest.java
--- 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/AcceptanceTest.java
    2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/AcceptanceTest.java
    1970-01-01 01:00:00.000000000 +0100
@@ -1,50 +0,0 @@
-package org.slf4j.simple;
-
-import org.junit.Ignore;
-import org.slf4j.Logger;
-import org.slf4j.event.Level;
-
-import java.io.PrintStream;
-
-@Ignore
-public class AcceptanceTest extends LoggerTestSuite {
-
-    @Override
-    public Logger createLogger(ListAppendingOutputStream outputStream, Level 
level) {
-        SimpleLogger.CONFIG_PARAMS.outputChoice = new OutputChoice(new 
PrintStream(outputStream));
-
-        SimpleLogger logger = new SimpleLogger("TestSuiteLogger");
-        logger.currentLogLevel = 
SimpleLoggerConfiguration.stringToLevel(level.toString());
-        return logger;
-    }
-
-    @Override
-    public String extractMessage(String message) {
-        return message
-                .split("\n")[0]
-                .split("- ")[1];
-    }
-
-    @Override
-    public String extractExceptionMessage(String message) {
-        String[] logLines = message.split("\n");
-
-        if (logLines.length < 2) {
-            return null;
-        }
-        String exceptionLine = logLines[1];
-        return exceptionLine.split(": ")[1];
-    }
-
-    @Override
-    public String extractExceptionType(String message) {
-        String[] logLines = message.split("\n");
-
-        if (logLines.length < 2) {
-            return null;
-        }
-        String exceptionLine = logLines[1];
-        return exceptionLine.split(": ")[0];
-    }
-
-}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/LoggerTestSuite.java
 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/LoggerTestSuite.java
--- 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/LoggerTestSuite.java
   2026-05-12 20:32:21.000000000 +0200
+++ 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/LoggerTestSuite.java
   1970-01-01 01:00:00.000000000 +0100
@@ -1,250 +0,0 @@
-package org.slf4j.simple;
-
-
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.event.Level;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-
-public abstract class LoggerTestSuite {
-
-    private ListAppendingOutputStream prepareSink(List<String> outputList) {
-        return new ListAppendingOutputStream(outputList);
-    }
-
-    @Test
-    public void testTrace() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.TRACE);
-
-        assertTrue("Trace level should be enabled for this test", 
configuredLogger.isTraceEnabled());
-        configuredLogger.trace("Simple trace message");
-
-        assertEquals("Trace message should've been captured", 1, 
outputList.size());
-        assertTrue("Message should be logged in trace level", 
isTraceMessage(outputList.get(0)));
-        assertEquals("Supplied trace message wasn't found in the log",
-                     "Simple trace message",
-                     extractMessage(outputList.get(0)));
-
-        outputList.clear();
-
-        configuredLogger.debug("Simple debug message");
-        configuredLogger.info("Simple info message");
-        configuredLogger.warn("Simple warn message");
-        configuredLogger.error("Simple error message");
-        assertEquals("The other levels should have been captured", 4, 
outputList.size());
-
-    }
-
-    @Test
-    public void testDebug() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.DEBUG);
-
-        configuredLogger.trace("Simple trace message");
-        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
-
-        assertTrue("Debug level should be enabled for this test", 
configuredLogger.isDebugEnabled());
-        configuredLogger.debug("Simple debug message");
-
-        assertEquals("Debug message should've been captured", 1, 
outputList.size());
-        assertTrue("Message should be logged in debug level", 
isDebugMessage(outputList.get(0)));
-        assertEquals("Supplied debug message wasn't found in the log",
-                     "Simple debug message",
-                     extractMessage(outputList.get(0)));
-
-        outputList.clear();
-
-        configuredLogger.info("Simple info message");
-        configuredLogger.warn("Simple warn message");
-        configuredLogger.error("Simple error message");
-        assertEquals("The other levels should have been captured", 3, 
outputList.size());
-    }
-
-
-    @Test
-    public void testInfo() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
-
-        configuredLogger.trace("Simple trace message");
-        configuredLogger.debug("Simple debug message");
-        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
-
-        assertTrue("Info level should be enabled for this test", 
configuredLogger.isInfoEnabled());
-        configuredLogger.info("Simple info message");
-
-        assertEquals("Info message should've been captured", 1, 
outputList.size());
-        assertTrue("Message should be logged in debug level", 
isInfoMessage(outputList.get(0)));
-        assertEquals("Supplied info message wasn't found in the log",
-                     "Simple info message",
-                     extractMessage(outputList.get(0)));
-
-        outputList.clear();
-
-        configuredLogger.warn("Simple warn message");
-        configuredLogger.error("Simple error message");
-        assertEquals("The other levels should have been captured", 2, 
outputList.size());
-    }
-
-    @Test
-    public void testWarn() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.WARN);
-
-        configuredLogger.trace("Simple trace message");
-        configuredLogger.debug("Simple debug message");
-        configuredLogger.info("Simple info message");
-        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
-
-        assertTrue("Warn level should be enabled for this test", 
configuredLogger.isWarnEnabled());
-        configuredLogger.warn("Simple warn message");
-
-        assertEquals("Warn message should've been captured", 1, 
outputList.size());
-        assertTrue("Message should be logged in warn level", 
isWarnMessage(outputList.get(0)));
-        assertEquals("Supplied warn message wasn't found in the log",
-                     "Simple warn message",
-                     extractMessage(outputList.get(0)));
-
-        outputList.clear();
-
-        configuredLogger.error("Simple error message");
-        assertEquals("The other levels should have been captured", 1, 
outputList.size());
-    }
-
-    @Test
-    public void testError() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.ERROR);
-
-        configuredLogger.trace("Simple trace message");
-        configuredLogger.debug("Simple debug message");
-        configuredLogger.info("Simple info message");
-        configuredLogger.warn("Simple warn message");
-        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
-
-        assertTrue("Error level should be enabled for this test", 
configuredLogger.isErrorEnabled());
-        configuredLogger.error("Simple error message");
-
-        assertEquals("Error message should've been captured", 1, 
outputList.size());
-        assertTrue("Message should be logged in error level", 
isErrorMessage(outputList.get(0)));
-        assertEquals("Supplied error message wasn't found in the log",
-                     "Simple error message",
-                     extractMessage(outputList.get(0)));
-    }
-
-    @Test
-    public void testFormatting() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
-
-        configuredLogger.info("Some {} string", "formatted");
-        assertEquals("The formatted message should've been captured", 1, 
outputList.size());
-        assertEquals("Message should've been formatted", "Some formatted 
string", extractMessage(outputList.get(0)));
-    }
-
-    @Test
-    public void testException() {
-        ArrayList<String> outputList = new ArrayList<>();
-        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
-
-        Exception exception = new RuntimeException("My error");
-
-        configuredLogger.info("Logging with an exception", exception);
-        assertEquals("The formatted message should've been captured", 1, 
outputList.size());
-        assertEquals("Message should've been formatted",
-                     "My error",
-                     extractExceptionMessage(outputList.get(0)));
-
-        assertEquals("Message should've been formatted",
-                     "java.lang.RuntimeException",
-                     extractExceptionType(outputList.get(0)));
-    }
-
-
-    /**
-     * Allows tests to check whether the log message contains a trace message.
-     * Override if needed.
-     * @param message String containing the full log message
-     * @return whether it is a trace message or not
-     */
-    protected boolean isTraceMessage(String message) {
-        return message.toLowerCase().contains("trace");
-    }
-
-    /**
-     * Allows tests to check whether the log message contains a debug message.
-     * Override if needed.
-     * @param message String containing the full log message
-     * @return whether it is a debug message or not
-     */
-    protected boolean isDebugMessage(String message) {
-        return message.toLowerCase().contains("debug");
-    }
-
-    /**
-     * Allows tests to check whether the log message contains an info message.
-     * Override if needed.
-     * @param message String containing the full log message
-     * @return whether it is an info message or not
-     */
-    protected boolean isInfoMessage(String message) {
-        return message.toLowerCase().contains("info");
-    }
-
-    /**
-     * Allows tests to check whether the log message contains a warn message.
-     * Override if needed.
-     * @param message String containing the full log message
-     * @return whether it is a warn message or not
-     */
-    protected boolean isWarnMessage(String message) {
-        return message.toLowerCase().contains("warn");
-    }
-
-    /**
-     * Allows tests to check whether the log message contains an error message.
-     * Override if needed.
-     * @param message String containing the full log message
-     * @return whether it is an error message or not
-     */
-    protected boolean isErrorMessage(String message) {
-        return message.toLowerCase().contains("error");
-    }
-
-    /**
-     * Extracts only the part of the log string that should represent the 
`message` string.
-     * @param message the full log message
-     * @return only the supplied message
-     */
-    public abstract String extractMessage(String message);
-
-    /**
-     * Extracts only the part of the log string that should represent the 
supplied exception message, if any.
-     * @param message the full log message
-     * @return only the supplied exception message
-     */
-    public abstract String extractExceptionMessage(String message);
-
-    /**
-     * Extracts only the part of the log string that should represent the 
supplied exception type.
-     * @param message the full log message
-     * @return only the supplied exception type name
-     */
-    public abstract String extractExceptionType(String message);
-
-    /**
-     * Configures the logger for running the tests.
-     * @param outputStream The output stream for logs to be written to
-     * @param level The expected level the tests will run for this logger
-     * @return a configured logger able to run the tests
-     */
-    public abstract Logger createLogger(ListAppendingOutputStream 
outputStream, Level level);
-
-}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/SimpleLoggerAcceptanceTest.java
 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/SimpleLoggerAcceptanceTest.java
--- 
old/slf4j-2.0.18/slf4j-simple/src/test/java/org/slf4j/simple/SimpleLoggerAcceptanceTest.java
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/slf4j-2.0.20/slf4j-simple/src/test/java/org/slf4j/simple/SimpleLoggerAcceptanceTest.java
        2026-09-22 16:37:49.000000000 +0200
@@ -0,0 +1,358 @@
+package org.slf4j.simple;
+
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.event.Level;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Acceptance tests for the slf4j-simple SimpleLogger implementation.
+ *
+ * <p>This concrete JUnit test class exercises logging at different levels
+ * (TRACE, DEBUG, INFO, WARN, ERROR), verifies message formatting, and
+ * checks exception logging behavior. The class contains helper methods to
+ * create a logger that writes into a supplied {@link 
ListAppendingOutputStream}
+ * and to extract message and exception details from produced log lines.</p>
+ *
+ * <p>Important: these tests are predicated on the fact that {@code 
SimpleLogger}
+ * invokes {@code flush()} on the provided output stream after writing each
+ * log entry and that each logging event is accumulated into a single entry of
+ * the backing {@code List<String>} fiels in {@link ListAppendingOutputStream}.
+ * </p>
+ *
+ * <p>Each logging event — including events that contain a {@code Throwable}
+ * and produce stacktrace lines — is accumulated into the {@code targetList}
+ * field of {@link ListAppendingOutputStream} as a single entry representing
+ * the entire event. Tests expect one list element per logging event.</p>
+ *
+ */
+public class SimpleLoggerAcceptanceTest {
+
+    /**
+     * Create a {@link ListAppendingOutputStream} backed by the supplied list.
+     *
+     * <p>This helper returns an output stream that accumulates written text
+     * lines into the provided list. Tests rely on the {@code SimpleLogger}
+     * implementation to invoke {@code flush()} on the provided stream after
+     * writing each log entry so that bytes are converted into list entries and
+     * visible to assertions. </p>
+     *
+     * <p>Each logging event (including a {@code Throwable} and its stacktrace)
+     * is collected by {@code ListAppendingOutputStream} into a single element
+     * in its {@code targetList} field; tests expect one string element per 
event.</p>
+     *
+     * @param outputList the list which will receive captured log lines
+     * @return a new ListAppendingOutputStream writing into the supplied list
+     */
+    private ListAppendingOutputStream prepareSink(List<String> outputList) {
+        return new ListAppendingOutputStream(outputList);
+    }
+
+    /**
+     * Extracts only the part of the log string that should represent the 
`message` string.
+     *
+     * @param message the full log message
+     * @return only the supplied message
+     */
+    public String extractMessage(String message) {
+        // Example log line:
+        // [main] INFO TestSuiteLogger - An exception occurred: 99
+        return message
+                .split("\r?\n")[0]
+                .split("- ")[1];
+    }
+
+    /**
+     * Extracts only the part of the log string that should represent the 
supplied exception message, if any.
+     *
+     * @param message the full log message
+     * @return only the supplied exception message
+     */
+    public String extractExceptionMessage(String message) {
+        // Example log lines:
+        // [main] INFO TestSuiteLogger - An exception occurred: 99
+        // java.lang.IllegalArgumentException: Invalid argument
+        //  at 
org.slf4j.simple.SimpleLoggerAcceptanceTest.testExceptionParameterFormatting(SimpleLoggerAcceptanceTest.java:274)
+
+        String[] logLines = message.split("\r?\n");
+
+        if(logLines.length < 2) {
+            return null;
+        }
+        String exceptionLine = logLines[1];
+        return exceptionLine.split(": ")[1];
+    }
+
+    /**
+     * Extracts only the part of the log string that should represent the 
supplied exception type.
+     *
+     * @param message the full log message
+     * @return only the supplied exception type name
+     */
+    public String extractExceptionType(String message) {
+        String[] logLines = message.split("\n");
+
+        if(logLines.length < 2) {
+            return null;
+        }
+        String exceptionLine = logLines[1];
+        return exceptionLine.split(": ")[0];
+    }
+
+    /**
+     * Configures the logger for running the tests.
+     *
+     * @param outputStream The output stream for logs to be written to
+     * @param level        The expected level the tests will run for this 
logger
+     * @return a configured logger able to run the tests
+     */
+    public Logger createLogger(ListAppendingOutputStream outputStream, Level 
level) {
+        SimpleLogger.CONFIG_PARAMS.outputChoice = new OutputChoice(new 
PrintStream(outputStream));
+
+        SimpleLogger logger = new SimpleLogger("TestSuiteLogger");
+        logger.currentLogLevel = 
SimpleLoggerConfiguration.stringToLevel(level.toString());
+        return logger;
+    }
+
+    @Test
+    public void testTrace() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.TRACE);
+
+        assertTrue("Trace level should be enabled for this test", 
configuredLogger.isTraceEnabled());
+        configuredLogger.trace("Simple trace message");
+
+        assertEquals("Trace message should've been captured", 1, 
outputList.size());
+        assertTrue("Message should be logged in trace level", 
isTraceMessage(outputList.get(0)));
+        assertEquals("Supplied trace message wasn't found in the log",
+                     "Simple trace message",
+                     extractMessage(outputList.get(0)));
+
+        outputList.clear();
+
+        configuredLogger.debug("Simple debug message");
+        configuredLogger.info("Simple info message");
+        configuredLogger.warn("Simple warn message");
+        configuredLogger.error("Simple error message");
+        assertEquals("The other levels should have been captured", 4, 
outputList.size());
+
+    }
+
+    @Test
+    public void testDebug() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.DEBUG);
+
+        configuredLogger.trace("Simple trace message");
+        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
+
+        assertTrue("Debug level should be enabled for this test", 
configuredLogger.isDebugEnabled());
+        configuredLogger.debug("Simple debug message");
+
+        assertEquals("Debug message should've been captured", 1, 
outputList.size());
+        assertTrue("Message should be logged in debug level", 
isDebugMessage(outputList.get(0)));
+        assertEquals("Supplied debug message wasn't found in the log",
+                     "Simple debug message",  
extractMessage(outputList.get(0)));
+
+        outputList.clear();
+
+        configuredLogger.info("Simple info message");
+        configuredLogger.warn("Simple warn message");
+        configuredLogger.error("Simple error message");
+        assertEquals("The other levels should have been captured", 3, 
outputList.size());
+    }
+
+
+    @Test
+    public void testInfo() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
+
+        configuredLogger.trace("Simple trace message");
+        configuredLogger.debug("Simple debug message");
+        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
+
+        assertTrue("Info level should be enabled for this test", 
configuredLogger.isInfoEnabled());
+        configuredLogger.info("Simple info message");
+
+        assertEquals("Info message should've been captured", 1, 
outputList.size());
+        assertTrue("Message should be logged in debug level", 
isInfoMessage(outputList.get(0)));
+        assertEquals("Supplied info message wasn't found in the log",
+                     "Simple info message",
+                     extractMessage(outputList.get(0)));
+
+        outputList.clear();
+
+        configuredLogger.warn("Simple warn message");
+        configuredLogger.error("Simple error message");
+        assertEquals("The other levels should have been captured", 2, 
outputList.size());
+    }
+
+    @Test
+    public void testWarn() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.WARN);
+
+        configuredLogger.trace("Simple trace message");
+        configuredLogger.debug("Simple debug message");
+        configuredLogger.info("Simple info message");
+        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
+
+        assertTrue("Warn level should be enabled for this test", 
configuredLogger.isWarnEnabled());
+        configuredLogger.warn("Simple warn message");
+
+        assertEquals("Warn message should've been captured", 1, 
outputList.size());
+        assertTrue("Message should be logged in warn level", 
isWarnMessage(outputList.get(0)));
+        assertEquals("Supplied warn message wasn't found in the log",
+                     "Simple warn message",
+                     extractMessage(outputList.get(0)));
+
+        outputList.clear();
+
+        configuredLogger.error("Simple error message");
+        assertEquals("The other levels should have been captured", 1, 
outputList.size());
+    }
+
+    @Test
+    public void testError() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.ERROR);
+
+        configuredLogger.trace("Simple trace message");
+        configuredLogger.debug("Simple debug message");
+        configuredLogger.info("Simple info message");
+        configuredLogger.warn("Simple warn message");
+        assertEquals("Lower levels should have been ignored", 0, 
outputList.size());
+
+        assertTrue("Error level should be enabled for this test", 
configuredLogger.isErrorEnabled());
+        configuredLogger.error("Simple error message");
+
+        assertEquals("Error message should've been captured", 1, 
outputList.size());
+        assertTrue("Message should be logged in error level", 
isErrorMessage(outputList.get(0)));
+        assertEquals("Supplied error message wasn't found in the log",
+                     "Simple error message",
+                     extractMessage(outputList.get(0)));
+    }
+
+    @Test
+    public void testFormatting() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
+
+        configuredLogger.info("Some {} string", "formatted");
+        assertEquals("The formatted message should've been captured", 1, 
outputList.size());
+        assertEquals("Message should've been formatted", "Some formatted 
string", extractMessage(outputList.get(0)));
+    }
+
+    @Test
+    public void testException() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
+
+        Exception exception = new RuntimeException("My error");
+
+        configuredLogger.info("Logging with an exception", exception);
+        assertEquals("The formatted message should've been captured", 1, 
outputList.size());
+        assertEquals("Message should've been formatted",
+                     "My error",
+                     extractExceptionMessage(outputList.get(0)));
+
+        assertEquals("Message should've been formatted",
+                     "java.lang.RuntimeException",
+                     extractExceptionType(outputList.get(0)));
+    }
+
+
+    @Test
+    public void testExceptionParameterFormatting() {
+        ArrayList<String> outputList = new ArrayList<>();
+        Logger configuredLogger = createLogger(prepareSink(outputList), 
Level.INFO);
+
+        Exception exception = new IllegalArgumentException("Invalid argument");
+
+        configuredLogger.info("An exception occurred: {}", 99, exception);
+        assertEquals("The formatted message should've been captured", 1, 
outputList.size());
+        assertEquals("Message should've been formatted",
+                     "An exception occurred: 99",
+                     extractMessage(outputList.get(0)));
+
+        assertEquals("Exception type should've been captured",
+                     "java.lang.IllegalArgumentException",
+                     extractExceptionType(outputList.get(0)));
+
+
+        configuredLogger.info("Another exception occurred: {} {}", exception, 
99);
+        assertEquals("The formatted message should've been captured", 2, 
outputList.size());
+        assertEquals("Message should've been formatted",
+                     "Another exception occurred: 
java.lang.IllegalArgumentException: Invalid argument 99",
+                     extractMessage(outputList.get(1)));
+
+        AtomicInteger atomicInteger = new AtomicInteger();
+        outputList.forEach(s -> System.out.println("x Log line " + 
(atomicInteger.getAndIncrement()) + ": " + s));
+    }
+
+    /**
+     * Allows tests to check whether the log message contains a trace message.
+     * Override if needed.
+     *
+     * @param message String containing the full log message
+     * @return whether it is a trace message or not
+     */
+    protected boolean isTraceMessage(String message) {
+        return message.toLowerCase().contains("trace");
+    }
+
+    /**
+     * Allows tests to check whether the log message contains a debug message.
+     * Override if needed.
+     *
+     * @param message String containing the full log message
+     * @return whether it is a debug message or not
+     */
+    protected boolean isDebugMessage(String message) {
+        return message.toLowerCase().contains("debug");
+    }
+
+    /**
+     * Allows tests to check whether the log message contains an info message.
+     * Override if needed.
+     *
+     * @param message String containing the full log message
+     * @return whether it is an info message or not
+     */
+    protected boolean isInfoMessage(String message) {
+        return message.toLowerCase().contains("info");
+    }
+
+    /**
+     * Allows tests to check whether the log message contains a warn message.
+     * Override if needed.
+     *
+     * @param message String containing the full log message
+     * @return whether it is a warn message or not
+     */
+    protected boolean isWarnMessage(String message) {
+        return message.toLowerCase().contains("warn");
+    }
+
+    /**
+     * Allows tests to check whether the log message contains an error message.
+     * Override if needed.
+     *
+     * @param message String containing the full log message
+     * @return whether it is an error message or not
+     */
+    protected boolean isErrorMessage(String message) {
+        return message.toLowerCase().contains("error");
+    }
+
+}

++++++ slf4j-build.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/common.xml new/common.xml
--- old/common.xml      2026-05-13 17:06:23.519700208 +0200
+++ new/common.xml      2026-09-29 13:01:10.194716031 +0200
@@ -3,7 +3,7 @@
 <project name="common" basedir=".">
 
   <property file="build.properties"/>
-  <property name="project.version" value="2.0.18"/>
+  <property name="project.version" value="2.0.20"/>
   <property name="project.groupId" value="org.slf4j"/>
   <property name="project.url" value="http://www.slf4j.org/"/>
 

Reply via email to