Um Guia Para Logback

Um Guia Para Logback

*1. Visão geral *

Logback é uma das estruturas de log mais usadas na Comunidade Java. É um substituição para seu antecessor, Log4j. O Logback oferece uma implementação mais rápida que o Log4j, oferece mais opções de configuração e mais flexibilidade no arquivamento de arquivos de log antigos.

Esta introdução apresentará a arquitetura do Logback e mostrará como você pode usá-lo para melhorar seus aplicativos.

===* 2. Arquitetura de Logback *

Três classes compreendem a arquitetura Logback; Logger, Appender e Layout.

Um criador de logs é um contexto para mensagens de log. Essa é a classe com a qual os aplicativos interagem para criar mensagens de log.

Os anexadores colocam as mensagens de log em seus destinos finais. Um criador de logs pode ter mais de um Appender. Geralmente, pensamos em Appenders como anexados a arquivos de texto, mas o Logback é muito mais potente que isso.

O layout prepara mensagens para saída. O Logback suporta a criação de classes personalizadas para formatar mensagens, bem como opções de configuração robustas para as existentes.

===* 3. Configuração *

====* 3.1 Dependência Maven *

O logback usa o Simple Logging Facade para Java (SLF4J) como sua interface nativa. Antes de começarmos a registrar mensagens, precisamos adicionar Logback e Slf4j ao nosso pom.xml:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.8.0-beta2</version>
    <scope>test</scope>
</dependency>

====* 3.2 Caminho de classe *

O logback também requer _logback-classic.jar _ no caminho de classe para o tempo de execução.

Adicionaremos isso a pom.xml como uma dependência de teste:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

4. Exemplo básico e configuração

Vamos começar com um exemplo rápido do uso do Logback em um aplicativo.

Primeiro, precisamos de um arquivo de configuração. Criaremos um arquivo de texto chamado logback.xml e o colocaremos em algum lugar do nosso caminho de classe:

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT"/>
  </root>
</configuration>

Em seguida, precisamos de uma classe simples com um método main:

public class Example {

    private static final Logger logger
      = LoggerFactory.getLogger(Example.class);

    public static void main(String[] args) {
        logger.info("Example log from {}", Example.class.getSimpleName());
    }
}

Essa classe cria um Logger e chama _info () _ para gerar uma mensagem de log.

Quando executamos Exemplo, vemos nossa mensagem registrada no console:

20:34:22.136 [main] INFO Example - Example log from Example

É fácil ver por que o Logback é tão popular; estamos em funcionamento em minutos.

Essa configuração e código nos dão algumas dicas de como isso funciona.

  1. Temos um appender nomeado STDOUT que faz referência a um nome de classe ConsoleAppender.

  2. Há um padrão que descreve o formato da nossa mensagem de log.

  3. Nosso código cria um Logger e passamos nossa mensagem a ele por meio de um método _info () _ .

Agora que entendemos o básico, vamos dar uma olhada mais de perto.

===* 5. Logger Contextos *

====* 5.1. Criando um contexto *

Para registrar uma mensagem no Logback, inicializamos um Logger do SLF4J ou Logback:

private static final Logger logger
  = LoggerFactory.getLogger(Example.class);

E então usei:

logger.info("Example log from {}", Example.class.getSimpleName());

Este é o nosso contexto de log. Quando o criamos, passamos à LoggerFactory nossa classe. Isso dá ao Logger um nome (também há uma sobrecarga que aceita uma String) .

Os contextos de log existem em uma hierarquia que se assemelha à hierarquia de objetos Java:

* Um criador de logs é um pai quando não há ancestrais entre ele e um filho *

Por exemplo, a classe Example abaixo está no pacote com..logback. Há outra classe chamada ExampleAppender no pacote com..logback.appenders.

ExampleAppender’s Logger é filho do Example’s Logger.

Todos os registradores são descendentes do registrador raiz predefinido.

Um Logger possui um Level, _ que pode ser definido via configuração ou com _Logger.setLevel () . A configuração do nível no código substitui os arquivos de configuração.

Os níveis possíveis são, em ordem de precedência: TRACE, DEBUG, INFO, WARN e ERROR .* * ** Cada nível possui um método correspondente que usamos para registrar uma mensagem nesse nível.

*Se um registrador não receber um nível explicitamente atribuído, ele herdará o nível de seu ancestral mais próximo.* O registrador raiz assume como padrão _DEBUG._ Veremos como substituir isso abaixo.

5.2 Usando um contexto

Vamos criar um programa de exemplo que demonstre o uso de um contexto nas hierarquias de log:

ch.qos.logback.classic.Logger parentLogger =
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com..logback");

parentLogger.setLevel(Level.INFO);

Logger childlogger =
  (ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com..logback.tests");

parentLogger.warn("This message is logged because WARN > INFO.");
parentLogger.debug("This message is not logged because DEBUG < INFO.");
childlogger.info("INFO == INFO");
childlogger.debug("DEBUG < INFO");

Quando executamos isso, vemos estas mensagens:

20:31:29.586 [main] WARN com..logback - This message is logged because WARN > INFO.
20:31:29.594 [main] INFO com..logback.tests - INFO == INFO

Começamos recuperando um Logger chamado com..logback e o convertemos em um ch.qos.logback.classic.Logger.

Um contexto de Logback é necessário para definir o nível na próxima instrução; observe que o logger abstrato do SLF4J não implementa setLevel () .

Definimos o nível do nosso contexto como INFO _; e, em seguida, criamos outro logger chamado _com..logback.tests.

Registramos duas mensagens com cada contexto para demonstrar a hierarquia. O logback registra as mensagens WARN, _ e _INFO e filtra as mensagens DEBUG .

Agora, vamos usar o logger raiz:

ch.qos.logback.classic.Logger logger =
  (ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com..logback");
logger.debug("Hi there!");

Logger rootLogger =
  (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
logger.debug("This message is logged because DEBUG == DEBUG.");

rootLogger.setLevel(Level.ERROR);

logger.warn("This message is not logged because WARN < ERROR.");
logger.error("This is logged.");

Vemos essas mensagens quando executamos este trecho:

20:44:44.241 [main] DEBUG com..logback - Hi there!
20:44:44.243 [main] DEBUG com..logback - This message is logged because DEBUG == DEBUG.
20:44:44.243 [main] ERROR com..logback - This is logged.

Para concluir, começamos com um contexto Logger e imprimimos uma mensagem DEBUG.

Em seguida, recuperamos o registrador raiz usando seu nome definido estaticamente e definimos seu nível como ERROR.

E, finalmente, demonstramos que o Logback realmente filtra qualquer instrução com menos de um erro.

5.3. Mensagens parametrizadas

Diferentemente das mensagens nos trechos de amostra acima, as mensagens de log mais úteis exigiam anexar Strings. Isso implica alocar memória, serializar objetos, concatenar _Strings, _ e potencialmente limpar o lixo posteriormente.

Considere a seguinte mensagem:

log.debug("Current count is " + count);

Incorremos no custo de criar a mensagem, independentemente de o Logger registrá-la ou não.

O Logback oferece uma alternativa com suas mensagens parametrizadas:

log.debug("Current count is {}", count);
*Os colchetes \ {} aceitarão qualquer _Object_ e usarão o método _toString () _ para criar uma mensagem somente depois de verificar se a mensagem de log é necessária.*

Vamos tentar alguns parâmetros diferentes:

String message = "This is a String";
Integer zero = 0;

try {
    logger.debug("Logging message: {}", message);
    logger.debug("Going to divide {} by {}", 42, zero);
    int result = 42/zero;
} catch (Exception e) {
    logger.error("Error dividing {} by {} ", 42, zero, e);
}

Este trecho gera:

21:32:10.311 [main] DEBUG com..logback.LogbackTests - Logging message: This is a String
21:32:10.316 [main] DEBUG com..logback.LogbackTests - Going to divide 42 by 0
21:32:10.316 [main] ERROR com..logback.LogbackTests - Error dividing 42 by 0
java.lang.ArithmeticException:/by zero
  at com..logback.LogbackTests.givenParameters_ValuesLogged(LogbackTests.java:64)
...

Vemos como um String, _ um _int, _ e um _Integer podem ser transmitidos como parâmetros.

Além disso, quando uma Exception é passada como o último argumento para um método de log, o Logback imprime o rastreamento de pilha para nós.

*6. Configuração detalhada *

Nos exemplos anteriores, estávamos usando o arquivo de configuração de 11 linhas que criamos no link: #example [seção 4] para imprimir mensagens de log no console. Esse é o comportamento padrão do Logback; se não conseguir encontrar um arquivo de configuração, ele cria um ConsoleAppender e o associa ao registrador raiz.

====* 6.1 Localizando informações de configuração *

Um arquivo de configuração pode ser colocado no caminho de classe e denominado logback.xml ou logback-test.xml.

Veja como o Logback tentará encontrar dados de configuração:

  1. Procure os arquivos denominados logback-test.xml, logback.groovy, * * * *ou logback.xml no caminho de classe, nessa ordem.

  2. Se a biblioteca não encontrar esses arquivos, ela tentará usar os https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html [ServiceLoader] _ do Java para localizar um implementador do _com.qos.logback.classic.spi.Configurator.

  3. Configure-se para registrar a saída diretamente no console

Nota: a versão atual do Logback não suporta a configuração do Groovy porque não existe uma versão do Groovy compatível com o Java 9.

6.2. Configuração básica

Vamos dar uma olhada no nosso link: #example [exemplo de configuração.]

O arquivo inteiro está em tags <configuration> * * **.

*Vemos uma tag que declara um _Appender_ do tipo _ConsoleAppender_ e o denomina _STDOUT_. Aninhado nessa tag está um codificador. Ele possui um padrão com o que se parece com os códigos de escape _sprintf-style_:*
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

Por último, vemos uma tag root. Essa tag define o registrador raiz para o modo DEBUG e associa sua saída ao Appender chamado STDOUT:

<root level="debug">
    <appender-ref ref="STDOUT"/>
</root>

6.3. Solução de problemas de configuração

Os arquivos de configuração de logback podem ficar complicados, portanto, existem vários mecanismos internos para solução de problemas.

Para ver informações de depuração enquanto o Logback processa a configuração, você pode ativar o log de depuração:

<configuration debug="true">
  ...
</configuration>

O Logback imprimirá as informações de status no console à medida que processa a configuração:

23:54:23,040 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
  at [file:/Users/egoebelbecker/ideaProjects/logback-guide/out/test/resources/logback-test.xml]
23:54:23,230 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender
  of type [ch.qos.logback.core.ConsoleAppender]
23:54:23,236 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
23:54:23,247 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type
  [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
23:54:23,308 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
23:54:23,309 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
23:54:23,310 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
23:54:23,313 |-INFO in [email protected] - Registering current configuration
  as safe fallback point

Se avisos ou erros forem encontrados durante a análise do arquivo de configuração, o Logback gravará mensagens de status no console.

Existe um segundo mecanismo para imprimir informações de status:

<configuration>
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
    ...
</configuration>

O StatusListener intercepta mensagens de status e as imprime durante a configuração e enquanto o programa está em execução.

A saída de todos os arquivos de configuração é impressa, tornando-o útil para localizar arquivos de configuração “não autorizados” no caminho de classe.

6.4. Recarregando a configuração automaticamente

Recarregar a configuração do log enquanto um aplicativo está em execução é uma poderosa ferramenta de solução de problemas. O logback torna isso possível com o parâmetro scan:

<configuration scan="true">
  ...
</configuration>

O comportamento padrão é verificar se há alterações no arquivo de configuração a cada 60 segundos. Modifique esse intervalo adicionando scanPeriod:

<configuration scan="true" scanPeriod="15 seconds">
  ...
</configuration>

Podemos especificar valores em milissegundos, segundos, minutos ou horas.

6.5. Modificando Loggers

Em nosso arquivo de amostra acima, definimos o nível do registrador raiz e o associamos ao console Appender **. * *

Podemos definir o nível para qualquer logger:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="com..logback" level="INFO"/>
    <logger name="com..logback.tests" level="WARN"/>
    <root level="debug">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

Vamos adicionar isso ao nosso caminho de classe e executar este código:

Logger foobar =
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com..foobar");
Logger logger =
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com..logback");
Logger testslogger =
  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com..logback.tests");

foobar.debug("This is logged from foobar");
logger.debug("This is not logged from logger");
logger.info("This is logged from logger");
testslogger.info("This is not logged from tests");
testslogger.warn("This is logged from tests");

Vemos esta saída:

00:29:51.787 [main] DEBUG com..foobar - This is logged from foobar
00:29:51.789 [main] INFO com..logback - This is logged from logger
00:29:51.789 [main] WARN com..logback.tests - This is logged from tests

Ao não definir o nível de nossos Loggers programaticamente, a configuração os define;* com..foobar * * herda * *_ DEBUG _ * do registrador raiz.

Os registradores ** * *também herdam o appender-ref do registrador raiz. Como veremos abaixo, podemos substituir isso.

====* 6.6. Substituição variável *

Os arquivos de configuração de logback suportam variáveis. Definimos variáveis ​​dentro do script de configuração ou externamente. Uma variável pode ser especificada em qualquer ponto de um script de configuração no lugar de um valor.

Por exemplo, aqui está a configuração para um FileAppender:

<property name="LOG_DIR" value="/var/log/application"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${LOG_DIR}/tests.log</file>
    <append>true</append>
    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

Na parte superior da configuração, declaramos uma property * * * denominada LOG_DIR . * * *Em seguida, a usamos como parte do caminho para o arquivo dentro da definição appender.

As propriedades são declaradas em uma marca _ <property> _ nos scripts de configuração. Mas eles também estão disponíveis em fontes externas, como propriedades do sistema. Podemos omitir a declaração property neste exemplo e definir o valor de LOG_DIR na linha de comando:

$ java -DLOG_DIR=/var/log/application com..logback.LogbackTests

Nós especificamos o valor da propriedade com _ $ \ {propertyname} ._ Logback implementa variáveis ​​como substituição de texto. A substituição variável pode ocorrer em qualquer ponto do arquivo de configuração em que um valor possa ser especificado.

===* 7. Appenders *

Loggers passam LoggingEvents para Appenders. Appenders realizam o trabalho real de criação de log. Geralmente pensamos no log como algo que vai para um arquivo ou console, mas o Logback é capaz de muito mais. Logback-core fornece vários anexos úteis.

====* 7.1 ConsoleAppender *

Já vimos o ConsoleAppender * * * em ação. Apesar do nome, ConsoleAppender anexa mensagens a System.out * * *ou System.err.

Ele usa um OutputStreamWriter para armazenar buffer na E/S, portanto, direcioná-lo para System.err não resulta em gravação sem buffer.

====* 7.2 FileAppender *

FileAppender * * * *anexa mensagens a um arquivo. Ele suporta uma ampla gama de parâmetros de configuração. Vamos adicionar um anexo de arquivo à nossa configuração básica:

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>tests.log</file>
        <append>true</append>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com..logback" level="INFO"/>
    <logger name="com..logback.tests" level="WARN">
        <appender-ref ref="FILE"/>
    </logger>

    <root level="debug">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

O FileAppender é configurado com um nome de arquivo via _ <file> ._ A tag <append> * * * instrui o Appender * * *a anexar um arquivo existente em vez de truncá-lo. Se executarmos o teste várias vezes, veremos que a saída do log é anexada ao mesmo arquivo.

Se executarmos novamente o teste acima, as mensagens de com..logback.tests vão para o console e para um arquivo chamado tests.log.* O criador de logs descendente herda a associação do criador de logs raiz com o ConsoleAppender com sua associação com o FileAppender. Os anexos são cumulativos. *

Podemos substituir esse comportamento:

<logger name="com..logback.tests" level="WARN" additivity="false" >
    <appender-ref ref="FILE"/>
</logger>

<root level="debug">
    <appender-ref ref="STDOUT"/>
</root>

Definir additivity como false * * * *desativa o comportamento padrão. Tests não fará logon no console e nenhum de seus descendentes.

====* 7.3. RollingFileAppender *

Muitas vezes, anexar mensagens de log ao mesmo arquivo não é o comportamento que precisamos. Queremos que os arquivos “rolem” com base no tempo, no tamanho do arquivo de log ou em uma combinação de ambos.

Para isso, temos _RollingFileAppender: _

<property name="LOG_FILE" value="LogFile"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_FILE}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- daily rollover -->
        <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>

        <!-- keep 30 days' worth of history capped at 3GB total size -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

Um RollingFileAppender * * * possui um RollingPolicy . * * Nesta configuração de exemplo, vemos um TimeBasedRollingPolicy . * * *

Semelhante ao _FileAppender, _ configuramos este appender com um nome de arquivo. Declaramos uma propriedade e a usamos para isso, porque reutilizaremos o nome do arquivo abaixo.

Definimos um fileNamePattern dentro de RollingPolicy .* * * Esse padrão define não apenas o nome dos arquivos, mas também a frequência com que os rola. TimeBasedRollingPolicy * * *examina o padrão e rola no período mais finamente definido.

Por exemplo:

<property name="LOG_FILE" value="LogFile"/>
<property name="LOG_DIR" value="/var/logs/application"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_DIR}/${LOG_FILE}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_DIR}/%d{yyyy/MM}/${LOG_FILE}.gz</fileNamePattern>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>

O arquivo de log ativo é /var/logs/application/LogFile .* * * Este arquivo passa para o início de cada mês em /Ano Atual/Mês Atual/LogFile.gz * * e RollingFileAppender cria * * *um novo arquivo ativo.

Quando o tamanho total dos arquivos arquivados atinge 3 GB, RollingFileAppender * * * *exclui os arquivos primeiro a entrar, primeiro a sair.

Existem códigos para uma semana, hora, minuto, segundo e até milissegundo. O logback tem uma referência aqui.

RollingFileAppender * * * *também possui suporte interno para compactação de arquivos. Ele comprime nossos arquivos compactados porque os denominamos LogFile.gz.

TimeBasedPolicy * * * não é nossa única opção para rolar arquivos. O Logback também oferece SizeAndTimeBasedRollingPolicy, * , que rolará com base no tamanho atual do arquivo de log e no tempo. Ele também oferece um FixedWindowRollingPolicy **, que rola os nomes dos arquivos de log toda vez que o criador de logs é iniciado.

Também podemos escrever nossos próprios https://logback.qos.ch/manual/appenders.html#onRollingPolicies [RollingPolicy] .

7.4. Apêndices personalizados

Podemos criar anexos personalizados, estendendo uma das classes de aplicativo básico do Logback. Temos um tutorial para criar anexos personalizados:/custom-logback-appender [aqui].

*8. Layouts *

Layouts formatam mensagens de log. Como o restante do Logback, os Layouts * * * *são extensíveis e podemos criar o nosso. No entanto, o PatternLayout padrão oferece o que a maioria dos aplicativos precisa e então alguns.

Usamos PatternLayout em todos os nossos exemplos até agora:

<encoder>
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>

Este script de configuração contém a configuração para PatternLayoutEncoder .* * * Passamos um Encoder para o nosso Appender, * * *e esse codificador usa o PatternLayout para formatar as mensagens.

O texto na tag _ <padrão> _ define como as mensagens de log estão sendo formatadas.* PatternLayout implementa uma grande variedade de palavras de conversão e modificadores de formato para criar padrões. *

Vamos dividir este aqui. PatternLayout reconhece palavras de conversão com%, portanto, as conversões em nosso padrão geram:

  • _% d \ {HH: mm: ss.SSS} _ - um registro de data e hora com horas, minutos, segundos e milissegundos

  • _ [% thread] _ - o nome do thread que gera a mensagem de log, entre colchetes

  • % - 5level - o nível do evento de registro, preenchido com 5 caracteres

  • _% logger \ {36} _ - o nome do logger, truncado para 35 caracteres

  • % msg% n - as mensagens de log seguidas pelo caractere separador de linha dependente da plataforma

Então, vemos mensagens semelhantes a esta:

21:32:10.311 [main] DEBUG com..logback.LogbackTests - Logging message: This is a String

Uma lista exaustiva de palavras de conversão e modificadores de formato pode ser encontrada em aqui.

9. Conclusão

Neste guia abrangente, abordamos os fundamentos do uso do Logback em um aplicativo.

Examinamos os três principais componentes da arquitetura do Logback: registradores, anexadores e layout. O Logback’s possui scripts de configuração poderosos, usados ​​para manipular componentes para filtrar e formatar mensagens. Também analisamos os dois anexadores de arquivos mais usados ​​para criar, rolar, organizar e compactar arquivos de log.

Como de costume, podem ser encontrados trechos de código over no GitHub.