Por que as mensagens de registro Level.FINE não estão sendo exibidas?

110

Os JavaDocs para ojava.util.logging.Level estado:


Os níveis em ordem decrescente são:

  • SEVERE (valor mais alto)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (valor mais baixo)

Fonte

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

Resultado

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 . . .

Declaração do problema

Meu exemplo define o Levelcomo FINER, portanto, esperava ver 2 mensagens para cada loop. Em vez disso, vejo uma única mensagem para cada loop (as Level.FINEmensagens estão faltando).

Questão

O que precisa ser alterado para ver a saída FINE( FINERou FINEST)?

Atualizar (solução)

Graças à resposta de Vineet Reynolds , essa versão funciona de acordo com minha expectativa. Ele exibe 3 x INFOmensagens e 3 x FINEmensagens.

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
fonte
10
Parece-me que você terá as mensagens impressas duas vezes no console para INFO e acima: primeiro pelo logger anônimo, depois por seu pai, o logger global que também possui um ConsoleHandler configurado como INFO por padrão. Para desabilitar o logger global, você precisa adicionar esta linha de código: logger.setUseParentHandlers (false);
minutos de
Só quero confirmar o meu comentário sobre as duplas. você obterá duas saídas, a menos que use .setUseParentHandlers (false);
xpagesbeast

Respostas:

124

Os loggers apenas registram a mensagem, ou seja, eles criam os registros de log (ou solicitações de log). Eles não publicam as mensagens nos destinos, que são tratados pelos Manipuladores. Definir o nível de um registrador só faz com que ele crie registros de log correspondentes a esse nível ou superior.

Você pode estar usando um ConsoleHandler(não consegui inferir onde sua saída é System.err ou um arquivo, mas presumo que seja o primeiro), cujo padrão é a publicação de registros de log do nível Level.INFO. Você terá que configurar este manipulador, para publicar registros de log de nível Level.FINERe superior, para o resultado desejado.

Eu recomendaria ler o guia Java Logging Overview , a fim de entender o design subjacente. O guia cobre a diferença entre o conceito de Logger e Handler.

Editando o nível do manipulador

1. Usando o arquivo de configuração

O arquivo de propriedades java.util.logging (por padrão, este é o logging.propertiesarquivo JRE_HOME/lib) pode ser modificado para alterar o nível padrão do ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Criação de manipuladores em tempo de execução

Isso não é recomendado, pois resultaria na substituição da configuração global. Usar isso em toda a sua base de código resultará em uma configuração de logger possivelmente não gerenciável.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
fonte
2
Obrigado. Isso funcionou. Eu percebo agora porque eu estava inicialmente tão confuso. Eu já havia trabalhado com níveis de registro, mas minha implementação naquele momento simplesmente descartou todas as mensagens registradas em uma lista que era exibida sem considerar o Handler.
Andrew Thompson
3
De nada. E sim, o design atinge você, se alguém estiver escrevendo registradores que simplesmente despejam strings em um arquivo, console etc.
Vineet Reynolds
7
E alterar o logging.properties na biblioteca JRE global é gerenciável?
James Anderson
2
Observe que o nível do registrador em si deve ser menos restritivo do que o nível do manipulador. Antes de registrar, o Java verifica o nível máximo entre o criador de logs e o manipulador. Portanto, se você definir apenas handler.setLevel (Level.FINER); você não verá nada porque o nível do registrador padrão é Level.INFO. Você deve defini-lo como FINER, FINEST ou ALL. Diga logger.setLevel (Level.ALL);
Jeff_Alieffson,
Acho que isso vai resolver o problema de uma linha, mesmo quando você usa loggers anônimos e nomeados em um ambiente padrão:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

O porque

java.util.logging tem um logger root cujo padrão é Level.INFOe um ConsoleHandler anexado a ele que também assume o padrão Level.INFO. FINEé menor que INFO, portanto, as mensagens finas não são exibidas por padrão.


Solução 1

Crie um logger para toda a sua aplicação, por exemplo, a partir do nome do seu pacote ou uso Logger.getGlobal(), e conecte seu próprio ConsoleLogger a ele. Em seguida, peça ao logger root para desligar (para evitar saída duplicada de mensagens de nível superior) ou peça ao logger para não encaminhar os logs para o root.

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

Solução 2

Alternativamente, você pode diminuir a barra do logger root.

Você pode defini-los por código:

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

Ou com o arquivo de configuração de registro, se você o estiver usando :

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

Ao diminuir o nível global, você pode começar a ver mensagens de bibliotecas centrais, como de alguns componentes Swing ou JavaFX. Nesse caso, você pode definir um Filtro no logger raiz para filtrar mensagens que não são do seu programa.

Sheepy
fonte
applog.setUseParentHandlers (false) esse código me ajuda, muito obrigado.
aolphn
4

por que meu log de java não está funcionando

fornece um arquivo jar que o ajudará a descobrir por que seu login não está funcionando conforme o esperado. Ele fornece um dump completo de quais loggers e manipuladores foram instalados e quais níveis estão definidos e em que nível na hierarquia de log.

Mateus
fonte
Este é um comentário, não uma resposta.
hfontanez
4

PORQUE

Conforme mencionado por @Sheepy, a razão pela qual não funciona é que java.util.logging.Loggertem um logger root que tem como padrão Level.INFO, e o ConsoleHandleranexado a esse logger root também tem como padrão Level.INFO. Portanto, para ver a saída FINE( FINERou FINEST), você precisa definir o valor padrão do logger raiz e seu ConsoleHandlerda Level.FINEseguinte maneira:

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


O problema da sua atualização (solução)

Conforme mencionado por @mins, você terá as mensagens impressas duas vezes no console para INFOe acima: primeiro pelo logger anônimo, depois por seu pai, o logger root que também tem um ConsoleHandlerdefinido como INFOpor padrão. Para desativar o logger raiz, você precisa adicionar esta linha de código:logger.setUseParentHandlers(false);

Existem outras maneiras de evitar que os logs sejam processados ​​pelo gerenciador de console padrão do logger raiz mencionado por @Sheepy, por exemplo:

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

Mas Logger.getLogger("").setLevel( Level.OFF );não funcionará porque apenas bloqueia a mensagem passada diretamente para o logger root, e não a mensagem que vem de um logger filho. Para ilustrar como Logger Hierarchyfunciona, desenho o seguinte diagrama:

insira a descrição da imagem aqui

public void setLevel(Level newLevel)definir o nível de log especificando quais níveis de mensagem serão registrados por este logger. Níveis de mensagem inferiores a esse valor serão descartados. O valor de nível Level.OFF pode ser usado para desligar o registro. Se o novo nível for nulo, significa que este nó deve herdar seu nível de seu ancestral mais próximo com um valor de nível específico (não nulo).

Joe Yichong
fonte
0

Eu encontrei meu problema real e não foi mencionado em nenhuma resposta: alguns dos meus testes de unidade estavam fazendo com que o código de inicialização de log fosse executado várias vezes no mesmo conjunto de testes, bagunçando o log nos testes posteriores.

Alex R
fonte
0

Tentei outras variantes, isso pode ser adequado

    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
fonte
0

Esta solução me parece melhor, em relação à manutenção e design para mudanças:

  1. Crie o arquivo de propriedade de registro incorporando-o na pasta do projeto de recursos, para ser incluído no arquivo jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Carregue o arquivo de propriedade do 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) {
          // ...
       }
    }
user2627331
fonte