Aller au contenu principal

Grafana Loki


1 - Présentation

Loki = Prometheus pour les logs. Système de logs horizontalement scalable, inspiré de Prometheus.


2 - Différences avec ELK

AspectLokiElasticsearch
IndexationLabels uniquementFull-text
CoûtTrès basÉlevé
ComplexitéSimpleComplexe
QueriesLogQLQuery DSL
ScalabilitéNativeConfiguration requise

Philosophie Loki : Ne pas indexer le contenu des logs, seulement les labels.


3 - Installation

3.1 Docker Compose

# docker-compose.yml
version: '3.8'
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
- loki_data:/loki
command: -config.file=/etc/loki/local-config.yaml

promtail:
image: grafana/promtail:2.9.0
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
command: -config.file=/etc/promtail/config.yml

volumes:
loki_data:

3.2 Configuration Loki

# loki-config.yml
auth_enabled: false

server:
http_listen_port: 3100

common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory

schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h

limits_config:
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
max_streams_per_user: 10000
max_entries_limit_per_query: 5000

storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/index_cache
cache_ttl: 24h

compactor:
working_directory: /loki/compactor
shared_store: filesystem
retention_enabled: true
retention_delete_delay: 2h

ruler:
alertmanager_url: http://alertmanager:9093

3.3 Kubernetes (Helm)

helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
--namespace monitoring \
--set promtail.enabled=true \
--set grafana.enabled=true

4 - Promtail

4.1 Configuration

# promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0

positions:
filename: /tmp/positions.yaml

clients:
- url: http://loki:3100/loki/api/v1/push

scrape_configs:
# Logs fichiers
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*log

# Logs Docker
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
- source_labels: ['__meta_docker_container_log_stream']
target_label: 'logstream'

# Logs Kubernetes
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod

4.2 Pipeline stages

scrape_configs:
- job_name: app-logs
static_configs:
- targets: [localhost]
labels:
job: app
__path__: /var/log/app/*.log
pipeline_stages:
# Parse JSON
- json:
expressions:
level: level
message: message
timestamp: ts

# Extraire des labels
- labels:
level:

# Parser le timestamp
- timestamp:
source: timestamp
format: RFC3339Nano

# Filtrer
- match:
selector: '{level="debug"}'
action: drop

# Regex
- regex:
expression: 'user=(?P<user>\w+)'

# Output final
- output:
source: message

5 - LogQL

5.1 Sélecteurs

// Sélecteur simple
{job="app"}

// Avec plusieurs labels
{job="app", level="error"}

// Regex
{job=~"app.*", level!="debug"}

// Combinaisons
{namespace="production", app=~"api|web"}

5.2 Filtres de ligne

// Contient
{job="app"} |= "error"

// Ne contient pas
{job="app"} != "debug"

// Regex
{job="app"} |~ "user=\\d+"

// Chaînage
{job="app"} |= "error" != "expected" |~ "connection"

5.3 Parser

// JSON
{job="app"} | json

// Logfmt
{job="app"} | logfmt

// Regex
{job="app"} | regexp `user=(?P<user>\w+)`

// Pattern
{job="app"} | pattern `<ip> - - <_> "<method> <path> <_>" <status>`

// Accéder aux champs parsés
{job="app"} | json | level="error"

5.4 Fonctions d'agrégation

// Count logs
count_over_time({job="app"}[5m])

// Rate (logs par seconde)
rate({job="app"}[1m])

// Bytes rate
bytes_rate({job="app"}[1m])

// Sum par label
sum(rate({job="app"}[5m])) by (level)

// Top K
topk(5, sum(rate({job="app"}[5m])) by (path))

// Quantile (estimation)
quantile_over_time(0.99, {job="app"} | json | unwrap latency [5m])

5.5 Exemples pratiques

// Taux d'erreurs
sum(rate({job="app", level="error"}[5m]))
/
sum(rate({job="app"}[5m])) * 100

// Latence P99
quantile_over_time(0.99,
{job="app"} | json | unwrap response_time [5m]
) by (endpoint)

// Top endpoints avec erreurs
topk(10,
sum(rate({job="app", level="error"} | json [1h])) by (endpoint)
)

// Logs avec latence > 1s
{job="app"}
| json
| latency > 1000
| line_format "{{.endpoint}} - {{.latency}}ms"

6 - Alerting

6.1 Ruler Configuration

# rules.yml
groups:
- name: app-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate({job="app", level="error"}[5m])) > 10
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }} errors/s"

- alert: NoLogs
expr: |
absent(rate({job="app"}[5m]))
for: 5m
labels:
severity: warning
annotations:
summary: "No logs from app"

7 - Grafana Integration

7.1 Data Source

# provisioning/datasources/loki.yml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
jsonData:
maxLines: 1000
derivedFields:
- name: TraceID
matcherRegex: "trace_id=(\\w+)"
url: "$${__value.raw}"
datasourceUid: tempo

7.2 Explore & Dashboard

// Panel avec logs
{
"type": "logs",
"targets": [
{
"expr": "{namespace=\"production\", app=\"api\"} |= \"error\"",
"refId": "A"
}
],
"options": {
"showTime": true,
"showLabels": true,
"wrapLogMessage": true
}
}

8 - Performance Tips

TipDescription
Labels cardinalitéLimiter le nombre de valeurs uniques
Éviter labels dynamiquesPas de timestamp/UUID comme label
Utiliser filtresFiltrer avant de parser
Chunk sizeAjuster selon le volume
RetentionConfigurer selon besoins
# Bon usage des labels
labels:
environment: production # Peu de valeurs
service: api-gateway # Peu de valeurs
# ❌ Éviter: request_id, user_id, timestamp

# Mauvais
labels:
request_id: abc123 # Trop de valeurs uniques

Résumé

Dans ce chapitre, nous avons appris :

  • L'architecture de Loki
  • La configuration de Promtail
  • Le langage LogQL
  • L'alerting avec Ruler
  • L'intégration Grafana
  • Les bonnes pratiques de performance

Prochaine étape

Dans le prochain chapitre, nous verrons Alerting.

→ Chapitre suivant : Alerting


← Retour à la table des matières