2009-11-10 34 views
5

Mantengo una aplicación que actúa como un contenedor para múltiples programas individuales. Estos programas tienen su propia función de registro dedicada, es decir, todo lo que registran hace a un archivo de registro especial.Eliminando el acceso a System.out en java

Sin embargo, los desarrolladores de aplicaciones parecen amor a tirar System.out.println y e.printStackTrace llamadas por todas partes, por lo que es imposible mantener una consola limpia cuando se ejecuta el contenedor.

¿Cómo puedo evitar que estas aplicaciones contaminen System.out y System.err?


notas de implementación:

  • las aplicaciones de uso Log4J para el registro;
  • el contenedor también usa la consola para el registro, pero está estrictamente reservado para eventos y problemas del ciclo de vida, por lo que aún necesito la consola;
  • las aplicaciones se cargan utilizando un cargador de clases personalizado, pero no se aplican controles de seguridad.

actualización:

simplemente redirigir System.out no funcionaría, ya que vuelve a dirigir toda la salida, así que algo como esto falla:

System.setOut(new PrintStream(new OutputStream() { 

     @Override 
     public void write(int b) { 

      throw new Error("Not on my watch you don't"); 

     } 
    })); 

    Logger logger = Logger.getLogger(Runner.class); 
    logger.info("My log message"); 

Esto debería suceder.

Actualización 2:

Las aplicaciones se cargan y se configuran mediante un código similar al

App app = new UrlClassLoader(...).loadClass(className)).newInstance(); 
app.setLogger(loggerForClass(app)); 

Log4J se carga desde el cargador de clases del sistema.

+0

Esto no es realmente una respuesta, pero: como desarrollador, suelo hacer esto también porque es más fácil verificar la consola en lugar de buscar en los archivos de registro. Así que cuando me registro, primero hago un buscar/reemplazar: buscar 'System.out' (reemplazar con' log.debug() '), buscar' System.err' (reemplazar con 'log.info()'), busque 'printStackTrace' (eliminar). Incluso creo que esto podría estar automatizado para svn por ejemplo (no estoy seguro de cómo, pero creo que se puede hacer) – laura

+0

Hm, en un lenguaje de sistemas el modismo sería 'fork()', redirigir el stdout y stderr del proceso secundario, y luego 'exec()', pero no estoy seguro de cuál es el equivalente con los cargadores de clase Java. ¿Podría entrar en más detalles sobre cómo está cargando las aplicaciones? – thirtyseven

+0

¿El contenedor está utilizando log4j o imprimiendo directamente a la consola? – Fedearne

Respuesta

2

La clave aquí es configurar log4j antes de redirigir las secuencias de salida, p.

BasicConfigurator.configure(); 
System.setOut(...); 
System.setErr(...); 

System.out.println("I fail"); 
Logger.getLogger(...).info("I work"); 
5

Use averssion therapy. Se programa una visita de "Los inspectores" cada vez que se verifica algún código que contenga construcciones desagradables.

Nice cubicle you got ere, be shame if anyfing appened to it. 
+0

Bueno ... no, gracias :-) Aunque valió la pena pensarlo. –

+0

Guau, esta sugerencia también resolvió mi problema completamente sin relación ... ¡gracias! ;-) –

+0

También tengo otro patrón útil: "Recompensa", se trata de vino tinto, sombreros de fiesta y una visita de "The Choir". "¡Aleluya!" – djna

6

Mientras que Java define un System.out y System.err estándar, estos se pueden sobrescribir con sus propias transmisiones. Ver http://www.devx.com/tips/Tip/5616

Básicamente puede configurar nuevas transmisiones que se conectan al registro, o simplemente dejan que los datos se desvanezcan en la nada. Mi preferencia sería la última, ya que al instante curaría a los desarrolladores de confiar en System.out y se equivocaría, ya que todo lo que escriben desaparece.

** Actualización: Acabo de volver a leer sus estipulaciones en la pregunta y veo que todavía necesita la consola para la aplicación del contenedor.Esto aún podría funcionar si escribe un contenedor alrededor de la transmisión estándar para que pueda verificar cada llamada y ver si proviene de la aplicación principal (y pasarla) o una aplicación secundaria (y bloquearla)

10

Puede utilice System.setOut() y System.setErr() para redirigir stdout y stderr a instancias de PrintStream.

+0

Gracias. Por favor mira mi actualización –

12

Suponiendo que usted puede controlar su salida de contenedores que puede hacer lo siguiente:

import java.io.*; 
public class SysOut { 
    public static void main(String[] args) throws Exception { 
      PrintStream pw = new PrintStream(new FileOutputStream("a.txt")); 
      PrintStream realout = System.out; 
      System.setOut(pw); 
      System.out.println("junk"); 
      realout.print("useful"); 
} 
} 

$ java SysOut 
useful 
$ cat a.txt 
junk 
+0

Gracias. Por favor mira mi actualización –

1

convertir el System.out y System.err corrientes a las implementaciones especiales que arrojan una RuntimeException ("utilizar el registro en lugar del sistema. out ") cada vez que se escribe un personaje.

Si el contenedor es importante, ellos tienen la idea bastante rápido :)

(Para extra de bonificación OutOfMemoryException tiro en vez ;-))

+0

Gracias. Por favor mira mi actualización –

+0

En ese caso, brinde un ejemplo de cómo distinguir entre el producto que le gusta y el resultado que no le gusta. ¿Se permiten algunas clases para hacer esto y otras no? –

+0

Si tiene el origen para el contenedor, ¿por qué no refactorizarlo para guardar y usar las transmisiones originales, ENTONCES puede reemplazar System.out y System.err? –

1

Lo que he hecho es redirigir la PrintStream de System.out y System.err a commons-logging como INFO y registro de nivel de ERROR, respectivamente.

Esto se vuelve más complicado si quiere que algunos subprocesos puedan escribir en la consola o si desea que los registros también vayan a la consola, pero se puede hacer.

+0

Sí, estoy en el área 'más difícil'. –

+0

Puede hacer que el código vuelva a ingresar. es decir establecer un indicador cuando se llama a OutputStream.write. si la bandera no está configurada, imprima en los registradores. Si está configurado, imprima de forma normal ya que debe provenir del registrador. –

+0

Nota: debe desactivar el indicador en un bloque finally. ;) –

0

Utilizamos el truco log4j pero registramos en archivos separados (stdout.log, stderr.log). No es útil mezclar su salida con las partes que realmente comprenden el registro ...

1

En realidad, puede obtener y almacenar System.out/err antes de reemplazarlos.

OutputStream out=System.getOut(); // I think the names are right 
System.setOut(some predefined output stream, null won't work); 
out.println("Hey, this still goes to the output"); 
System.out.println("Oh noes, this does not"); 

He utilizado este para interceptar todas las System.out.println está en el código base y el prefijo cada línea de salida con el número nombre del método/línea de vino.

1

Cierre las secuencias System.out y System.err.

+0

Gracias. Por favor mira mi actualización –

+0

Se puede argumentar que [como la JVM creó esas transmisiones, solo debería cerrarlas] (http://stackoverflow.com/a/7457737/545127). – Raedwald

3

Si tiene un mecanismo de creación sin cabeza, hormiga o similar, entonces podría agregar CheckStyle a la construcción y configurar el estilo de comprobación para fallar la compilación si encuentra System.out.println o e.printStackTrace en el código.

Si no tiene una estructura sin cabeza, le recomendaría que construya una, ya que significa que tiene construcciones repetibles y predecibles.

3

System.setOut redirigirá toda la salida, pero el PrintStream que usted suministra puede decidir cómo se maneja la salida. Por lo tanto, estoy seguro de que podría proporcionar un flujo que solo imprimiera realmente declaraciones desde su aplicación.

La única parte difícil es realmente poder detectar qué es una llamada válida y qué no. Una forma de hacerlo, pero probablemente muy lenta, sería llamar al Thread.currentThread().getStackTrace() y ver qué código (o paquete, al menos) le está llamando (simplemente regresando si no es válido). Sin embargo, no lo recomendaría ya que el rendimiento sería asombroso, especialmente al hacer esto en cada byte leído.

Una mejor idea podría ser establecer un indicador de ThreadLocal en todos sus hilos de contenedor válidos.A continuación, se puede implementar el PrintStream algo como lo siguiente:

public class ThreadValidity extends ThreadLocal<Boolean> 
{ 
    private static final INSTANCE = new ThreadValidity(); 

    @Override Boolean initialValue() { return false; } 
    public static ThreadValidity getInstance() { return INSTANCE; } 
} 

class VerifyingPrintStream extends PrintStream 
{ 
    private boolean isValidThread() 
    { 
     return ThreadValidity.instance().get(); 
    } 

    public void println(String s) 
    { 
     if (!isValidThread()) return; 
     super.println(s); 
    } 

    public void println(Object o) 
    { 
     if (!isValidThread()) return; 
     super.println(o); 
    } 

    // etc 
} 

Alternativamente, si usted es capaz de cambiar las println s en el código del contenedor, las cosas se ponen más fácil. Puede transferir todas las escrituras de la consola a un trabajador específico; y haga que este trabajador "robe" System.out (guárdelo en su propio campo y úselo directamente para escribir el resultado) mientras configura el System.out real en una grabadora no operativa.