I'm writing a multithreaded Java program where each thread potentially needs its standard output redirected to a separate file. Each thread would have its own file. Is it possible to redirect System.out on a "per-thread" basis or are changes to System.out global across all threads?
Is it possible to redirect System.out on a "per-thread" basis
No it is not possible. System.out
is static and there is one per JVM that is loaded as part of the system classloader when the JVM initially boots. Although of course using proper logging calls per-thread is recommend, I assume there are reasons why you can't do this. Probably a 3rd party library or other code is what is using System.out
in this manner.
One thing you could do (as a radical suggestion) is to make your own PrintStream
that delegates to a ThreadLocal<PrintStream>
. But you will need to @Override
the appropriate methods called by your application to get it to work per-thread.
Lastly, if you are asking this because you are worried about concurrency, System.out
is a PrintStream
so it is already synchronized
under the covers and can be used safely by multiple threads.
java.lang.System
class can only be loaded by the bootstrap class loader (along with all other java.lang
classes). –
Inwardness ClassLoader
. Your statement caused confusion in this question, which is why I downvoted (if you can clarify what you were trying to say, we can edit your answer, and I'll remove the downvote). –
Inwardness Is it possible to redirect System.out on a "per-thread" basis
Some developers from Maia Company have provided a public implementation of a PrintStream that provides one "STDOUT" per thread in this article : "Thread Specific System.out".
In their implementation they override only write methods, flush, close and checkError. It seems to be enough in their case.
They did not "need to @Override all of the methods called to get it to work per-thread" as @Gray stated in his answer.
NOTA:
Please find below the original code from Maia.
I found it here on the wayback machine. The original page was removed from the website of Maia. I reproduce it here for the reader's curiosity. I do not provide any support for this code.
Main.java
Creates a ThreadPrintStream, installs it as System.out, and creates and starts 10 threads.
public class Main {
public static void main(String[] args) {
// Call replaceSystemOut which replaces the
// normal System.out with a ThreadPrintStream.
ThreadPrintStream.replaceSystemOut();
// Create and start 10 different threads. Each thread
// will create its own PrintStream and install it into
// the ThreadPrintStream and then write three messages
// to System.out.
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new StreamText());
thread.start();
// Report to the console that a new thread was started.
System.out.println("Created and started " + thread.getName());
}
}
}
StreamText.java
A simple Runnable for each thread that opens a file for the thread’s output and installs it into the ThreadPrintStream.
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
/** A small test class that sets System.out for the currently executing
* thread to a text file and writes three messages to System.out. */
public class StreamText implements Runnable {
@Override
public void run() {
try {
// Create a text file where System.out.println()
// will send its data for this thread.
String name = Thread.currentThread().getName();
FileOutputStream fos = new FileOutputStream(name + ".txt");
// Create a PrintStream that will write to the new file.
PrintStream stream = new PrintStream(new BufferedOutputStream(fos));
// Install the PrintStream to be used as System.out for this thread.
((ThreadPrintStream)System.out).setThreadOut(stream);
// Output three messages to System.out.
System.out.println(name + ": first message");
System.out.println("This is the second message from " + name);
System.out.println(name + ": 3rd message");
// Close System.out for this thread which will
// flush and close this thread's text file.
System.out.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
ThreadPrintStream.java
Extends java.io.PrintStream. An object of ThreadPrintStream replaces the normal System.out and maintains a separate java.io.PrintStream for each thread.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/** A ThreadPrintStream replaces the normal System.out and ensures
* that output to System.out goes to a different PrintStream for
* each thread. It does this by using ThreadLocal to maintain a
* PrintStream for each thread. */
public class ThreadPrintStream extends PrintStream {
/** Changes System.out to a ThreadPrintStream which will
* send output to a separate file for each thread. */
public static void replaceSystemOut() {
// Save the existing System.out
PrintStream console = System.out;
// Create a ThreadPrintStream and install it as System.out
ThreadPrintStream threadOut = new ThreadPrintStream();
System.setOut(threadOut);
// Use the original System.out as the current thread's System.out
threadOut.setThreadOut(console);
}
/** Thread specific storage to hold a PrintStream for each thread */
private ThreadLocal<PrintStream> out;
private ThreadPrintStream() {
super(new ByteArrayOutputStream(0));
out = new ThreadLocal<PrintStream>();
}
/** Sets the PrintStream for the currently executing thread. */
public void setThreadOut(PrintStream out) {
this.out.set(out);
}
/** Returns the PrintStream for the currently executing thread. */
public PrintStream getThreadOut() {
return this.out.get();
}
@Override public boolean checkError() {
return getThreadOut().checkError();
}
@Override public void write(byte[] buf, int off, int len) {
getThreadOut().write(buf, off, len);
}
@Override public void write(int b) { getThreadOut().write(b); }
@Override public void flush() { getThreadOut().flush(); }
@Override public void close() { getThreadOut().close(); }
}
org.apache.geronimo.gshell.support.gshell-io.SystemOutputHijacker
Maven package and source code browser –
Aver You are right but not in the way you think. When a thread uses
System.out.println();
It takes a copy of the reference System.out
, but not a copy of the object this references.
This means all threads will normally see the same object for writing to output.
Note: This fields in not thread safe and if you call System.setOut(PrintStream)
If you use this there is a potential, undesirable race condition where different threads to have different local copies of System.out. This cannot be used to solve this question.
Is it possible to redirect System.out on a "per-thread" basis
You can do this by replacing System.out with your own implementation which is thread specific. i.e. a sub class of PrintStream. I have done this for logging where I wanted each thread's output to be consistent and not interleaved. e.g. Imagine printing two stack traces in two threads at the same time. ;)
Boy this is misleading
? –
Fawkes System.out
is static, and therefore the same instance is shared between all threads.
Is it possible to redirect System.out on a "per-thread" basis
You can redirect them all to your delegate which will be responsible for 'per-thread' logic.
Here is example of parallel JBehave tests having theirs own file output.
© 2022 - 2024 — McMap. All rights reserved.
PrintStream
object using AspectJ – Sasnett