How to check if file or console is associated with Standard Output?

Tags:



I am using following code to redirect standard out and standard error out to Log file depending on the boolean value of a variable.

if (logToFile==true){
java.io.File outputFile = new java.io.File(logFilePath);

System.setOut(new java.io.PrintStream(new java.io.FileOutputStream(outputFile, true), true));
System.setErr(new java.io.PrintStream(new java.io.FileOutputStream(outputFile, true), true));

}

Moving further down my code, I want to find out whether my standard out and error out are associated with file – only then I would want to log few things. And over there, I don’t have access to logToFile variable.

Is there any way to find out whether the standard out and error out are associated with file or the default console currently? And if they are associated to file then can we get the file path?

Answer

Moving further down my code, I want to find out whether my standard out and error out are associated with file – only then I would want to log few things. And over there, I don’t have access to logToFile variable.

What about storing the value of logToFile in a static variable, like for example:

if (logToFile) {
    StandardStreams.redirectToFile(new File(logFilePath));
}
public class StandardStreams {
    private static boolean redirectedToFile;

    public static void redirectToFile(File file) throws FileNotFoundException {
        PrintStream stream = new PrintStream(new FileOutputStream(file, true), true);
        System.setOut(stream);
        System.setErr(stream);
        redirectedToFile = true;
    }

    public static boolean areRedirectedToFile() {
        return redirectedToFile;
    }
}

And then:

if (StandardStreams.areRedirectedToFile()) {
    // Log few things
}

Is there any way to find out whether the standard out and error out are associated with file or the default console currently? And if they are associated to file then can we get the file path?

Create your own PrintStream:

class ConsoleLinkedFile extends PrintStream {
    private final File file;

    ConsoleLinkedFile(File file) throws FileNotFoundException {
        super(new FileOutputStream(file, true), true);
        this.file = file;
    }

    File getFile() {
        return file;
    }
}
if (logToFile) {
    PrintStream stream = new ConsoleLinkedFile(new File(logFilePath));
    System.setOut(stream);
    System.setErr(stream);
}

To find out and retrieve the file path:

public static Optional<File> getFileIfRedirected(PrintStream stream) {
    if (stream instanceof ConsoleLinkedFile) {
        ConsoleLinkedFile linkedFile = (ConsoleLinkedFile) stream;
        return Optional.of(linkedFile.getFile());
    }
    return Optional.empty();
}
if (getFileIfRedirected(System.out).isPresent()) {
    // Log few things
}

Note that the same PrintStream can be shared between standard input and standard error.


If you cannot create your own PrintStream, then you need to use reflection:

private static final VarHandle OUT, PATH;

static {
    final Class<?> OUT_class = FilterOutputStream.class;
    final Class<?> PATH_class = FileOutputStream.class;
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    try {
        OUT = MethodHandles.privateLookupIn(OUT_class, lookup)
                .findVarHandle(OUT_class, "out", OutputStream.class);
        PATH = MethodHandles.privateLookupIn(PATH_class, lookup)
                .findVarHandle(PATH_class, "path", String.class);
    } catch (ReflectiveOperationException e) {
        throw new ExceptionInInitializerError(e);
    }
}

private static Optional<String> getFileIfRedirected(PrintStream stream) {
    Object out = OUT.get(stream);
    if (out instanceof BufferedOutputStream) {
        out = OUT.get(out);
    }
    return Optional.ofNullable((String) PATH.get(out));
}

VarHandle is faster than java.lang.reflect. In Java 8, you can use the latter:

private static final Field OUT, PATH;

static {
    try {
        OUT = FilterOutputStream.class.getDeclaredField("out");
        OUT.setAccessible(true);
        PATH = FileOutputStream.class.getDeclaredField("path");
        PATH.setAccessible(true);
    } catch (NoSuchFieldException e) {
        throw new ExceptionInInitializerError(e);
    }
}


Source: stackoverflow