Redirect Console Output to String in Java

Redirect console output to string in Java

If the function is printing to System.out, you can capture that output by using the System.setOut method to change System.out to go to a PrintStream provided by you. If you create a PrintStream connected to a ByteArrayOutputStream, then you can capture the output as a String.

Example:

// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());

This program prints just one line:

Here: Foofoofoo!

System.out to string

Even if not the best idea, one solution could be:

public static void main(String[] xxx) {
System.setOut(new DoublePrintStream(System.out, "/myfile.txt"));
System.setErr(new DoublePrintStream(System.err, "/errors.txt"));

System.out.println("this works");
try { throw new RuntimeException("oulala");} catch(Exception e) { e.printStackTrace(); }

//System.out.close(); // maybe required at the end of execution
}

class DoublePrintStream extends PrintStream {
private final OutputStream fos;

DoublePrintStream(OutputStream out, String filename){
super(out);

try {
fos = new FileOutputStream(new File(filename));
} catch (FileNotFoundException e) {
throw new AssertionError("cant create file", e);
}
}

@Override
public void write(byte[] buf, int off, int len) {
super.write(buf, off, len);

try {
fos.write(buf, off, len);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void close() {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
super.close();
}
}
}

so you have output in the console + in a file, and all errors in a separate file.

Even if logging frameworks are way better, this solution has the advantage to require no code change at all.

PS: in a multithreaded context, you should also synchronize the methods of DoublePrintStream

How to capture all system.out.println to an array or list of string

System.out.println method has print and newLine methods. There is no way to capture only System.out.println method, you should capture all System.out.print methods with changing System.out variable.

System.out.println jdk implementation:

    public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

Text collector output stream class for capturing System.out.print content:

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class TextCollector extends OutputStream {
private final List<String> lines = new ArrayList<>();
private StringBuilder buffer = new StringBuilder();

@Override
public void write(int b) throws IOException {
if (b == '\n') {
lines.add(buffer.toString());
buffer = new StringBuilder();
} else {
buffer.append((char) b);
}
}

public List<String> getLines() {
return lines;
}
}

Example test implementation:

import java.util.*;
import java.io.*;

public class Main {
public static void main(String[] args) {
// store current output
PrintStream tmpOut = System.out;

// change stream into text collector
TextCollector textCollector = new TextCollector();
System.setOut(new PrintStream(textCollector));

// collect lines
System.out.println("Test-1");
System.out.println("Test-2");

// print lines to console
System.setOut(tmpOut);
List<String> lines = textCollector.getLines();
for (String line : lines) {
System.out.println(line);
}
}
}

Redirect stdout to a string in Java

Yes - you can use a ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));

Then you can get the string with baos.toString().

To specify encoding (and not rely on the one defined by the platform), use the PrintStream(stream, autoFlush, encoding) constructor, and baos.toString(encoding)

If you want to revert back to the original stream, use:

System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));

Redirecting console output to GUI

You need to invoke startMeUp in a new thread, because your console program is blocking the event dispatch thread. Like this:

new Thread () {
@Override public void run () {
GreenhouseControls.startMeUp();
}
}.start();

instead of just

GreenhouseControls.startMeUp();

I want to save eclipse console log into a string

You can redirect console output to file in run configuration settings on the "Common" tab.

Sample Image

Java/Kotlin way to redirect command output to both stdout and String

Here is the ProcessBuilder solution, which I initially wanted to avoid. It does the job though it is bulky. Let me know if a better API is made available!

var logs:String = ""
runCatching {
var command:List<String> = listOf("command", "arg")
parameters.params?.let {command += it} // dynamic args
ProcessBuilder(command)
.directory(File(scriptRoot))
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectErrorStream(true) // Merges stderr into stdout
.start().also { process ->
withContext(Dispatchers.IO) { // More info on this context switching : https://elizarov.medium.com/blocking-threads-suspending-coroutines-d33e11bf4761
launch {
process.inputStream.bufferedReader().run {
while (true) { // Breaks when readLine returns null
readLine()?.let { line ->
logger.trace(line) // realtime logging
logs += "$line\n" // record
} ?: break
}
}
}

process.waitFor(60, TimeUnit.MINUTES)
if(process.isAlive) {
logs += "TIMEOUT occurred".also { logger.warn(it) } + "\n"
process.destroy()
}
}
}
}.onSuccess { process ->
if(process.exitValue() == 0) {
// completed with success
} else {
// completed with failure
}

}.onFailure { ex ->
logs = ex.stackTraceToString()
}

// Logs are available in $logs


Related Topics



Leave a reply



Submit