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 p
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 out;
private ThreadPrintStream() {
super(new ByteArrayOutputStream(0));
out = new ThreadLocal();
}
/** 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(); }
}