«Отключить» выходной поток - PullRequest
12 голосов
/ 26 ноября 2010

Я использую своенравную библиотеку, которая, к сожалению, печатает информацию в System.out (или иногда System.err). Какой самый простой способ предотвратить это?

Я думал о создании потока вывода в память, заменяя System.out и err перед каждым вызовом одного из проблемных методов, восстанавливая их позже, и просто игнорируя буфер созданного потока. Есть ли более простой и элегантный способ?

РЕДАКТИРОВАТЬ: Я не хочу перенаправлять весь вывод - это легко сделать. Я только хочу игнорировать вывод, потенциально генерируемый определенными вызовами библиотеки.

Ответы [ 4 ]

16 голосов
/ 28 ноября 2010

В итоге я сделал что-то вроде:

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 и укладчику за то, что перенаправили меня к этому короткому решению.

4 голосов
/ 26 ноября 2010

Способ избавиться от этих нежелательных отпечатков навсегда будет использовать манипулирование байт-кодом , чтобы удалить операторы печати из проблемной библиотеки.Это может быть сделано, например, с использованием ASM (или другого более высокого уровня, более простого в использовании AOP frameworks).

Вы можете сделать это либо во время выполнения, либокак одноразовая операция переписывания файлов классов библиотеки.Обратитесь к документации ASM, чтобы узнать как.Вот подтверждение концепции.Он заменяет все ссылки на System.out ссылкой на PrintStream, который ничего не делает.

Сначала тесты.Они используют некоторые служебные классы из моего проекта , чтобы помочь с тестированием преобразований байт-кода (тестирование требует создания собственного загрузчика классов и применения преобразований байт-кода к нужному классу, но не к любым другим классам).

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

А потом реализация.Обратите внимание, что вы не должны использовать этот код без предварительного понимания.Не программируйте по совпадению .

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);
            }
        }
    }
}
1 голос
/ 26 ноября 2010

Есть 2 способа. 1. Самый простой способ - запустить вашу программу и перенаправить весь вывод в / dev / null (если вы используете unix) или в специальный файл, который будет удален (для windows) Этот способ прост, но это означает, что все ваши результаты сводятся к нулю. Если ваша программа использует STDOUT для хороших вещей, вы не можете использовать этот способ.

  1. Вы можете установить STDOUT, используя Java API.

    System.setOut (уходит) System.setErr (уходит)

Теперь вы можете реализовать свой собственный 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);
    }

}

Это лучше, потому что вы можете отфильтровать нерелевантный вывод и распечатать полезную информацию.

1 голос
/ 26 ноября 2010
File file  = new File(filename);
PrintStream printStream = new PrintStream(new FileOutputStream(file));
System.setOut(printStream);
...