JavaRush /Blogue Java /Random-PT /Logging em Java: o quê, como, onde e com quê?
Roman Beekeeper
Nível 35

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

Publicado no grupo Random-PT
Olá a todos, comunidade JavaRush! Hoje falaremos sobre log Java:
  1. O que é isso, por que é isso. Em que casos é melhor usar, em que casos não é?
  2. Quais são as diferentes implementações de log em Java e o que devemos fazer com essa diversidade?
  3. Níveis de registro. Vamos discutir o que é appender e como configurá-lo corretamente.
  4. Logging de nós e como configurá-los corretamente para que tudo funcione da maneira que desejamos.
Este material é destinado a um público amplo. Ficará claro tanto para quem está começando a conhecer Java, quanto para quem já está trabalhando, mas só descobriu com o logger.info(“log something”); Let's Go!

Por que o registro é necessário?

Vejamos casos reais em que o registro resolveria o problema. Aqui está um exemplo do meu trabalho. Existem pontos de aplicação que se integram a outros serviços. Eu uso o registro desses pontos como um “álibi” : se a integração não funcionar, será fácil descobrir de que lado o problema se originou. Também é aconselhável registrar informações importantes que são salvas no banco de dados. Por exemplo, criando um usuário administrador. Isso é exatamente o que seria bom registrar.

Ferramentas de registro Java

Exploração madeireira: o quê, como, onde e com quê?  - 2Soluções conhecidas para registro em Java incluem:
  • log4j
  • JUL - java.util.logging
  • JCL - registro de bens comuns de Jacarta
  • Logback
  • SLF4J - fachada de registro simples para java
Vamos dar uma olhada rápida em cada um deles, e na parte prática do material tomaremos como base a conexão Slf4j - log4j . Isso pode parecer estranho agora, mas não se preocupe: ao final do artigo tudo ficará claro.

Sistema.err.println

Inicialmente, é claro, havia System.err.println (gravar saída no console). Ainda é usado para obter rapidamente um log durante a depuração. Claro, não há necessidade de falar sobre nenhuma configuração aqui, então vamos apenas lembrar disso e seguir em frente.

Log4j

Esta já era uma solução completa, criada a partir das necessidades dos desenvolvedores. Acabou sendo uma ferramenta realmente interessante de usar. Devido a diversas circunstâncias, esta solução nunca chegou ao JDK, o que incomodou bastante toda a comunidade. log4j tinha opções de configuração para que o log pudesse ser ativado em um pacote com.example.typee desativado em um subpacote com.example.type.generic. Isso possibilitou separar rapidamente o que precisava ser registrado do que não era necessário. É importante observar aqui que existem duas versões do log4j: 1.2.xe 2.x.x, que não são compatíveis entre si . log4j adicionou um conceito como appender , ou seja, uma ferramenta com a qual os logs são gravados e o layout é formatado. Isso permite que você registre apenas o que precisa e como precisa. Falaremos mais sobre o appender um pouco mais tarde.

JUL - java.util.logging

Uma das principais vantagens é a solução - o JUL está incluído no JDK (kit de desenvolvimento Java). Infelizmente, durante o seu desenvolvimento, não foi o popular log4j que se tomou como base, mas sim uma solução da IBM, que influenciou o seu desenvolvimento. Na verdade, no momento existe o JUL, mas ninguém usa. Do “mais ou menos”: no JUL os níveis de logging são diferentes do que está no Logback, Log4j, Slf4j, e isso piora o entendimento entre eles. Criar um logger é mais ou menos semelhante. Para fazer isso você precisa importar:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
O nome da classe é passado especificamente para saber de onde vem o registro. Desde Java 8, é possível passar Supplier<String>. Isso ajuda a contar e criar uma string apenas no momento em que ela é realmente necessária, e não sempre, como era antes. Somente com o lançamento do Java 8 os desenvolvedores resolveram problemas importantes, após os quais o JUL tornou-se verdadeiramente utilizável. Ou seja, métodos com argumentos Supplier<String> msgSupplierconforme mostrado abaixo:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - registro de bens comuns de Jacarta

Devido ao fato de que por muito tempo não existia um padrão da indústria para registro e houve um período em que muitas pessoas criaram seu próprio registrador personalizado, eles decidiram lançar o JCL - um wrapper comum que seria usado em detrimento de outros. Por que? Quando algumas dependências foram adicionadas ao projeto, elas poderiam usar um criador de logs diferente do criador de logs do projeto. Por conta disso, foram adicionados transitivamente ao projeto, o que gerou verdadeiros problemas na hora de tentar juntar tudo. Infelizmente, o wrapper era muito pobre em funcionalidade e não apresentava nenhuma adição. Provavelmente seria conveniente se todos usassem JCL para fazer seu trabalho. Mas na realidade não funcionou assim, então usar JCL não é uma boa ideia no momento.

Logback

Quão espinhoso é o caminho do código aberto... O Logback foi escrito pelo mesmo desenvolvedor do log4j para criar um sucessor para ele. A ideia era a mesma do log4j. As diferenças foram aquelas no logback:
  • performance melhorada;
  • adicionado suporte nativo para slf4j;
  • A opção de filtragem foi expandida.
Por padrão, o logback não requer nenhuma configuração e registra todos os logs do nível DEBUG e superior. Se for necessária configuração, isso pode ser feito via 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

Por volta de 2006, um dos fundadores do log4j deixou o projeto e criou o slf4j - Simple Logging Facade for Java - um wrapper em torno de log4j, JUL, loggins comuns e logback. Como você pode ver, o progresso chegou ao ponto em que eles criaram um wrapper em cima do wrapper... Além disso, está dividido em duas partes: a API, que é utilizada na aplicação, e a implementação, que é adicionada como dependências separadas para cada tipo de registro. Por exemplo, slf4j-log4j12.jar, slf4j-jdk14.jar. Basta conectar a implementação correta e pronto: todo o projeto funcionará com ela. Slf4j oferece suporte a todos os novos recursos, como formatação de string para registro. Houve esse problema antes. Digamos que haja uma entrada de log:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
userHá uma conversão implícita no objeto user.toString()devido à concatenação de strings, e isso leva tempo, o que torna o sistema lento. E está tudo bem se depurarmos o aplicativo. Os problemas começam se o nível de log desta classe for INFO e superior. Ou seja, esse log não deve ser anotado e a concatenação de strings também não deve ser realizada. Em teoria, isso deveria ter sido decidido pela própria biblioteca de log. Além disso, este acabou por ser o maior problema da primeira versão do log4j. Eles não entregaram uma solução normal, mas sugeriram fazer assim:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Ou seja, em vez de uma linha de registro, eles sugeriram escrever 3(!). O registro deveria minimizar as alterações no código, e três linhas contradiziam claramente a abordagem geral. O slf4j não teve problemas de compatibilidade com o JDK e a API, então uma bela solução surgiu imediatamente:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
onde {}denotam inserções de argumentos que são passados ​​no método. Ou seja, o primeiro {}corresponde a user, o segundo {}- request.getRemoteAddr(). Devido a isso, somente se o nível de registro permitir o registro, esta mensagem poderá ser concatenada em uma única. Depois disso, o SJF4J cresceu rapidamente em popularidade e é atualmente a melhor solução. Portanto, consideraremos o log usando o exemplo de um pacote configurável slf4j-log4j12.

O que precisa ser registrado

Claro, você não deve registrar tudo. Às vezes isso é desnecessário e até perigoso. Por exemplo, se você fornecer dados pessoais de alguém e eles de alguma forma vierem à tona, haverá problemas reais, especialmente em projetos orientados para o Ocidente. Mas também há algo que é obrigatório registrar :
  1. Início/fim da aplicação. Precisamos saber se o aplicativo realmente foi iniciado conforme esperávamos e encerrado conforme esperado.
  2. Questões de segurança. Aqui seria bom registrar tentativas de adivinhação de senha, registrar logins de usuários importantes, etc.
  3. Alguns estados do aplicativo . Por exemplo, a transição de um estado para outro num processo de negócio.
  4. Algumas informações para depuração , com um nível de registro apropriado.
  5. Alguns scripts SQL. Existem casos reais em que isso é necessário. Novamente, ajustando habilmente os níveis, excelentes resultados podem ser alcançados.
  6. Threads executadas (Thread) podem ser registradas nos casos em que a operação correta é verificada.

Erros populares de registro

Existem muitas nuances, mas aqui estão alguns erros comuns:
  1. Excesso de registro. Você não deve registrar todas as etapas que teoricamente poderiam ser importantes. Existe uma regra: os logs podem carregar o desempenho em no máximo 10%. Caso contrário, haverá problemas de desempenho.
  2. Registrando todos os dados em um arquivo. Isso tornará a leitura/gravação muito difícil em um determinado ponto, sem mencionar que há limites de tamanho de arquivo em determinados sistemas.
  3. Usando níveis de log incorretos. Cada nível de registro tem limites claros e deve ser respeitado. Se o limite for vago, você poderá 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
Quais são os níveis de registro? Para classificar de alguma forma os logs, foi necessário dar certas designações e distinções. Para este propósito, foram introduzidos níveis de registro. O nível é definido no aplicativo. Se uma entrada pertencer a um nível inferior ao designado, ela não será inserida no log. Por exemplo, temos logs que são usados ​​para depurar o aplicativo. No trabalho normal de produção (quando o aplicativo é usado para a finalidade pretendida), esses logs não são necessários. Portanto, o nível de registro será maior do que o de depuração. Vejamos os níveis usando log4j como exemplo. Outras soluções, exceto JUL, utilizam os mesmos níveis. Aqui estão eles em ordem decrescente:
  • OFF: nenhum log é gravado, todos serão ignorados;
  • FATAL: um erro após o qual o aplicativo não poderá mais funcionar e será interrompido, por exemplo, erro de falta de memória da JVM;
  • ERRO: A taxa de erro quando há problemas que precisam ser resolvidos. O erro não interrompe o aplicativo como um todo. Outras consultas podem funcionar corretamente;
  • WARN: Indica logs que contêm um aviso. Ocorreu uma ação inesperada, apesar disso o sistema resistiu e concluiu a solicitação;
  • INFO: um log que registra ações importantes no aplicativo. Não são erros, não são avisos, são ações esperadas do sistema;
  • DEBUG: logs necessários para depurar a aplicação. Para garantir que o sistema faz exatamente o que se espera dele, ou para descrever a ação do sistema: “método1 começou a funcionar”;
  • TRACE: logs de menor prioridade para depuração, com o nível de log mais baixo;
  • ALL: nível em que serão registrados todos os logs do sistema.
Acontece que se o nível de registro INFO estiver habilitado em algum lugar do aplicativo, todos os níveis serão registrados, começando em INFO e até FATAL. Se o nível de registro for FATAL, somente os registros com esse nível serão registrados.

Gravando e enviando logs: Appender

Consideraremos este processo usando log4j como exemplo: ele oferece amplas oportunidades para gravação/envio de logs:
  • para gravar em uma solução de arquivo DailyRollingFileAppender ;
  • para receber dados no console do aplicativo - ConsoleAppender ;
  • para gravar logs no banco de dados - JDBCAppender ;
  • controlar a transmissão via TCP/IP - TelnetAppender ;
  • para garantir que o registro não afete o desempenho - AsyncAppender .
Existem diversas outras implementações: a lista completa pode ser encontrada aqui . A propósito, se o anexador necessário não estiver disponível, isso não será um problema. Você pode escrever seu próprio appender implementando a interface Appender , que aceita apenas log4j.

Nós de registro

Para a demonstração usaremos a interface slf4j e a implementação do log4j. Criar um logger é muito simples: você precisa escrever o seguinte em uma classe chamada MainDemo, na qual será feito o log:
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Isso criará um logger para nós. Para fazer uma entrada no log, você pode usar vários métodos que indicam em que nível as entradas serão feitas. 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 log4j.properties. Please, provide them");
logger.error("Connection refused to host = {}", host);
Mesmo que estejamos passando na aula, no final é o nome completo da turma com os pacotes que está anotado. Isso é feito para que você possa dividir a criação de log em nós e configurar um nível de criação de log e um anexador para cada nó. Por exemplo, o nome da classe: com.github.romankh3.logginglecture.MainDemo- nela foi criado um logger. E é assim que ele pode ser dividido em nós de registro. O nó principal é o RootLogger nulo . Este é o nó que recebe todos os logs de toda a aplicação. O restante pode ser descrito conforme mostrado abaixo: Exploração madeireira: o quê, como, onde e com quê?  - 4Appenders configuram seu trabalho especificamente em nós de registro. Agora, usando log4j.properties como exemplo , veremos como configurá-los.

Configuração passo a passo do Log4j.properties

Agora vamos configurar tudo passo a passo e ver o que pode ser feito:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Esta linha indica que estamos registrando um anexador CONSOLE que usa a implementação org.apache.log4j.ConsoleAppender. Este anexador grava dados no console. A seguir, vamos registrar outro appender que irá gravar em um arquivo:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
É importante observar que os anexadores ainda precisarão ser configurados. Uma vez que já tenhamos anexadores registrados, podemos determinar qual nível de registro estará nos nós e quais anexadores serão usados.

log4j.rootLogger=DEBUG, CONSOLE, ARQUIVO

  • log4j.rootLogger significa que configuraremos o nó principal, que contém todos os logs;
  • após o sinal de igual, a primeira palavra indica em que nível e superior os logs serão gravados (no nosso caso é DEBUG);
  • depois da vírgula são indicados todos os anexadores que serão utilizados.
Para configurar um nó de criação de log específico, é necessário usar a seguinte entrada:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
onde log4j.logger.é usado para configurar um nó específico, no nosso caso é com.github.romankh3.logginglecture. E agora vamos falar sobre como configurar o anexador CONSOLE:
# CONSOLE appender customisation
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 podemos definir o nível a partir do qual o anexador irá processar. Situação real: uma mensagem com nível info foi recebida pelo nó logging e repassada ao appender que está atribuído a ele, mas o appender, com nível warning e superior, aceitou esse log, mas não fez nada com ele. Em seguida, você precisa decidir qual modelo estará na mensagem. Estou usando PatternLayout no exemplo, mas existem muitas soluções por aí. Eles não serão divulgados neste artigo. Um exemplo de configuração de um anexador FILE:
# File appender customisation
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
Aqui você pode configurar em qual arquivo os logs serão gravados, como pode ser visto em
log4j.appender.FILE.File=./target/logging/logging.log
A gravação vai para o arquivo logging.log. Para evitar problemas com o tamanho do arquivo, você pode definir o máximo: neste caso, 1MB. MaxBackupIndex - informa quantos desses arquivos existirão. Se for criado mais do que este número, o primeiro arquivo será excluído. Para ver um exemplo real onde o log está configurado, você pode acessar o repositório aberto no GitHub.

Vamos consolidar o resultado

Tente fazer tudo descrito sozinho:
  • Crie seu próprio projeto semelhante ao do exemplo acima.
  • Se você tiver conhecimento sobre o uso do Maven, nós o usaremos; caso contrário, aqui está um link para um artigo que descreve como conectar a biblioteca.

Vamos resumir

  1. Conversamos sobre quais soluções existem em Java.
  2. Quase todas as bibliotecas de registro conhecidas foram escritas sob o controle de uma pessoa:D
  3. Aprendemos o que precisa ser registrado e o que não.
  4. Descobrimos os níveis de registro.
  5. Conhecemos os nós de registro.
  6. Vimos o que é um appender e para que serve.
  7. Configuramos o arquivo log4j.proterties passo a passo.

Materiais adicionais

  1. JavaRush: Registro. Desenrole uma bola de stectrace
  2. JavaRush: Palestra sobre Logger
  3. Habr: registro em Java. Olá Mundo
  4. Habr: Java logging: a história de um pesadelo
  5. Youtube: cursos Golovach. Exploração madeireira. Parte 1 , Parte 2 , Parte 3 , Parte 4
  6. Log4j: anexador
  7. Log4j: layout
Veja também meus outros artigos:
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION