diff --git a/integration/pom.xml b/integration/pom.xml
index d53dc4f..1bf94b9 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
integration
diff --git a/jcl-over-slf4j/pom.xml b/jcl-over-slf4j/pom.xml
index ae478d1..9dd9f61 100755
--- a/jcl-over-slf4j/pom.xml
+++ b/jcl-over-slf4j/pom.xml
@@ -5,7 +5,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
4.0.0
diff --git a/jul-to-slf4j/pom.xml b/jul-to-slf4j/pom.xml
index e424ce8..174185b 100755
--- a/jul-to-slf4j/pom.xml
+++ b/jul-to-slf4j/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
jul-to-slf4j
diff --git a/log4j-over-slf4j/pom.xml b/log4j-over-slf4j/pom.xml
index 2b14deb..e772a74 100755
--- a/log4j-over-slf4j/pom.xml
+++ b/log4j-over-slf4j/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
diff --git a/osgi-over-slf4j/pom.xml b/osgi-over-slf4j/pom.xml
index fbc8d0b..86a0691 100755
--- a/osgi-over-slf4j/pom.xml
+++ b/osgi-over-slf4j/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
osgi-over-slf4j
diff --git a/pom.xml b/pom.xml
index f6b6b0e..04c84f2 100755
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
pom
SLF4J
diff --git a/slf4j-android/pom.xml b/slf4j-android/pom.xml
index 9f1aedf..38433a1 100644
--- a/slf4j-android/pom.xml
+++ b/slf4j-android/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-android
diff --git a/slf4j-api/pom.xml b/slf4j-api/pom.xml
index 340fd7a..44ce4fb 100755
--- a/slf4j-api/pom.xml
+++ b/slf4j-api/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-api
diff --git a/slf4j-ext/pom.xml b/slf4j-ext/pom.xml
index 108034c..68b6ea8 100755
--- a/slf4j-ext/pom.xml
+++ b/slf4j-ext/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-ext
diff --git a/slf4j-jcl/pom.xml b/slf4j-jcl/pom.xml
index 0e55d11..298759c 100755
--- a/slf4j-jcl/pom.xml
+++ b/slf4j-jcl/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-jcl
diff --git a/slf4j-jdk14/pom.xml b/slf4j-jdk14/pom.xml
index 348e429..82fa83d 100755
--- a/slf4j-jdk14/pom.xml
+++ b/slf4j-jdk14/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-jdk14
diff --git a/slf4j-log4j12/pom.xml b/slf4j-log4j12/pom.xml
index f6a7794..2510f29 100755
--- a/slf4j-log4j12/pom.xml
+++ b/slf4j-log4j12/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-log4j12
diff --git a/slf4j-migrator/pom.xml b/slf4j-migrator/pom.xml
index 01d433a..72cf981 100755
--- a/slf4j-migrator/pom.xml
+++ b/slf4j-migrator/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-migrator
diff --git a/slf4j-nop/pom.xml b/slf4j-nop/pom.xml
index e8f50bb..4d15693 100755
--- a/slf4j-nop/pom.xml
+++ b/slf4j-nop/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-nop
diff --git a/slf4j-simple/pom.xml b/slf4j-simple/pom.xml
index 66e3977..d20f444 100755
--- a/slf4j-simple/pom.xml
+++ b/slf4j-simple/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-simple
diff --git a/slf4j-simple/src/main/java/org/slf4j/impl/SimpleLogger.java b/slf4j-simple/src/main/java/org/slf4j/impl/SimpleLogger.java
index fe79822..0a3aeea 100644
--- a/slf4j-simple/src/main/java/org/slf4j/impl/SimpleLogger.java
+++ b/slf4j-simple/src/main/java/org/slf4j/impl/SimpleLogger.java
@@ -157,7 +157,7 @@
protected static final int LOG_LEVEL_OFF = LOG_LEVEL_ERROR + 10;
private static boolean INITIALIZED = false;
- static SimpleLoggerConfiguration CONFIG_PARAMS = null;
+ static final private SimpleLoggerConfiguration CONFIG_PARAMS = new SimpleLoggerConfiguration();
static void lazyInit() {
if (INITIALIZED) {
@@ -170,7 +170,6 @@
// external software might be invoking this method directly. Do not rename
// or change its semantics.
static void init() {
- CONFIG_PARAMS = new SimpleLoggerConfiguration();
CONFIG_PARAMS.init();
}
@@ -312,12 +311,22 @@
throw new IllegalStateException("Unrecognized level [" + level + "]");
}
+ /**
+ * To avoid intermingling of log messages and associated stack traces, the two
+ * operations are done in a synchronized block.
+ *
+ * @param buf
+ * @param t
+ */
void write(StringBuilder buf, Throwable t) {
PrintStream targetStream = CONFIG_PARAMS.outputChoice.getTargetPrintStream();
- targetStream.println(buf.toString());
- writeThrowable(t, targetStream);
- targetStream.flush();
+ synchronized (CONFIG_PARAMS) {
+ targetStream.println(buf.toString());
+ writeThrowable(t, targetStream);
+ targetStream.flush();
+ }
+
}
protected void writeThrowable(Throwable t, PrintStream targetStream) {
diff --git a/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/MultithereadedExecutionTest.java b/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/MultithereadedExecutionTest.java
new file mode 100755
index 0000000..3888cbc
--- /dev/null
+++ b/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/MultithereadedExecutionTest.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2004-2021 QOS.ch
+ * All rights reserved.
+ *
+ * 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
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * 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.simple.multiThreadedExecution;
+
+import java.io.PrintStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests that output in multi-threaded environments is not mingled.
+ *
+ * See also https://jira.qos.ch/browse/SLF4J-515
+ */
+public class MultithereadedExecutionTest {
+
+ private static int THREAD_COUNT = 2;
+ private static long TEST_DURATION_IN_MILLIS = 100;
+
+ private Thread[] threads = new Thread[THREAD_COUNT];
+
+ private final PrintStream oldOut = System.out;
+ StateCheckingPrintStream scps = new StateCheckingPrintStream(oldOut);
+
+ volatile boolean signal = false;
+
+ @Before
+ public void setup() {
+ System.setErr(scps);
+ // System.setProperty(SimpleLogger.LOG_FILE_KEY, "System.err");
+ // LoggerFactoryFriend.reset();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // LoggerFactoryFriend.reset();
+ // System.clearProperty(SimpleLogger.LOG_FILE_KEY);
+ System.setErr(oldOut);
+ }
+
+ @Test
+ public void test() throws Throwable {
+ WithException withException = new WithException();
+ Other other = new Other();
+ threads[0] = new Thread(withException);
+ threads[1] = new Thread(other);
+ threads[0].start();
+ threads[1].start();
+ Thread.sleep(TEST_DURATION_IN_MILLIS);
+ signal = true;
+ threads[0].join();
+ threads[1].join();
+
+ if (withException.throwable != null) {
+ throw withException.throwable;
+ }
+
+ if (other.throwable != null) {
+ throw other.throwable;
+ }
+
+ }
+
+ class WithException implements Runnable {
+
+ volatile Throwable throwable;
+ Logger logger = LoggerFactory.getLogger(WithException.class);
+
+ public void run() {
+ int i = 0;
+
+ while (!signal) {
+ try {
+ logger.info("Hello {}", i, new Throwable("i=" + i));
+ i++;
+ } catch (Throwable t) {
+ throwable = t;
+ MultithereadedExecutionTest.this.signal = true;
+ return;
+ }
+ }
+
+ }
+ }
+
+ class Other implements Runnable {
+ volatile Throwable throwable;
+ Logger logger = LoggerFactory.getLogger(Other.class);
+
+ public void run() {
+ int i = 0;
+ while (!signal) {
+ try {
+ logger.info("Other {}", i++);
+ } catch (Throwable t) {
+ throwable = t;
+ MultithereadedExecutionTest.this.signal = true;
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/StateCheckingPrintStream.java b/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/StateCheckingPrintStream.java
new file mode 100755
index 0000000..e4dbe14
--- /dev/null
+++ b/slf4j-simple/src/test/java/org/slf4j/simple/multiThreadedExecution/StateCheckingPrintStream.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2004-2021 QOS.ch
+ * All rights reserved.
+ *
+ * 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
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * 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.simple.multiThreadedExecution;
+
+import java.io.PrintStream;
+import java.util.regex.Pattern;
+
+/**
+ * This PrintStream checks that output lines are in an expected order.
+ *
+ * @author ceki
+ */
+public class StateCheckingPrintStream extends PrintStream {
+
+ enum State {
+ INITIAL, UNKNOWN, HELLO, THROWABLE, AT1, AT2, OTHER;
+ }
+
+ volatile State currentState = State.INITIAL;
+
+ public StateCheckingPrintStream(PrintStream ps) {
+ super(ps);
+ }
+
+ public void print(String s) {
+ }
+
+ public void println(String s) {
+
+ State next = computeState(s);
+ //System.out.println(next + " " + s);
+ switch (currentState) {
+ case INITIAL:
+ currentState = next;
+ break;
+
+ case UNKNOWN:
+ // ignore garbage
+ currentState = next;
+ break;
+
+ case OTHER:
+ if (next == State.UNKNOWN) {
+ currentState = State.UNKNOWN;
+ return;
+ }
+
+ if (next != State.OTHER && next != State.HELLO) {
+ throw badState(s, currentState, next);
+ }
+ currentState = next;
+ break;
+
+ case HELLO:
+ if (next != State.THROWABLE) {
+ throw badState(s, currentState, next);
+ }
+ currentState = next;
+ break;
+ case THROWABLE:
+ if (next != State.AT1) {
+ throw badState(s, currentState, next);
+ }
+ currentState = next;
+ break;
+
+ case AT1:
+ if (next != State.AT2) {
+ throw badState(s, currentState, next);
+ }
+ currentState = next;
+ break;
+
+ case AT2:
+ currentState = next;
+ break;
+ default:
+ throw new IllegalStateException("Unreachable code");
+ }
+ }
+
+ private IllegalStateException badState(String s, State currentState2, State next) {
+ return new IllegalStateException("Unexpected state " + next + " for current state " + currentState2 + " for " + s);
+
+ }
+
+ String OTHER_PATTERN_STR = ".*Other \\d{1,5}";
+ String HELLO_PATTERN_STR = ".*Hello \\d{1,5}";
+ String THROWABLE_PATTERN_STR = "java.lang.Throwable: i=\\d{1,5}";
+ String AT1_PATTERN_STR = "\\s*at " + this.getClass().getPackage().getName() + ".*";
+ String AT2_PATTERN_STR = "\\s*at " + ".*Thread.java.*";
+
+ Pattern PATTERN_OTHER = Pattern.compile(OTHER_PATTERN_STR);
+ Pattern PATTERN_HELLO = Pattern.compile(HELLO_PATTERN_STR);
+ Pattern PATTERN_THROWABLE = Pattern.compile(THROWABLE_PATTERN_STR);
+ Pattern PATTERN_AT1 = Pattern.compile(AT1_PATTERN_STR);
+ Pattern PATTERN_AT2 = Pattern.compile(AT2_PATTERN_STR);
+
+ private State computeState(String s) {
+
+ if (PATTERN_OTHER.matcher(s).matches()) {
+ return State.OTHER;
+ } else if (PATTERN_HELLO.matcher(s).matches()) {
+ return State.HELLO;
+ } else if (PATTERN_THROWABLE.matcher(s).matches()) {
+ return State.THROWABLE;
+ } else if (PATTERN_AT1.matcher(s).matches()) {
+ return State.AT1;
+ } else if (PATTERN_AT2.matcher(s).matches()) {
+ return State.AT2;
+ } else {
+ return State.UNKNOWN;
+ }
+ }
+
+ public void println(Object o) {
+ println(o.toString());
+ }
+}
\ No newline at end of file
diff --git a/slf4j-site/pom.xml b/slf4j-site/pom.xml
index e38d4ba..21c64fb 100755
--- a/slf4j-site/pom.xml
+++ b/slf4j-site/pom.xml
@@ -7,7 +7,7 @@
org.slf4j
slf4j-parent
- 1.7.31
+ 1.7.32
slf4j-site