¿Por qué no se muestran los mensajes de registro de Level.FINE?

110

Los JavaDocs para eljava.util.logging.Level estado:


Los niveles en orden descendente son:

  • SEVERE (valor más alto)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (valor más bajo)

Fuente

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(Level.FINER);
        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}

Salida

Logging level is: FINER
Jun 11, 2011 9:39:23 PM LoggingLevelsBlunder main
INFO: 0 0
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 1 1
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 2 4
Press any key to continue . . .

Planteamiento del problema

Mi ejemplo establece el Levelto FINER, por lo que esperaba ver 2 mensajes para cada bucle. En cambio, veo un solo mensaje para cada bucle ( Level.FINEfaltan los mensajes).

Pregunta

¿Qué necesita cambiar para ver la salida FINE(, FINERo FINEST)?

Actualizar (solución)

Gracias a la respuesta de Vineet Reynolds , esta versión funciona según mis expectativas. Muestra 3 x INFOmensajes y 3 x FINEmensajes.

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        // LOG this level to the log
        logger.setLevel(Level.FINER);

        ConsoleHandler handler = new ConsoleHandler();
        // PUBLISH this level
        handler.setLevel(Level.FINER);
        logger.addHandler(handler);

        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}
Andrew Thompson
fuente
10
Me parece que tendrá los mensajes impresos dos veces en la consola para INFO y superior: primero por el registrador anónimo, luego por su padre, el registrador global que también tiene un ConsoleHandler configurado en INFO por defecto. Para deshabilitar el registrador global, debe agregar esta línea de código: logger.setUseParentHandlers (false);
min
Solo quiero confirmar el comentario de unos minutos sobre los dobles. obtendrá dos salidas a menos que use .setUseParentHandlers (false);
xpagesbeast

Respuestas:

124

Los registradores solo registran el mensaje, es decir, crean los registros de registro (o solicitudes de registro). No publican los mensajes a los destinos, de lo que se encargan los Handlers. Establecer el nivel de un registrador solo hace que cree registros que coincidan con ese nivel o más.

Es posible que esté usando un ConsoleHandler(no podría inferir dónde está su salida System.err o un archivo, pero supongo que es el primero), que por defecto publica registros de registro del nivel Level.INFO. Tendrá que configurar este controlador, para publicar registros de registro de nivel Level.FINERy superior, para el resultado deseado.

Recomendaría leer la guía de descripción general del registro de Java para comprender el diseño subyacente. La guía cubre la diferencia entre el concepto de Logger y Handler.

Editar el nivel de controlador

1. Usando el archivo de configuración

El archivo de propiedades java.util.logging (de forma predeterminada, este es el logging.propertiesarchivo en JRE_HOME/lib) se puede modificar para cambiar el nivel predeterminado de ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Creación de controladores en tiempo de ejecución

Esto no se recomienda, ya que podría anular la configuración global. El uso de esto en toda su base de código resultará en una configuración de registrador posiblemente inmanejable.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
fuente
2
Gracias. Eso hizo el truco. Ahora me doy cuenta de por qué estaba inicialmente tan confundido. Anteriormente había trabajado con niveles de registro, pero mi implementación en ese momento simplemente colocó cada mensaje registrado en una lista que se mostraba sin tener en cuenta el Handler.
Andrew Thompson
3
De nada. Y sí, el diseño le afecta, si uno ha estado escribiendo registradores que simplemente volcaron cadenas en un archivo, consola, etc.
Vineet Reynolds
7
¿Y cambiar las logging.properties en la biblioteca JRE global es manejable?
James Anderson
2
Tenga en cuenta que el nivel del registrador en sí debe ser menos restrictivo que el nivel del controlador. Antes de iniciar sesión, Java comprueba el nivel máximo entre el registrador y el controlador. Entonces, si establece solo handler.setLevel (Level.FINER); no verá nada porque el nivel de registrador predeterminado es Level.INFO. Debe configurarlo en MÁS FINO, MÁS FINO o TODOS. Diga logger.setLevel (Level.ALL);
Jeff_Alieffson
Supongo que esto resolverá el problema en una sola línea, incluso cuando use registradores anónimos y con nombre en un entorno predeterminado:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

El porque

java.util.logging tiene un registrador raíz que tiene como valor predeterminado Level.INFOy un ConsoleHandler adjunto que también tiene como valor predeterminado Level.INFO. FINEes menor que INFO, por lo que los mensajes finos no se muestran de forma predeterminada.


Solución 1

Cree un registrador para toda su aplicación, por ejemplo, a partir del nombre o uso de su paquete Logger.getGlobal(), y conecte su propio ConsoleLogger a él. Luego, pídale al registrador raíz que se cierre (para evitar la salida duplicada de mensajes de nivel superior), o pídale a su registrador que no reenvíe registros a la raíz.

public static final Logger applog = Logger.getGlobal();
...

// Create and set handler
Handler systemOut = new ConsoleHandler();
systemOut.setLevel( Level.ALL );
applog.addHandler( systemOut );
applog.setLevel( Level.ALL );

// Prevent logs from processed by default Console handler.
applog.setUseParentHandlers( false ); // Solution 1
Logger.getLogger("").setLevel( Level.OFF ); // Solution 2

Solucion 2

Alternativamente, puede bajar la barra del registrador raíz.

Puede configurarlos por código:

Logger rootLog = Logger.getLogger("");
rootLog.setLevel( Level.FINE );
rootLog.getHandlers()[0].setLevel( Level.FINE ); // Default console handler

O con el archivo de configuración de registro, si lo está utilizando :

.level = FINE
java.util.logging.ConsoleHandler.level = FINE

Al reducir el nivel global, puede comenzar a ver mensajes de las bibliotecas centrales, como algunos componentes Swing o JavaFX. En este caso, puede configurar un Filtro en el registrador raíz para filtrar los mensajes que no son de su programa.

Sheepy
fuente
applog.setUseParentHandlers (falso) este código me ayuda, muchas gracias.
aolphn
4

¿Por qué mi registro de Java no funciona?

proporciona un archivo jar que le ayudará a averiguar por qué su inicio de sesión no funciona como se esperaba. Le brinda un volcado completo de qué registradores y controladores se han instalado y qué niveles están establecidos y en qué nivel en la jerarquía de registro.

mateo
fuente
Esto es un comentario, no una respuesta.
hfontanez
4

POR QUÉ

Como lo mencionó @Sheepy, la razón por la que no funciona es que java.util.logging.Loggertiene un registrador raíz predeterminado Level.INFO, y el ConsoleHandleradjunto a ese registrador raíz también predeterminado Level.INFO. Por lo tanto, con el fin de ver el FINE(, FINERo FINESTsalida), es necesario establecer el valor predeterminado del registrador raíz y su ConsoleHandlera Level.FINEsigue como:

Logger.getLogger("").setLevel(Level.FINE);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);


El problema de su actualización (solución)

Como lo menciona @mins, los mensajes se imprimirán dos veces en la consola para INFOy arriba: primero por el registrador anónimo, luego por su padre, el registrador raíz, que también tiene un ConsoleHandlervalor INFOpredeterminado. Para deshabilitar el registrador raíz, debe agregar esta línea de código:logger.setUseParentHandlers(false);

Hay otras formas de evitar que los registros sean procesados ​​por el controlador de consola predeterminado del registrador raíz mencionado por @Sheepy, por ejemplo:

Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF );

Pero Logger.getLogger("").setLevel( Level.OFF );no funcionará porque solo bloquea el mensaje que se pasa directamente al registrador raíz, no el mensaje proviene de un registrador secundario. Para ilustrar cómo Logger Hierarchyfunciona, dibujo el siguiente diagrama:

ingrese la descripción de la imagen aquí

public void setLevel(Level newLevel)establezca el nivel de registro especificando qué niveles de mensajes registrará este registrador. Los niveles de mensaje inferiores a este valor se descartarán. El valor de nivel Level.OFF se puede utilizar para desactivar el registro. Si el nuevo nivel es nulo, significa que este nodo debe heredar su nivel de su antepasado más cercano con un valor de nivel específico (no nulo).

Joe Yichong
fuente
0

Encontré mi problema real y no se mencionó en ninguna respuesta: algunas de mis pruebas unitarias estaban causando que el código de inicialización del registro se ejecutara varias veces dentro del mismo conjunto de pruebas, arruinando el registro en las pruebas posteriores.

Alex R
fuente
0

Probé otras variantes, esto puede ser adecuado

    Logger logger = Logger.getLogger(MyClass.class.getName());        
    Level level = Level.ALL;
    for(Handler h : java.util.logging.Logger.getLogger("").getHandlers())    
        h.setLevel(level);
    logger.setLevel(level);
// this must be shown
    logger.fine("fine");
    logger.info("info");
zhen_khokh
fuente
0

Esta solución me parece mejor, con respecto a la mantenibilidad y el diseño para el cambio:

  1. Cree el archivo de propiedades de registro incrustándolo en la carpeta del proyecto de recursos, que se incluirá en el archivo jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Cargue el archivo de propiedades desde el código:

    public static java.net.URL retrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    public synchronized void initializeLogger() {
       try (InputStream is = retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
usuario2627331
fuente