“关闭”输出流

时间:2010-11-25 21:48:28

标签: java printstream output-redirect

我正在使用一个任性的图书馆,遗憾的是,它会将信息打印到System.out(或偶尔System.err)。什么是防止这种情况最简单的方法?

我一直在考虑创建一个到内存的输出流,在每次调用一个故障排除方法之前替换System.outerr,稍后恢复它们,然后忽略所创建的缓冲区流。是否有更简单,更优雅的方式?

编辑:我不想重定向所有输出 - 这很容易实现。我只想忽略某些库调用可能产生的输出。

4 个答案:

答案 0 :(得分:16)

我最终做了类似的事情:

PrintStream out = System.out;
System.setOut(new PrintStream(new OutputStream() {
    @Override public void write(int b) throws IOException {}
}));
try {
    <library call>
} finally {
    System.setOut(out);
}

感谢AlexR和堆叠器将我重定向到这个简短的解决方案。

答案 1 :(得分:4)

一种摆脱那些不需要的打印永久的方法是使用字节码操作从麻烦的库中删除打印语句。这可以通过使用ASM(或其他更高级别和更易于使用的AOP框架之一)来完成。

您可以在运行时或作为重写库类文件的一次性操作执行此操作。请参阅ASM的文档以了解具体方法。这是一个概念证明。它的作用是将System.out的所有引用替换为对PrintStream没有任何作用的引用。

首先是测试。他们使用my project中的一些实用程序类来帮助测试字节码转换(测试它需要创建一个自定义类加载器并将字节码转换应用到正确的类而不是任何其他类)。

package net.orfjackal.dimdwarf.aop;

import net.orfjackal.dimdwarf.aop.conf.*;
import org.junit.*;
import org.objectweb.asm.*;
import org.objectweb.asm.util.CheckClassAdapter;

import java.io.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

public class RemoveCallsToSystemOutTest {

    private PrintStream originalOut;
    private ByteArrayOutputStream collectedOut;

    @Before
    public void collectSystemOut() {
        originalOut = System.out;
        collectedOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(collectedOut));
    }

    @After
    public void restoreOriginalSystemOut() {
        System.setOut(originalOut);
    }

    @Test
    public void the_target_class_prints_when_not_manipulated() throws Exception {
        String safetyCheck = callPrintSomething(TroublesomePrinter.class);

        assertThat(safetyCheck, is("it did execute"));
        assertThat(collectedOut.size(), is(greaterThan(0)));
    }

    @Test
    public void the_target_class_does_not_print_when_it_has_been_manipulated() throws Exception {
        String safetyCheck = callPrintSomething(instrumentClass(TroublesomePrinter.class));

        assertThat(safetyCheck, is("it did execute"));
        assertThat(collectedOut.size(), is(0));
    }

    private static String callPrintSomething(Class<?> clazz) throws Exception {
        Method m = clazz.getMethod("printSomething");
        m.setAccessible(true);
        return (String) m.invoke(null);
    }

    private static Class<?> instrumentClass(Class<?> cls) throws ClassNotFoundException {
        ClassFileTransformer transformer = new AbstractTransformationChain() {
            protected ClassVisitor getAdapters(ClassVisitor cv) {
                cv = new CheckClassAdapter(cv);
                cv = new RemoveCallsToSystemOut(cv);
                return cv;
            }
        };
        ClassLoader loader = new TransformationTestClassLoader(cls.getPackage().getName() + ".*", transformer);
        return loader.loadClass(cls.getName());
    }
}

class TroublesomePrinter {
    public static String printSomething() {
        System.out.println("something");
        return "it did execute";
    }
}

然后执行。请注意,如果没有先了解此代码,请不要使用此代码。不要program by coincidence

class SilentSystem {
    public static final PrintStream out = new PrintStream(new OutputStream() {
        public void write(int b) {
        }
    });
}

class RemoveCallsToSystemOut extends ClassAdapter {

    public RemoveCallsToSystemOut(ClassVisitor cv) {
        super(cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return new MyMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions));
    }

    private static class MyMethodAdapter extends MethodAdapter {
        public MyMethodAdapter(MethodVisitor mv) {
            super(mv);
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            if (opcode == Opcodes.GETSTATIC
                    && owner.equals("java/lang/System")
                    && name.equals("out")
                    && desc.equals("Ljava/io/PrintStream;")) {
                super.visitFieldInsn(opcode, "net/orfjackal/dimdwarf/aop/SilentSystem", name, desc);
            } else {
                super.visitFieldInsn(opcode, owner, name, desc);
            }
        }
    }
}

答案 2 :(得分:1)

File file  = new File(filename);
PrintStream printStream = new PrintStream(new FileOutputStream(file));
System.setOut(printStream);

答案 3 :(得分:1)

有两种方法。 1.最简单的方法是运行程序并将所有输出重定向到/ dev / null(如果你在unix上)或者将被删除的特殊文件(对于windows) 这种方式很简单,但这意味着你的所有输出都没有。如果你的程序使用STDOUT来做好事,你就不能用这种方式。

  1. 您可以使用java API设置STDOUT。

    System.setOut(下) System.setErr(下)

  2. 现在您可以实现自己的OutputStream:

    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.regex.Pattern;
    
    
    public class FilterOutputStream extends OutputStream {
        private OutputStream out;
        private Pattern filterOut;
        public FilterOutputStream(OutputStream out, Pattern filterOut) {
            this.out = out;
            this.filterOut = filterOut;
        }
    
    
        @Override
        public void write(int b) throws IOException {
            String callerClassName = new Throwable().getStackTrace()[1].getClassName();
            if (filterOut.matcher(callerClassName).find()) {
                return;
            }
            out.write(b);
        }
    
    }
    

    这样做更好,因为您可以过滤掉不相关的输出并打印好的信息。