Appearance
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 | shPara instalarlo en Ubuntu se puede ejecutar:
bash
sudo apt update && sudo apt install fluent-bitConfigurando 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 astdout(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: contLa expresion
regexcaptura logs formateando los datos respectivamente.springboot_multilinees 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.logEn otra terminal iniciamos el servicio de Fluent Bit así:
bash
fluent-bit -c fluent-bit.yamlLuego, podemos hacer una petición HTTP para generar un log:
bash
curl http://localhost:8080/logSi 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
dockerque permite leer los logs de los contenedores. También podría usarsecontainerd
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-networkAquí definimos dos servicios:
logger-apppara nuestra aplicación Java yfluent-bitpara el colector de logs. Ambos comparten una red llamadalogging-network.
Para iniciar los servicios, ejecutamos:
bash
docker-compose up --buildUna vez que los servicios estén en ejecución, podemos generar logs haciendo peticiones HTTP a nuestra aplicación:
bash
curl http://localhost:8080/logSi 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.