Pregunta

Estoy usando una biblioteca díscolo que, por desgracia, imprime información a System.out (u ocasionalmente System.err). ¿Cuál es la forma más sencilla de evitar esto?

He estado pensando acerca de cómo crear un flujo de salida a la memoria, reemplace System.out y err antes de cada llamada a uno de los métodos troublemaking, restaurarlos posteriormente, y simplemente ignorar el buffer del flujo creado. ¿Hay una manera más fácil, más elegante?

EDIT: no quiero redireccionar toda la salida - que se logra fácilmente. Sólo quiero hacer caso omiso de salida potencialmente generados por ciertas llamadas a las bibliotecas.

¿Fue útil?

Solución

terminé haciendo algo como:

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

Gracias a AlexR y apilador para mí volver a dirigir a este breve solución.

Otros consejos

Una forma de deshacerse de esas impresiones no deseadas permanente sería el uso de manipulación de código de bytes para eliminar las declaraciones de impresión desde la biblioteca problemático. Esto se puede hacer por ejemplo utilizando ASM (o uno de los otros nivel más alto y más fácil de usar AOP marcos).

Se puede hacer esto ya sea en tiempo de ejecución o como una operación de una sola vez de volver a escribir los archivos de clase de la biblioteca. Consulte la documentación del ASM para averiguar cómo. Aquí está una prueba de concepto. Lo que hace es que reemplaza todas las referencias a System.out con una referencia a un PrintStream que no hace nada.

En primer lugar las pruebas. Utilizan algunas clases de utilidad de mi proyecto para ayuda con la prueba transformaciones de código de bytes (para ello, requiere la creación de un cargador de clases personalizadas y aplicando las transformaciones de código de bytes a la clase correcta, pero no cualesquiera otras clases).

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

Y entonces la aplicación. Tenga en cuenta que no debe utilizar este código sin entender por primera vez. Hacer programa rel="nofollow"> no .

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

Hay 2 maneras. 1. La forma más sencilla es ejecutar su programa y redireccionar toda la salida a / dev / null (si está en UNIX) o al archivo especial que será retirado (para Windows) De esta manera es sencillo pero significa que toda su producción va a la nada. Si el programa utiliza la salida estándar para las cosas buenas no se puede utilizar de esta manera.

  1. Se puede configurar la salida estándar utilizando la API de Java.

    System.setOut (out) System.setErr (out)

Ahora usted puede implementar su propio 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);
    }

}

Esto es mejor porque se puede filtrar la salida irrelevantes e imprimir una buena información.

scroll top