elk

Una aplicacion que esta desplegada en produccion no esta terminada hasta que no tiene un buen sistema de logs. En la era de los microservicios y las aplicaciones distribuidas en las que el intercambio de informacion se hace mayoritariamente a traves de HTTP es muy importante logar esas peticiones, tantos las entrantes como las salientes para poder depurar nuestra aplicacion en caso de fallo. Con el framework ELK podemos logar de una forma sencilla estas peticiones

Que es ELK

ELK es un conjunto de herramientas de Elastic las cuales unidas nos aportan una forma muy sencilla de recoger logs de aplicaciones. Las herramientas que componen ELK son:

  • Elasticsearch: La base de datos donde se almacenan los logs
  • Logstash: Es una herramienta de ingestion de datos. Es la que se encargara de coger los datos y mandarlos a Elasticsearch
  • Kibana: La herramienta en la que veremos nuestros datos

Gracias a estas 3 herramientas tenemos todo lo necesario para guardar y mostrar los logs. Solo necesitamos generarlos de alguna manera en nuestra aplicacion

Logbook

Logbook es una libreria creada por Zalando para logar peticiones y respuestas HTTP. Esta empresa tiene muchos servicios y necesitaban poder logar lo que estaba pasando en ellos asi que crearon la libreria

Tiene muchas opciones y se integra perfectamente con Spring Boot por ejemplo. Se pueden logar las peticiones en texto plano o JSON e incluso podemos definir un nombre de categoria para aislarla de los demas logs de aplicacion

Su configuracion es muy sencilla. Veamos como se hace en una aplicacion Spring Boot

Lo primero es añadir la dependencia en nuestro pom.xml

<dependency>
  <groupId>org.zalando</groupId>
  <artifactId>logbook-spring-boot-starter</artifactId>
  <version>1.4.0</version>
</dependency>

Y ahora solo debemos poner en el archivo application.yml las opciones de configuracion necesarias

logbook:
  format.style: json
  write:
    level: INFO
    category: elk-data

Una vez tenemos cambiado esto es hora de configurar el archivo de log para que nos saque en un fichero a parte los logs que nos interesan

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <include resource="org/springframework/boot/logging/logback/base.xml"/>

  <appender name="ELK-DATA" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>elk-data.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <Pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSXXX", UTC} %msg%n</Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>elk-data.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>10MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
  </appender>

  <logger name="elk-data" level="INFO">
    <appender-ref ref="ELK-DATA" />
  </logger>
</configuration>

El siguiente paso es configurar un entorno ELK. Para no complicarnos mucho la vida vamos a usar Docker Compose en aras de simplificar todo. Crearemos el fichero docker-compose.yml con el siguiente codigo

version: '3'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:5.6.1
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
      xpack.security.enabled: "false"
    networks:
      - elk

  logstash:
    image: docker.elastic.co/logstash/logstash:5.6.1
    ports:
      - "5000:5000"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
      xpack.security.enabled: "false"
    volumes:
      - ./conf/logstash:/usr/share/logstash/pipeline
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:5.6.1
    ports:
      - "5601:5601"
    environment:
      SERVER_NAME: kibana
      ELASTICSEARCH_URL: http://elasticsearch:9200
      xpack.security.enabled: "false"
    networks:
      - elk
    depends_on:
      - elasticsearch
networks:
  elk:

Crearemos el fichero de configuracion de logstash en conf/logstash.conf

input {
  tcp {
    port => 5000
  }
}

filter {
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA:log}"
    }
  }
  json {
    source => "log"
    remove_field => ["message"]
  }
  mutate {
    remove_field => ["log"]
  }
}

output {
  elasticsearch {
    hosts => "elasticsearch:9200"
  }
}

Lo que estamos haciendo con este fichero es parsear la fecha de nuestro log y parsear el json que tenemos como mensaje de log y añadirlo al json que se grabara en elasticsearch

Para la ingesta de datos hemos creado un socket tcp en el puerto 5000. Se pueden configurar muchas formas de ingesta como leer de un fichero, por HTTP, de base de datos…

Arrancamos nuestro stack con el comando docker-compose up -d y empezamos a generar logs en nuestra aplicacion haciendo peticiones HTTP

Cuando tengamos ya los logs en un fichero podemos mandarlo a logstash usando Netcat con el comando nc localhost 5000 < logs/elk/elk-data.log

Solo falta configurar Kibana para poder ver los logs que hemos mandado a Logstash. Para ello iremos a la url localhost:5601 y configuramos el campo Time Filter field name a timestamp. Pulsamos Create y ya tenemos nuestros logs en Kibana
Kibana