CodeGym /Blogue Java /Random-PT /Logging: o quê, como, onde e com quê?
John Squirrels
Nível 41
San Francisco

Logging: o quê, como, onde e com quê?

Publicado no grupo Random-PT
Olá a todos da comunidade CodeGym! Logging: o quê, como, onde e com quê?  - 1 Hoje vamos falar sobre registro:
  1. O que é, porque existe, quando deve usar, quando deve evitar.
  2. Quais implementações de criação de log estão disponíveis em Java e o que você deve fazer com todas essas opções de criação de log.
  3. E níveis de log. Discutiremos o que é appender e como configurá-lo corretamente.
  4. Nós de log e como configurá-los corretamente para que tudo funcione da maneira que queremos.
Este material destina-se a um público amplo. Ficará claro para quem está começando a conhecer Java, assim como para quem já está trabalhando, mas apenas explorou. logger.info("log something"); Vamos lá!

Por que você precisa registrar?

Vejamos alguns casos reais em que o log pode resolver um problema. Aqui está um exemplo do meu trabalho. Existem pontos em que um aplicativo se integra a outros serviços. Eu uso o registro nesses pontos para estabelecer uma espécie de "álibi" : se a integração não estiver funcionando, fica fácil descobrir qual lado está com o problema. Também é desejável registrar informações importantes armazenadas em um banco de dados. Por exemplo, a criação de um usuário administrador. Este é precisamente o tipo de coisa que seria bom registrar.

Ferramentas para fazer login em Java

Dentre as soluções de logging mais conhecidas em Java, podemos destacar as seguintes:
  • Log4j
  • JUL — java.util.logging
  • JCL — Registro em Jacarta Commons
  • Logback
  • SLF4J — Fachada de registro simples para Java
Daremos uma visão geral de cada um deles. Em seguida, usaremos uma ligação slf4j - log4j como base para uma discussão prática. Isso pode parecer estranho agora, mas não se preocupe: ao final do artigo, tudo ficará claro.

System.err.println

No início, havia System.err.println (exibindo entradas de log no console). Ainda hoje, essa técnica é usada para obter um log rapidamente durante a depuração. Claro, não há configurações para discutir aqui, então apenas lembre-se deste método e seguiremos em frente.

Log4j

Esta é uma solução completa que os desenvolvedores criaram por necessidade. O resultado é uma ferramenta realmente interessante que você pode usar. Por diversas circunstâncias, essa solução não foi parar no JDK, fato que desagradou muito toda a comunidade. O Log4j tem opções de configuração que permitem ativar o login no com.example.typepacote e desativá-lo no com.example.type.genericsubpacote. Isso torna possível excluir rapidamente o código que não precisa ser registrado. É importante observar aqui que existem duas versões do Log4j: 1.2.xe 2.xx, e elas são incompatíveis entre si . Log4j adicionou os conceitos de appender(uma ferramenta usada para escrever logs) e layout (formatação de log). Isso permite que você registre apenas o que você precisa e registre exatamente como você precisa. Falaremos mais sobre appender um pouco mais tarde.

JUL — java.util.logging

Um dos principais benefícios desta solução é que o JUL está incluído no JDK (Java Development Kit). Infelizmente, quando foi desenvolvido, seus criadores não o basearam no popular utilitário Log4j, mas sim em uma solução da IBM. Essa decisão teve consequências. A realidade é que ninguém usa JUL agora. Os níveis de log em JUL diferem do que Logback, Log4j e Slf4j têm. Isso torna mais difícil para eles se entenderem. A criação de um registrador é mais ou menos semelhante. Para fazer isso, você precisa fazer uma importação:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
O nome da classe é passado, então sabemos de onde virá nosso registro. A partir do Java 8, você pode passar Supplier<String>. Isso nos ajuda a ler e criar uma linha apenas quando realmente precisamos, e não sempre, como acontecia anteriormente. Somente com o lançamento do Java 8 os desenvolvedores finalmente resolveram problemas importantes e tornaram o JUL verdadeiramente utilizável. Ou seja, métodos com um Supplier<String> msgSupplierparâmetro, conforme mostrado abaixo:

public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL — Registro em Jacarta Commons

Como não havia nenhum padrão da indústria em relação ao registro por muito tempo e muitas pessoas criaram seus próprios registradores personalizados, foi tomada a decisão de lançar o JCL, um wrapper geral que poderia ser usado em cima de outros. Por que? Às vezes, as dependências adicionadas ao projeto usavam um registrador diferente daquele do projeto. Por causa disso, eles foram adicionados ao projeto como dependências transitivas, e isso criou problemas reais ao tentar juntar tudo. Infelizmente, o wrapper não era muito funcional e não acrescentava nada. Provavelmente seria conveniente se todos usassem JCL. Mas não foi isso que aconteceu, então usar JCL não é a melhor ideia no momento.

Logback

O caminho do código aberto é espinhoso... O mesmo desenvolvedor que escreveu o Log4j também escreveu o Logback como uma estrutura de log sucessora. Foi baseado na mesma ideia do Log4j. As diferenças no Logback são:
  • performance melhorada
  • adicionado suporte nativo para Slf4j
  • opções de filtragem expandidas
Por padrão, o Logback não requer nenhuma configuração e registra todos os eventos no nível DEBUG e superior. Se você precisar de alguma personalização, poderá obtê-la por meio de uma configuração XML:

<configuration> 
    <appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
        <file>app.log</file> 
        <encoder> 
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern> 
        </encoder> 
    </appender> 
    <logger name="org.hibernate.SQL" level="DEBUG" /> 
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" /> 
    <root level="info"> 
        <appender-ref ref="FILE" /> 
    </root> 
</configuration>

SLF4J — Fachada de registro simples para Java

Em algum momento de 2006, um dos pais fundadores do Log4j deixou o projeto e criou o Slf4j (Simple Logging Facade for Java), um wrapper para Log4j, JUL, registro comum e Logback. Como podem ver, avançamos a ponto de criar um wrapper sobre outro wrapper... Neste caso, ele está dividido em duas partes: Uma API que é utilizada na aplicação, e uma implementação que é adicionada com dependências para cada tipo de criação de log. Por exemplo, slf4j-log4j12.jare slf4j-jdk14.jar. Você precisa ligar a implementação correta e pronto: todo o seu projeto vai usar. O Slf4j suporta todos os recursos mais recentes, como strings de formatação para registro. Anteriormente, havia esse problema. Digamos que criamos uma entrada de log como esta:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
Devido ao operador de concatenação, o userobjeto torna-se silenciosamente uma string graças a user.toString(). Isso leva tempo e retarda o sistema. E isso pode ser bom se estivermos depurando o aplicativo. Começamos a encontrar problemas se o nível de log dessa classe for INFO ou superior. Em outras palavras, não devemos escrever esta entrada de log (para INFO ou superior) e não devemos usar concatenação de strings. Em teoria, a própria biblioteca de registro deve tratar disso. Acontece que esse acabou sendo o maior problema na primeira versão do Log4j. Não forneceu uma solução decente, mas sugeriu fazer algo assim:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Ou seja, em vez de uma linha de código para registro, eles sugeriram escrever 3! O registro deve minimizar as alterações de código e as três linhas claramente violam essa abordagem geral. O Slf4j não teve problemas de compatibilidade com o JDK e a API, então uma boa solução surgiu imediatamente:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
where {}denota espaços reservados para os argumentos passados ​​para o método. Ou seja, o primeiro {}corresponde a user, e o segundo {}corresponde a request.getRemoteAddr(). Ao fazer isso, realizaremos a concatenação de strings apenas se o nível de log exigir que gravemos a entrada de log. Depois disso, Sjf4j começou a crescer rapidamente em popularidade. Atualmente, é a melhor solução. Assim, vamos dar uma olhada no log usando uma slf4j-log4j12ligação.

O que precisa ser registrado

Claro, você não deve registrar tudo. Isso muitas vezes não é necessário e às vezes até perigoso. Por exemplo, se você registrar os dados pessoais de alguém e eles vazarem, haverá problemas reais, especialmente em projetos focados nos mercados ocidentais. Mas também há coisas que você definitivamente deveria registrar :
  1. Início/fim da aplicação. Precisamos saber se o aplicativo realmente começou e terminou conforme o esperado.
  2. Problemas de segurança. Aqui seria bom registrar tentativas de adivinhar a senha de alguém, instâncias em que os administradores entram, etc.
  3. Certos estados de aplicativos . Por exemplo, a transição de um estado para outro em um processo de negócios.
  4. Certas informações de depuração junto com o nível de log correspondente.
  5. Certos scripts SQL. Existem casos do mundo real em que isso é necessário. Mas, novamente, ajustando habilmente os níveis de log, você pode obter excelentes resultados.
  6. Os threads em execução podem ser registrados ao verificar se as coisas estão funcionando corretamente.

Erros populares no registro

Existem muitas nuances aqui, mas faremos uma menção especial a alguns erros comuns:
  1. Log excessivo. Você não deve registrar todas as etapas que teoricamente podem ser importantes. Aqui está uma boa regra: os logs não devem exceder 10% da carga. Caso contrário, haverá problemas de desempenho.
  2. Registrando todos os dados em um arquivo. Em algum momento, isso tornará muito difícil a leitura/gravação do log, sem falar no fato de que certos sistemas têm limites de tamanho de arquivo.
  3. Usando níveis de log incorretos. Cada nível de log tem limites claros e eles devem ser respeitados. Se um limite não estiver claro, você pode chegar a um acordo sobre qual nível usar.

Níveis de registro

x: Visível
FATAL ERRO AVISAR INFORMAÇÕES DEPURAR VESTÍGIO TODOS
DESLIGADO
FATAL x
ERRO x x
AVISAR x x x
INFORMAÇÕES x x x x
DEPURAR x x x x x
VESTÍGIO x x x x x x
TODOS x x x x x x x
O que são níveis de log? Para criar de alguma forma uma hierarquia de entradas de log, certas convenções e delimitações são necessárias. É por isso que os níveis de log foram introduzidos. O nível é definido no aplicativo. Se uma entrada estiver abaixo de um nível especificado, ela não será registrada. Por exemplo, temos logs que usamos ao depurar o aplicativo. Durante a operação normal (quando o aplicativo é usado para o fim a que se destina), esses logs não são necessários. Portanto, o nível de log é maior do que para depuração. Vejamos os níveis de log usando Log4j. Além de JUL, outras soluções usam os mesmos níveis de log. Aqui estão eles em ordem decrescente:
  • OFF: Nenhuma entrada de log é registrada; tudo é ignorado.
  • FATAL: Um erro que impede a execução do aplicativo. Por exemplo, "JVM sem erro de memória".
  • ERRO: Erros neste nível indicam problemas que precisam ser resolvidos. O erro não interrompe o aplicativo como um todo. Outras solicitações podem funcionar corretamente.
  • WARN: Entradas de log que representam um aviso. Algo inesperado aconteceu, mas o sistema foi capaz de atender e atendeu à solicitação
  • INFO: Entradas de log que indicam ações importantes no aplicativo. Estes não são erros ou avisos. Eles são eventos esperados do sistema.
  • DEBUG: As entradas de logs precisam depurar o aplicativo. Para garantir que o aplicativo faça exatamente o que é esperado ou para descrever as ações executadas pelo aplicativo, ou seja, "Método inserido1".
  • TRACE: Entradas de log de prioridade mais baixa para depuração. O nível de log mais baixo.
  • ALL: Um nível de log para gravar todas as entradas de log do aplicativo.
No nível de log INFO está habilitado em algum lugar no aplicativo, então as entradas para cada nível serão registradas, de INFO a FATAL. Se o nível de log FATAL for definido, apenas as entradas de log com esse nível serão gravadas.

Log e envio de logs: Appender

Vamos considerar como tudo isso funciona quando usamos o Log4j, que oferece amplas oportunidades para escrever/enviar logs:
  • gravar em um arquivo—DailyRollingFileAppender
  • para gravar informações no console —ConsoleAppender
  • gravar logs em um banco de dados —JDBCAppender
  • para gerenciar o envio de logs por TCP/IP —TelnetAppender
  • para garantir que o registro não afete negativamente o desempenho —AsyncAppender
Existem mais algumas implementações: uma lista completa está disponível aqui . A propósito, se o appender que você precisa não existir, não há problema. Você pode escrever seu próprio appender implementando a interface Appender , que o Log4j suporta.

Nós de registro

Para fins de demonstração, usaremos uma interface Slf4j, com uma implementação de Log4j. Criar um logger é muito simples: em uma classe chamada MainDemo, que fará alguns logs, precisamos adicionar o seguinte:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Isso criará um registrador para nós. Para fazer uma entrada de log, existem vários métodos disponíveis cujos nomes refletem qual nível de log será usado. Por exemplo:

logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
Embora estejamos passando a classe, o nome final é o nome completo da classe, incluindo os pacotes. Isso é feito para que posteriormente você possa dividir o registro em nós e configurar o nível de registro e o anexador para cada nó. Por exemplo, o logger foi criado na com.github.romankh3.logginglecture.MainDemoclasse. O nome fornece a base para criar uma hierarquia de nós de registro. O nó principal é o RootLogger de nível superior . Este é o nó que recebe todas as entradas de log para todo o aplicativo. Os nós restantes podem ser representados conforme mostrado abaixo: Logging: o quê, como, onde e com quê?  - 3Appenders são configurados para nós de registro específicos. Agora vamos examinar o arquivo log4j.properties para ver um exemplo de como configurá-los.

Um guia passo a passo para o arquivo log4j.properties

Vamos configurar tudo passo a passo e ver o que é possível:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Esta linha informa que estamos registrando o anexador CONSOLE, que usa a implementação org.apache.log4j.ConsoleAppender. Este anexador grava informações no console. Em seguida, registramos outro appender. Este irá gravar em um arquivo:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
É importante observar que os próprios anexadores ainda precisam ser configurados. Depois de registrar nossos anexadores, podemos determinar quais níveis de log e quais anexadores serão usados ​​nos nós.

log4j.rootLogger=DEBUG, CONSOLE, ARQUIVO

  • log4j.rootLogger significa que estamos configurando o nó raiz, que contém todas as entradas de log
  • A primeira palavra após o sinal de igual indica o nível mínimo de log a ser gravado (no nosso caso, é DEBUG)
  • Após a vírgula, indicamos todos os appenders a serem utilizados.
Para configurar um nó de registro mais específico, você usaria uma entrada como esta:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
where log4j.logger.é usado para referenciar um nó específico. No nosso caso, com.github.romankh3.logginglecture. agora vamos falar sobre como configurar o appender CONSOLE:

# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
Aqui vemos que é possível definir o nível específico no qual o appender começará a funcionar. Aqui está um exemplo do que realmente acontece: suponha que uma mensagem com o nível INFO seja recebida pelo nó de log e passada para o appender atribuído a ela. Se o limite do anexador for definido como WARN, ele receberá a entrada de log, mas não fará nada com ela. Em seguida, precisamos decidir qual layout a mensagem usará. Eu uso PatternLayout no exemplo, mas existem muitas outras opções. Não os abordaremos neste artigo. Exemplo de configuração do anexador FILE:

# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
Você pode configurar o arquivo específico no qual as entradas de log serão gravadas, como pode ser visto nesta linha:

log4j.appender.FILE.File=./target/logging/logging.log
A entrada é gravada no logging.logarquivo. Para evitar problemas com o tamanho do arquivo, você pode configurar o máximo, que neste caso é 1MB. MaxBackupIndexindica quantos arquivos de log existirão. Se precisarmos criar mais arquivos do que isso, o primeiro arquivo será excluído. Para ver um exemplo real em que o log está configurado, você pode acessar o repositório público no GitHub.

Reforce o que discutimos

Tente por conta própria fazer tudo o que descrevemos:
  • Crie seu próprio projeto semelhante ao nosso exemplo acima.
  • Se você sabe como usar o Maven, use-o. Caso contrário, leia este tutorial, que descreve como conectar a biblioteca.

Resumindo

  1. Falamos sobre as soluções de logging que existem em Java.
  2. Quase todas as bibliotecas de registro conhecidas foram escritas por uma pessoa :D
  3. Aprendemos o que deve e o que não deve ser registrado.
  4. Descobrimos os níveis de log.
  5. Fomos apresentados aos nós de log.
  6. Vimos o que é um appender e para que serve.
  7. Criamos um arquivo log4j.proterties passo a passo.

Materiais adicionais

  1. CodeGym: Lição do registrador
  2. Weekly Geekly: Log de Java. Olá Mundo
  3. Horror na codificação: o problema com o registro
  4. YouTube: Entendendo o Java Logging Hell - O Básico. Java Logging Hell e como ficar de fora
  5. Log4j: Appender
  6. Log4j: Layout
Veja também meu outro artigo:
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION