Подтвердить что ты не робот

"Отключить" выходной поток

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

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

EDIT: я не хочу перенаправлять весь вывод - это легко выполняется. Я просто хочу игнорировать вывод, потенциально генерируемый некоторыми вызовами библиотеки.

4b9b3361

Ответ 1

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

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

Ответ 2

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

Вы можете сделать это либо во время выполнения, либо в виде однократной операции перезаписи файлов классов библиотеки. Обратитесь к документации 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);
            }
        }
    }
}

Ответ 3

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

Ответ 4

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

  • Вы можете установить 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);
    }

}

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