Grafana Stack đ 3. Collecte des logs avec OpenTelemetry
Dans lâarticle prĂ©cĂ©dent , nous avons collectĂ© les mĂ©triques de lâapplication Spring. Reste maintenant Ă collecter les logs de cette application. GrĂące Ă la configuration de Logback que lâon a mis en place dans le premier article , les logs de Spring sortent au formta JSON, ce qui va grandement simplifier les pipeline de collecte.
Les autres articles de la série :
- Observabilité avec Spring Boot 3
- Collecte des métriques avec OpenTelemetry
- Collecte des logs avec OpenTelemetry
- DĂ©ploiement dâun Grafana
Prérequis au déploiement
Docker Compose
Tout comme dans lâarticle prĂ©cĂ©dent, il faut complĂ©ter le fichier docker-compose.yml
avec un nouveau service : Loki
.
Service prometheus
Loki (toujours de Grafana Labs), est un moteur de stockage de logs. Sur le mĂȘme principe que Prometheus il va permettre de conserver les logs applicatifs pour les restituer via des requĂȘtes LogQL. Lâapproche de Loki est diffĂ©rente de celle dâElastic par exemple, car il ne va indexer que les meta-donnĂ©es des logs et non tout leur contenu.
Voilà la déclaration du service Loki dans le compose.
services:
loki:
image: grafana/loki:2.8.1
restart: unless-stopped
environment:
- LOKI_RETENTION_PERIOD: 90d
command:
- -config.file=/etc/loki/local-config.yaml
- -config.expand-env=true
volumes:
- ./.compose/loki/local-config.yaml:/etc/loki/local-config.yaml
- loki_data:/loki
networks:
metrics: {}
volumes:
loki_data: {}
networks:
metrics: {}
Loki se configure via le fichier local-config.yaml
passé en paramÚtre de la ligne de commande dans le dockerfile ci-dessus.
Les paramÚtres de configuration sont détaillés dans la documentation
, voilà le fichier utilisé pour notre application.
On notera dans les commandes du compose le paramĂštre -config.expand-env=true
qui autorise Ă mettre des variables dâenvironnement dans le fichier de configuration suivant.
---
auth_enabled: false
server:
http_listen_port: ${LOKI_LISTEN_PORT:-3100}
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
compactor:
retention_enabled: true
limits_config:
retention_period: ${LOKI_RETENTION_PERIOD:-30d}
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
Il sâagit du fichier de configuration par dĂ©faut avec quelques amĂ©liorations tout de mĂȘme :
- On active une rétention de 30 jours par défaut. Sans ça, Loki garde les logs Ad-Vitam.
- On utilise la rĂ©solution de variables dâenv pour permettre de modifier les valeurs du port et la durĂ©e de rĂ©tention.
Le stockage filesystem
est largement suffisant pour le cas de notre application, mais il prĂ©sente lâinconvĂ©nient de ne pas ĂȘtre scalable, contrairement Ă dâautres systĂšmes proposĂ©s.
Service OpenTelemetry
Nous avons dĂ©jĂ dĂ©ployĂ© un collecteur lors de lâarticle prĂ©cĂ©dent , on va modifier sa configuration pour y ajouter la collecte des logs.
Alternatives
Il existe plusieurs alternatives pour collecter les logs dâune application Spring vers un serveur Loki.
- Utiliser Loki4j , un plugin logback pour transmettre les logs directement Ă Loki. Il se prĂ©sente sous la forme dâun appender.
- Utiliser opentelemetry-logback-appender-1.0 , un autre plugin logback pour transmettre les logs Ă OpenTelemetry cette fois.
- Spring propose aussi des solutions pour envoyer les logs vers Open Telemetry , là on envoie aussi les traces avec et les métriques.
- Configurer un receiver Open Telemetry
Finalement, de toutes ces possibilitĂ©s, jâai optĂ© pour la derniĂšre. Le fait de lire les logs produits sur le disque plutĂŽt que de se les faire envoyer est moins intrusif pour lâapplication. Moins de risque de ralentir lâapplication et câest mieux dĂ©corrĂ©lĂ©.
Configuration du receiver
On a dĂ©jĂ vu la configuration dâOpen Telemetry, il suffit de rajouter un receiver pour scruter les logs. Dans le cas de notre application packagĂ©e en docker, il faut aller chercher les fichiers de log du conteneur, pour ça, il y a le plugin filelog
.
OTEL se configure en 4 Ă©tapes :
receivers:
filelog/containers:
include: ["/var/lib/docker/containers/*/*.log"]
start_at: end
include_file_path: false
include_file_name: false
operators: []
Il faudra changer le rĂ©pertoire docker si vous nâutilisez pas la configuration par dĂ©faut.
à cette configuration, il faut ajouter des opérateurs qui vont transformer les logs afin de les rendre plus exploitables dans Loki.
operators:
- type: json_parser
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
On parse les logs json qui sortent de docker.
operators:
- type: filter
expr: '(attributes?.attrs?.tag ?? "empty") == "empty"'
- type: key_value_parser
parse_from: attributes["attrs"]["tag"]
parse_to: resource.container
on_error: drop
- type: move
from: resource.container.id
to: resource.container_id
- type: move
from: resource.container.name
to: resource.container_name
- type: move
from: resource.container.image
to: resource.container_image
On extrait les informations du conteneur, son nom et celui de son image. Si le conteneur nâest pas correctement tagger, on ne tient pas compte du log. Pour que cela fonctionne, il faudra tagger correctement les conteneurs que vous souhaitez observer.
operators:
- type: move
from: attributes.log
to: body
- type: move
from: attributes["attrs"]["application"]
to: resource.application
- type: json_parser
timestamp:
parse_from: attributes.timestamp
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
severity:
parse_from: attributes.level
mapping:
warn: WARN
error: ERROR
info: INFO
debug: DEBUG
- type: move
from: attributes.message
to: body
On déplace ensuite le champ attributes.log
qui a été parsé du json docker vers body
qui est le contenu du message. Puis on refait un parsing json, cette fois pour parser le json qui sort de Spring. On prĂ©cise que le timestamp de lâĂ©vĂšnement sera celui de Spring et on mappe les niveaux de log en majuscule pour uniformiser.
Enfin, on déplace le message issu du log de Spring dans le champ body
pour en faire le nouveau contenu du log.
operators:
- type: remove
field: attributes.time
- type: remove
field: attributes.stream
- type: remove
field: attributes["timestamp"]
- type: remove
field: attributes["level"]
- type: remove
field: attributes["attrs"]
Pour finir, on fait le mĂ©nage dans les champs que lâon ne souhaite pas conserver.
Configuration de lâexporter
Une fois les logs reçus et traité, il faut les renvoyer à Loki. Cela se fait via un exporter loki .
exporters:
loki:
endpoint: 'http://loki:3100/loki/api/v1/push'
Configuration du pipeline
Enfin, on met toutes ces configurations bout Ă bout dans le pipeline de logs
Open Telemetry
service:
pipelines:
logs:
receivers: [filelog/containers]
processors: [attributes, batch]
exporters: [logging, loki]
Service compose application
CotĂ© Open Telemetry la configuration du service ne change pas. NĂ©anmoins, pour la configuration du service de notre application, il va ĂȘtre nĂ©cessaire de tagger correctement le conteneur.
|
|
Le tag dans les options de logging du service docker compose va permettre de récupérer les informations du conteneur lors du parsing fait pas Open Telemetry. On ajoute aussi un label pour indiquer le nom de notre application, cela permettra de distinguer plusieurs instances si le cas se présente.
Comme pour les configurations des articles précédents, le mode verbose
dâOpen Telemetry peut sâavĂ©rer trĂšs utile pour comprendre ce quâil se passe dans le pipeline de transformation.
Cela se fait grĂące au Logging Exporter .
exporters:
logging:
verbosity: detailed
Lâensemble des fichiers de configuration modifiĂ©s sont disponibles sur github .
Conclusion
Ă ce stade, les mĂ©triques et les logs de lâapplication Spring sont rĂ©coltĂ©s, traitĂ©s et stockĂ©s dans les serveurs Prometheus et Loki. Il ne reste plus quâĂ mettre en place un Grafana pour visualiser tout ça. Câest ce que nous verrons dans le prochain article.