代码之家  ›  专栏  ›  技术社区  ›  Oak

“关闭”输出流

  •  12
  • Oak  · 技术社区  · 15 年前

    我在用一个任性的图书馆,不幸的是,它把信息打印到 System.out (或偶尔 System.err ). 最简单的预防方法是什么?

    我一直在考虑创建一个输出流到内存,替换 系统输出 err 在每次调用某个麻烦方法之前,请稍后还原它们,然后忽略所创建流的缓冲区。有没有更简单、更优雅的方法?

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

    4 回复  |  直到 15 年前
        1
  •  16
  •   Oak    15 年前

    我最后做了如下事情:

    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);
    }
    

    感谢Alexer和stacker把我引向这个简短的解决方案。

        2
  •  4
  •   Esko Luontola    15 年前

    一种去除那些不需要的指纹的方法 永久地 会被用来 字节码操作 从麻烦的库中删除打印语句。例如,可以使用 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);
                }
            }
        }
    }
    
        3
  •  1
  •   stacker    15 年前
    File file  = new File(filename);
    PrintStream printStream = new PrintStream(new FileOutputStream(file));
    System.setOut(printStream);
    
        4
  •  1
  •   AlexR    15 年前

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

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

      系统放样 系统设置错误(输出)

    现在您可以实现自己的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);
        }
    
    }
    

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