2010-07-12 42 views
30

Mi aplicación tiene muchas sentencias System.out.println().Redirigir System.out.println

Quiero capturar mensajes de println y enviarlos al registrador estándar (Log4j, JUL, etc.).

¿Cómo hacer eso?

+0

posible duplicado de [Extracción de acceso a System.out en Java] (http://stackoverflow.com/questions/1710914/removing-access-to-system-out-in-java) –

+0

Si bien hay cruces en las respuestas para ese posible engaño, no creo que esté lo suficientemente cerca como para merecer una coincidencia. Se trata más bien de cómo bloquear la producción por completo: esta es para capturarla en otro lugar. – paxdiablo

Respuesta

33

La clase System tiene una setOut y setErr que se puede utilizar para cambiar el flujo de salida de, por ejemplo, un nuevo PrintStream con un respaldo de File o, en este caso, probablemente, otra corriente que utiliza el subsistema de registro de elección.


Tenga en cuenta que también puede conseguirse en problemas si alguna vez configurar la biblioteca de la tala de salida a la salida estándar o el error (del tipo de recursión infinita, posiblemente).

Si ese es el caso, es posible que desee ir y reemplazar sus System.out.print declaraciones de tipo con llamadas de registro reales.

+0

Para completar su solución, le sugiero que agregue instrucciones sobre cómo volver a la salida estándar normal una vez que haya interceptado todo lo requerido, es decir, System.setOut (nuevo PrintStream (nuevo FileOutputStream (FileDescriptor.out))); –

+0

@VicSeedoubleyew no sería mejor almacenar el 'PrintStream' original y restaurarlo después; si algo más también está redirigiendo' System.out', esto no necesariamente lo restaurará correctamente. – neuralmer

+0

@neuralmer seguro, eso funciona igual –

8

La mejor solución es pasar y cambiar todas las instrucciones println para usar una biblioteca de registro adecuada. Lo que estás tratando de hacer es un gran truco.

+0

Tal vez esté tratando de redirigir el sistema.a un Logger ;-) – Riduidel

+1

+1: el * punto * completo de un marco de trabajo de registro es poder abstraer dónde está grabando el resultado, debe llamar a sus métodos de registro como su salida "definitiva" del código. Sin mencionar, redirigir 'System.out' para llamar al registrador * aparece * para que funcione; hasta el punto en que un día configuras el registrador para enviarlo a stdout, y ** bam ** - desbordamiento de pila. –

+0

Y esto no es * tan * difícil. +1 – Nivas

28

Tuve una necesidad similar una vez. Necesitaba interceptar el resultado de un componente de terceros y reaccionar ante un mensaje de error. El concepto es el siguiente:

private class Interceptor extends PrintStream 
{ 
    public Interceptor(OutputStream out) 
    { 
     super(out, true); 
    } 
    @Override 
    public void print(String s) 
    {//do what ever you like 
     super.print(s); 
    } 
} 
public static void main(String[] args) 
{ 
    PrintStream origOut = System.out; 
    PrintStream interceptor = new Interceptor(origOut); 
    System.setOut(interceptor);// just add the interceptor 
} 
+2

Buena idea. Gracias – Jacky

-1

que aplica la idea base de este y workes bien. No es necesario cambiar todas las cosas de System.out. ### y System.err. ###.

import java.io.OutputStream; 
import java.io.PrintStream; 

import de.msg.pm.designer.services.simulation.junit.base.Defines; 

public class Interceptor extends PrintStream 
{ 
    /** the logger */ 
    private Logger log; 
    /** the origin output stream */ 
    PrintStream orig; 

    /** 
    * Initializes a new instance of the class Interceptor. 
    * 
    * @param out the output stream to be assigned 
    * @param log the logger 
    */ 
    public Interceptor(OutputStream out, Logger log) 
    { 
    super(out, true); 
    this.log = log; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    protected void finalize() throws Throwable 
    { 
    detachOut(); 
    super.finalize(); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public void print(String s) 
    { 
    //do what ever you like 
    orig.print(s); 
    log.logO(s, true); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public void println(String s) 
    { 
    print(s + Defines.LF_GEN); 
    } 

    /** 
    * Attaches System.out to interceptor. 
    */ 
    public void attachOut() 
    { 
    orig = System.out; 
    System.setOut(this); 
    } 

    /** 
    * Attaches System.err to interceptor. 
    */ 
    public void attachErr() 
    { 
    orig = System.err; 
    System.setErr(this); 
    } 

    /** 
    * Detaches System.out. 
    */ 
    public void detachOut() 
    { 
    if(null != orig) 
    { 
     System.setOut(orig); 
    } 
    } 

    /** 
    * Detaches System.err. 
    */ 
    public void detachErr() 
    { 
    if(null != orig) 
    { 
     System.setErr(orig); 
    } 
    } 
} 


public class InterceptionManager 
{ 
    /** out */ 
    private Interceptor out; 

    /** err */ 
    private Interceptor err; 

    /** log */ 
    private Logger log; 

    /** 
    * Initializes a new instance of the class InterceptionManager. 
    * 
    * @param logFileName the log file name 
    * @param append the append flag 
    */ 
    public InterceptionManager(String logFileName, boolean append) 
    { 
    log = new Logger(); 
    log.setLogFile(logFileName, append); 
    this.out = new Interceptor(System.out, log); 
    this.out.attachOut(); 
    this.err = new Interceptor(System.err, log); 
    this.err.attachErr(); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    protected void finalize() throws Throwable 
    { 
    if(null != log) 
    { 
     log.closeLogFile(); 
    } 
    super.finalize(); 
    } 
} 

Este pocas líneas habilitar el registro sin más cambios de código:

if(writeLog) 
    { 
    this.logFileName = this.getClassName() + "_Log.txt"; 
    this.icMan = new InterceptionManager(this.logFileName, false); 
    System.out.format("Logging to '%s'\n", this.logFileName); 
    } 
2

Aquí es cómo capturar las impresiones en System.out, y luego poner las cosas en orden:

// Start capturing 
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
System.setOut(new PrintStream(buffer)); 

// Run what is supposed to output something 
... 

// Stop capturing 
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); 

// Use captured content 
String content = buffer.toString(); 
buffer.reset(); 
1

extendiendo PrintStream es una mala solución, ya que tendrá que anular todos los métodos print() y println(). En su lugar, puede capturar la corriente:

public class ConsoleInterceptor { 

    public interface Block { 
     void call() throws Exception; 
    } 

    public static String copyOut(Block block) throws Exception { 
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     PrintStream printStream = new PrintStream(bos, true); 
     PrintStream oldStream = System.out; 
     System.setOut(printStream); 
     try { 
      block.call(); 
     } 
     finally { 
      System.setOut(oldStream); 
     } 
     return bos.toString(); 
    } 
} 

Ahora puede capturar de esta manera:

String result = ConsoleInterceptor.copyOut(() ->{ 
     System.out.print("hello world"); 
     System.out.print('!'); 
     System.out.println(); 
     System.out.println("foobar"); 
    }); 
    assertEquals("hello world!\nfoobar\n", result); 
+0

Buena * solución * reutilizable. También creo que la interfaz del bloque podría reemplazarse con Runnable. –

Cuestiones relacionadas