Domanda

Io sto usando una libreria ribelle che, purtroppo, stampa le informazioni per System.out (o occasionalmente System.err). Qual è il modo più semplice per evitare questo?

Ho pensato di creare un flusso di output per la memoria, sostituire System.out e err prima di ogni chiamata a uno dei metodi troublemaking, ripristinarli in seguito, e semplicemente ignorare il buffer dello stream creato. C'è un modo più semplice più elegante?

EDIT: Non voglio per reindirizzare tutto l'output - che è facilmente realizzabile. Voglio solo ignorare uscita potenzialmente generato da alcune chiamate di libreria.

È stato utile?

Soluzione

ho finito per fare qualcosa di simile:

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

Grazie a AlexR e stacker per me reindirizzamento a questo breve soluzione.

Altri suggerimenti

Un modo per sbarazzarsi di quelle stampe indesiderate in modo permanente sarebbe quella di utilizzare manipolazione bytecode per rimuovere le istruzioni di stampa dalla libreria fastidioso. Ciò può avvenire ad esempio utilizzando ASM (o uno degli altri livello superiore e più facile da usare AOP quadri).

È possibile eseguire questa operazione in fase di esecuzione o come operazione una tantum di riscrivere file di classe della biblioteca. Fare riferimento alla documentazione di ASM per scoprire come. Ecco una prova di concetto. Ciò che fa è che sostituisce tutti i riferimenti a System.out con un riferimento a un PrintStream, che non fa nulla.

Per prima dei test. Usano alcune classi di utilità da mio progetto per aiutare con il test trasformazioni bytecode (testarlo richiede la creazione di una classe personalizzata loader e applicando le trasformazioni bytecode alla classe giusta, ma non altre classi).

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

E poi l'attuazione. Si prega di notare che non si dovrebbe usare questo codice senza prima comprenderlo. Fare programma rel="nofollow"> non .

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);
            }
        }
    }
}
File file  = new File(filename);
PrintStream printStream = new PrintStream(new FileOutputStream(file));
System.setOut(printStream);

Ci sono 2 modi. 1. il modo più semplice è quello di eseguire il programma e reindirizzare tutto l'output a / dev / null (se siete su Unix) o file speciale che verrà rimosso (per Windows) In questo modo è semplice ma significa che tutto l'output sta per niente. Se il programma utilizza STDOUT per le cose buone non è possibile utilizzare in questo modo.

  1. E 'possibile impostare STDOUT utilizzando Java API.

    System.setOut (esce) System.setErr (esce)

Ora è possibile implementare il proprio 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);
    }

}

Questo è meglio, perché è possibile filtrare uscita irrilevante e stampare una buona informazione.

scroll top