Skip to content

Fluent Bit ​

Este colector de registros (logs) código abierto es ligero y eficiente, con opciones variadas para tomar archivos de salida y llevarlos a una fuente de datos. Es una alternativa a FluentD (de los mismos autores)

Instalando Fluent Bit ​

Fluent Bit se puede descargar para Linux o macOS con el siguiente comando:

bash
curl https://raw.githubusercontent.com/fluent/fluent-bit/master/install.sh | sh

Para instalarlo en Ubuntu se puede ejecutar:

bash
sudo apt update && sudo apt install fluent-bit

Configurando Fluent Bit con YAML ​

Para configurar Fluent Bit usando la versión de YAML (convencionalmente se usa TOML), podemos establecer un archivo fluent-bit.yaml con un contenido como el siguiente:

yaml
service:
  flush: 1
  log_level: info
  parsers_file: parsers.yaml

pipeline:
  inputs:
    - name: tail
      tag: springboot_logs
      path: ./logs/application.log
      read_from_head: true
      multiline.parser: springboot_multiline
  outputs:
    - name: stdout
      match: *

Primero se define el servicio y luego una entrada tail, finalmente la salida a stdout (u otro destino).

Para complementar la configuración con el archivo parsers.yaml podemos tener un contenido como el siguiente:

yaml
parsers:
  - name: springboot_parser
    format: regex
    regex: '^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[([^\]]*)\] (\w+) +(\S+) - (.*)'
    time_key: time
    time_format: '%Y-%m-%d %H:%M:%S.%L'
    time_keep: true

multiline_parsers:
  - name: springboot_multiline
    type: regex
    flush_timeout: 1000
    rules:
      - state: start_state
        regex: '/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*/'
        next_state: cont
      - state: cont
        regex: '/^\s+at.*/'
        next_state: cont

La expresion regex captura logs formateando los datos respectivamente.
springboot_multiline es usado cuando en Java se generan errores en pila (Stack Traces).

Ejemplo usando programa Java con JBang ​

Para ver en acción la magia de Fluent Bit, consideremos un ejemplo esencial en Java y SpringBoot, usando JBang para agilizar y simplificar el ejercicio. Definimos la clase LoggerApp.java con el siguiente contenido:

java
//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.springframework.boot:spring-boot-starter-web:3.3.2

package logapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@SpringBootApplication
public class LoggerApp {

    public static void main(String[] args) {
        SpringApplication.run(LoggerApp.class, args);
    }

    @RestController
    static class LogController {
        private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

        @GetMapping("/log")
        public String generateLog(@RequestParam(defaultValue = "test") String message) {
            String timestamp = LocalDateTime.now().format(formatter);
            System.out.printf("%s [http-thread] INFO  LoggerApp$LogController - Request with message: %s%n", timestamp, message);
            try {
                if (message.equals("error")) {
                    throw new RuntimeException("Simulated error for testing");
                }
            } catch (Exception e) {
                System.err.printf("%s [http-thread] ERROR LoggerApp$LogController - Error in request%n", timestamp);
                e.printStackTrace();
            }
            return "Log generated: " + message;
        }
    }
}

Para iniciar el programa anterior ejecutamos:

bash
jbang LoggerApp.java > logs/application.log

En otra terminal iniciamos el servicio de Fluent Bit así:

bash
fluent-bit -c fluent-bit.yaml

Luego, podemos hacer una petición HTTP para generar un log:

bash
curl http://localhost:8080/log

Si se desea generar un error intenta con: curl http://localhost:8080/log\?message=error

Fluent Bit usando un contonedor Docker ​

Para continuar nuestro ejercicio usando Docker definiremos un Dockerfile con un contenido como el siguiente:

dockerfile
FROM openjdk:17-jdk-slim
COPY LoggingAppStdout.java .
RUN curl -Ls https://sh.jbang.dev | bash -s - app install
EXPOSE 8080
CMD ["jbang", "run", "LoggerApp.java"]

A partir de una imagen con Java se copia la clase usada anteriormente y se instala JBang.

Para usar la salida de Docker con Fluent Bit usamos, configuramos fluent-bit.yaml con el siguiente contenido:

yaml
service:
  flush: 1
  log_level: info
  parsers_file: parsers.yaml

pipeline:
  inputs:
    - name: docker
      tag: springboot_logs
  outputs:
    - name: stdout
      match: *

En este caso usamos la entrada docker que permite leer los logs de los contenedores. También podría usarse containerd

Para usar nuestra aplicación y FuentBit juntos, definimos un archivo docker-compose.yml con un contenido como el siguiente:

yaml
version: '3.8'
services:
  logger-app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ./logs:/app/logs
    networks:
      - logging-network

  fluent-bit:
    image: fluent/fluent-bit:latest
    volumes:
      - ./fluent-bit.yaml:/fluent-bit/etc/fluent-bit.yaml
      - ./parsers.yaml:/fluent-bit/etc/parsers.yaml
    networks:
      - logging-network

Aquí definimos dos servicios: logger-app para nuestra aplicación Java y fluent-bit para el colector de logs. Ambos comparten una red llamada logging-network.

Para iniciar los servicios, ejecutamos:

bash
docker-compose up --build

Una vez que los servicios estén en ejecución, podemos generar logs haciendo peticiones HTTP a nuestra aplicación:

bash
curl http://localhost:8080/log

Si se desea generar un error intenta con: curl http://localhost:8080/log?message=error

Con este ejercicio hemos visto cómo integrar Fluent Bit con una aplicación Java simplificada, permitiendo la recolección y visualización de logs de manera eficiente. Esta configuración es ideal para entornos de desarrollo y producción, facilitando el monitoreo y la depuración de aplicaciones.

Ejemplo de DaemonSet para FluentBit en Kubernetes ​

Si piensas en Kubernetes encuentras que se usa FluentBit con DaemonSet de un modo eficiente. Como un abrebocas dejo un ejemplo para ilustrar brevemente el manifiesto:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      name: fluent-bit
  template:
    metadata:
      labels:
        name: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:latest
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         1
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf
    [INPUT]
        Name              tail
        Path              /var/log/containers/*.log
        Parser            docker
        Tag               kube.*
        Refresh_Interval  5
    [OUTPUT]
        Name  stdout
        Match *

Este manifiesto con DaemonSet despliega Fluent Bit en cada nodo del clúster, recolectando logs de todos los contenedores y enviándolos a la salida configurada.
Nótese que para la configuración de Fluent Bit se usa en este ejemlo TOML.