Java – Configurando o Log (Logback) de sua aplicação para o Logstash (ELK)

Um dos grandes problemas de aplicações, são Logs, e para sua solução existem diversas ferramentas que ajudam a coletar, armazenar e monitorar. Nesse post vou mostrar como configurar sua aplicação para que a saída de log com o logback seja no formato dos coletores mais utilizados do mercado, o LogStash. O LogStash é um coletor / transformador de logs, no caso, muito utilizado na stack ELK (Elastic-Search, Logstash, Kibana), ele coleta logs de diferentes fontes e persiste no Elastic-search; Não irei entrar em detalhes da configuração do LogStash ou da ferramenta e sua utilização, irei apenas mostrar como configurar sua aplicação para que a saida do arquivo de log seja compatível com o coletor;

Dependências

Primeiramente vamos adicionar algumas dependências em nosso projeto:

<!-- Dependencia do log -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>

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

<!-- Dependencia do encoder do logstash para o logback -->
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>6.0</version>
        </dependency>

Configurando o logback.xml

Após configurado as dependências, iremos configurar nosso arquivo logback.xml, o Logback é a implementação da especificação do SLF4J, sua implementação precisa de um arquivo de configuração aonde é definido o formato da saida de log;

O arquivo a seguir, inseri dois appenders, um para o formato json e um para o formato tradicional de console, fiz isso para que seja mais fácil alternar de um para outro, tendo em vista que o formato json é ruim para a leitura em desenvolvimento, porém não se esqueça que o formato json é o formato em que o LogStash irá reconhecer;

Note também que é nesse arquivo que iremos configurar o encoder do formato da saída do log.

Encoder: “net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder”

<configuration>

    <appender name="out-json" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <fieldName>ts</fieldName>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <loggerName>
                    <fieldName>logger</fieldName>
                </loggerName>
                <logLevel>
                    <fieldName>severity</fieldName>
                </logLevel>
                <callerData>
                    <classFieldName>class</classFieldName>
                    <methodFieldName>method</methodFieldName>
                    <lineFieldName>line</lineFieldName>
                    <fileFieldName>file</fileFieldName>
                </callerData>
                <threadName>
                    <fieldName>thread</fieldName>
                </threadName>
                <mdc/>
                <arguments>
                    <includeNonStructuredArguments>false</includeNonStructuredArguments>
                </arguments>
                <stackTrace>
                    <fieldName>stack</fieldName>
                </stackTrace>
                <message>
                    <fieldName>message</fieldName>
                </message>
            </providers>
        </encoder>
    </appender>

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

    <root level="info">
        <!--
        Can be custom your own environment (LOG_APPENDER)
        to load a specify appender ( out-json or out-console ).
        Default value: out-json
        -->
        <appender-ref ref="${LOG_APPENDER:-out-json}"/>
    </root>


</configuration>

Exemplo de uso

Praticamente é só isso! dessa maneira você já consegue logar usando o Logger normalmente e toda a saída será reconhecida pelo leitor do LogStash, exemplo de impressão de log:

package br.com.helpdev.log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SampleLog {

  private static final Logger LOG = LoggerFactory.getLogger(SampleLog.class);

  public static void main(String[] args) {
    LOG.info("Olá");
  }
}

Saída no console:

{   “ts”:”2020-02-24T18:57:55.258Z”,
   “logger”:”br.com.helpdev.log.SampleLog”,
   “severity”:”INFO”,
   “class”:”br.com.helpdev.log.SampleLog”,
   “method”:”main”,
   “file”:”SampleLog.java”,
   “line”:11,
   “thread”:”main”,
   “message”:”Olá”
}

Configuração do Logstash

Como curiosidade estou inserindo a configuração do arquivo do logstash para a leitura do arquivo de log. Disse que não entraria em detalhes sobre esse quesito, porém, o arquivo de configuração não é algo complexo que não possa ser mostrado;

Crie um arquivo para representar a leitura do seu log, exemplo logback.conf, esse arquivo contém instruções para ler os arquivos de log de um path e jogor no elasticsearch com seu devido index;

input {
    file {
        path => "/var/log/java/my_app/*.log"
        codec => "json"
        type => "logback"
    }
}
 
output {
    if [type]=="logback" {
         elasticsearch {
             hosts => [ "localhost:9200" ]
             index => "logback-%{+YYYY.MM.dd}"
        }
    }
}

Para execução do Logstash com esse arquivo de configuração, pode-se executa-lo da seguinte maneira:

$ bin/logstash -f logback.conf

Structured Logging

Criando uma estrutura para o log. Seria muito comum pensarmos em um objeto estruturado, ou até mesmo logar objetos complexos no formato que o logstash consiga interpretar ( json ). Para isso a própria dependência do et.logstash.logback nos fornece uma classe chamada StructuredArguments que permite realizarmos conversões de objetos no formato adequado para a impressão de Log, podemos converter objetos complexos, dicionários, etc;

Veja a seguir como usar a StructuredArguments para imprimirmos um objeto complexo em nosso Log;

package br.com.helpdev.log;

import net.logstash.logback.argument.StructuredArguments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SampleLog {

  private static final Logger LOG = LoggerFactory.getLogger(SampleLog.class);

  public static void main(String[] args) {
    SampleClass sampleClass = new SampleClass("Olá", 123);
    LOG.info("Any message", StructuredArguments.keyValue("model", sampleClass));
  }

  public static class SampleClass {
    private String arg1;
    private int arg2;

    public SampleClass(String arg1, int arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;
    }

    public String getArg1() {
      return arg1;
    }

    public int getArg2() {
      return arg2;
    }
  }
}

Saída no console:

{   “ts”:”2020-02-24T19:22:00.907Z”,
   “logger”:”br.com.helpdev.log.SampleLog”,
   “severity”:”INFO”,
   “class”:”br.com.helpdev.log.SampleLog”,
   “method”:”main”,
   “file”:”SampleLog.java”,
   “line”:13,
   “thread”:”main”,
   “model”:{      “arg1″:”Olá”,
      “arg2”:123
},
   “message”:”Any message”
}

Note que agora temos como organizar todo nosso log, ou imprimir objetos complexos, tornando cada vez melhor e indexável ao ElasticSearch nossos dados;

Referências:
https://www.innoq.com/en/blog/structured-logging/
https://www.baeldung.com/java-application-logs-to-elastic-stack
http://logback.qos.ch
https://github.com/jochenchrist/structured-logging-plain

Help DEV – Analista desenvolvedor Java / Android https://helpdev.com.br/zarelli

Java – Configurando o Log (Logback) de sua aplicação para o Logstash (ELK)

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Rolar para o topo