# Fichier: python_cheats/cheatsheets/ELK.txt
# Cheatsheet ELK Stack (Elasticsearch, Logstash, Kibana) - Guide Complet pour Débutants


[OK] INTRODUCTION À ELK STACK

# === QU'EST-CE QUE ELK ? ===

# ELK Stack = Elasticsearch + Logstash + Kibana (+ Beats)
# C'est une suite d'outils pour gérer, analyser et visualiser des données en temps réel

# Imaginez que vous avez une boutique en ligne:
# - Vos serveurs génèrent des millions de lignes de logs chaque jour
# - Comment trouver rapidement pourquoi un utilisateur a eu une erreur hier à 14h?
# - Comment voir combien de personnes visitent votre site en temps réel?
# - Comment détecter si quelqu'un essaie de pirater votre site?
# 
# ELK Stack résout tous ces problèmes!

# === LES 4 COMPOSANTS EXPLIQUÉS ===

# 1. ELASTICSEARCH (Le cerveau - Stockage + Recherche)
# --------------------------------------------------
# C'est une base de données NoSQL ultra-rapide spécialisée dans la recherche
# Imagine Google Search mais pour tes propres données
# 
# Exemple concret:
# - Tu as 10 millions de logs
# - Tu cherches "erreur 500 utilisateur Jean"
# - Elasticsearch trouve la réponse en millisecondes
# 
# Stockage sous forme de "documents JSON":
# {
#   "timestamp": "2024-01-15 14:30:00",
#   "level": "ERROR",
#   "message": "Database connection failed",
#   "user": "jean@example.com"
# }
#
# Pourquoi c'est puissant?
# - Recherche full-text (comme Google)
# - Agrégations (stats, moyennes, comptages)
# - Distribué (peut gérer des pétaoctets de données)
# - Temps réel (données disponibles en ~1 seconde)

# 2. LOGSTASH (Le transformateur - Collecte + Transformation)
# -----------------------------------------------------------
# C'est un pipeline qui collecte, transforme et envoie les données
# Comme un trieur de courrier qui lit, organise et redirige les lettres
# 
# Pipeline en 3 étapes:
# INPUT (Entrée)  -> FILTER (Transformation) -> OUTPUT (Sortie)
#
# Exemple concret:
# INPUT: Logs bruts Nginx
# 192.168.1.1 - - [15/Jan/2024:10:30:45] "GET /products HTTP/1.1" 200 1234
#
# FILTER: Parser et enrichir
# - Extraire: IP, date, méthode HTTP, URL, code réponse
# - Ajouter: géolocalisation de l'IP (pays, ville)
# - Convertir: types de données (string -> integer)
#
# OUTPUT: Document structuré vers Elasticsearch
# {
#   "client_ip": "192.168.1.1",
#   "timestamp": "2024-01-15T10:30:45Z",
#   "method": "GET",
#   "url": "/products",
#   "response_code": 200,
#   "bytes": 1234,
#   "geoip": {
#     "country": "France",
#     "city": "Paris"
#   }
# }
#
# Pourquoi c'est utile?
# - Nettoie et structure les données
# - Enrichit avec contexte (géolocalisation, etc.)
# - Supporte 200+ sources (fichiers, bases de données, APIs)
# - Filtre et transforme à la volée

# 3. KIBANA (L'interface - Visualisation + Exploration)
# -----------------------------------------------------
# C'est l'interface graphique pour explorer et visualiser les données
# Comme un tableau de bord de voiture qui affiche tout ce qui se passe
#
# Fonctionnalités principales:
#
# a) DISCOVER (Exploration)
#    - Chercher dans vos données comme Google
#    - Filtrer par date, niveau d'erreur, utilisateur, etc.
#    - Voir les logs en temps réel
#
# b) VISUALIZATIONS (Graphiques)
#    - Graphiques ligne: évolution dans le temps
#    - Camemberts: répartition (80% success, 20% errors)
#    - Cartes: visualiser géographiquement
#    - Tableaux: top 10 des erreurs
#
# c) DASHBOARDS (Tableaux de bord)
#    - Combiner plusieurs visualisations
#    - Vue d'ensemble en un coup d'œil
#    - Rafraîchissement automatique
#
# d) ALERTING (Alertes)
#    - "Préviens-moi si erreurs > 100/minute"
#    - Envoie email, Slack, SMS
#
# Exemple concret:
# Dashboard e-commerce avec:
# - Nombre de visiteurs en temps réel
# - Revenus du jour (graphique ligne)
# - Top 10 produits vendus (tableau)
# - Carte des ventes par pays
# - Alertes si site down

# 4. BEATS (Les collecteurs - Agents légers)
# ------------------------------------------
# Ce sont des petits programmes qui collectent des données spécifiques
# Comme des capteurs que tu installes partout pour surveiller
#
# Types de Beats:
#
# - FILEBEAT: Collecte logs de fichiers
#   Exemple: Surveille /var/log/nginx/*.log
#   Envoie chaque nouvelle ligne à Logstash/Elasticsearch
#
# - METRICBEAT: Collecte métriques système
#   Exemple: CPU, RAM, disque, réseau toutes les 10 secondes
#
# - PACKETBEAT: Analyse trafic réseau
#   Exemple: Capture requêtes HTTP, requêtes SQL
#
# - HEARTBEAT: Vérifie disponibilité
#   Exemple: Ping ton site toutes les minutes
#
# - AUDITBEAT: Audit de sécurité
#   Exemple: Qui s'est connecté? Quels fichiers modifiés?
#
# - WINLOGBEAT: Logs Windows
#   Exemple: Événements Windows (login, erreurs)
#
# Pourquoi des Beats au lieu de Logstash partout?
# - Plus légers (consomment moins de ressources)
# - Plus simples à configurer
# - Conçus pour tourner sur chaque serveur
# - Logstash reste pour transformations complexes

# === COMMENT ÇA MARCHE ENSEMBLE? ===

# Architecture typique:
#
# [Serveur Web] -> [Filebeat] ─┐
# [Serveur API] -> [Filebeat] ─┤
# [Base données] -> [Filebeat] ─┼-> [Logstash] -> [Elasticsearch] <- [Kibana]
# [Container 1] -> [Filebeat] ─┤                                      ^
# [Container 2] -> [Filebeat] ─┘                                      |
#                                                              [Navigateur Web]
#
# Flux de données:
# 1. Filebeat lit les logs sur chaque serveur
# 2. Envoie à Logstash pour transformation
# 3. Logstash parse, enrichit et envoie à Elasticsearch
# 4. Elasticsearch indexe et stocke
# 5. Tu explores/visualises dans Kibana depuis ton navigateur
#
# Architecture simplifiée (sans Logstash):
# [Serveurs] -> [Filebeat] -> [Elasticsearch] <- [Kibana]
# Filebeat peut envoyer directement à Elasticsearch (plus simple mais moins flexible)

# === CAS D'USAGE CONCRETS ===

# 1. CENTRALISATION DE LOGS
# Problème: Tu as 50 serveurs, chacun génère des logs
# Solution: Filebeat sur chaque serveur -> Tout centralisé dans ELK
# Bénéfice: Chercher dans tous les logs depuis un seul endroit
#
# Exemple: "Trouve tous les logs contenant 'user123' hier entre 14h et 15h"
# Sans ELK: Se connecter à 50 serveurs, grep dans chaque fichier (30 min)
# Avec ELK: Taper la recherche dans Kibana (5 secondes)

# 2. MONITORING D'INFRASTRUCTURE
# Problème: Comment savoir si un serveur va crasher?
# Solution: Metricbeat collecte CPU/RAM/Disque -> Alertes si seuils dépassés
# Bénéfice: Être prévenu AVANT que ça plante
#
# Exemple: Dashboard temps réel avec:
# - CPU de tous les serveurs (heatmap)
# - Mémoire disponible (jauge)
# - Espace disque (barres)
# - Alerte si CPU > 90% pendant 5 min

# 3. ANALYSE DE SÉCURITÉ (SIEM)
# Problème: Comment détecter une attaque?
# Solution: Collecter logs firewall, SSH, web -> Détecter patterns suspects
# Bénéfice: Bloquer attaques rapidement
#
# Exemple: Détection brute-force SSH
# - Filebeat collecte logs auth.log
# - Logstash détecte 20 failed logins en 1 minute
# - Alerte envoyée + IP ajoutée au blacklist automatiquement

# 4. ANALYSE MÉTIER E-COMMERCE
# Problème: Comprendre comportement utilisateurs
# Solution: Logger tous les événements -> Analyser dans Kibana
# Bénéfice: Optimiser conversions, détecter bugs
#
# Exemple: Dashboard temps réel
# - 1,234 visiteurs actuellement sur le site
# - 45 personnes en train d'acheter
# - Temps moyen checkout: 3m 24s
# - Taux abandon panier: 23% (en baisse!)
# - Alerte si temps checkout > 5 min (problème technique)

# 5. RECHERCHE FULL-TEXT DANS APPLICATION
# Problème: Ajouter fonction recherche dans ton app
# Solution: Indexer contenu dans Elasticsearch
# Bénéfice: Recherche ultra-rapide type Google
#
# Exemple: Site de documentation
# - Tous les articles indexés dans Elasticsearch
# - Recherche "installer docker ubuntu" 
# - Trouve articles pertinents en 20ms
# - Avec suggestions, highlighting, facettes

# === POURQUOI UTILISER ELK? ===

# AVANTAGES:
# [OK] Open source (gratuit pour fonctionnalités de base)
# [OK] Scalable (de 1 Go à plusieurs pétaoctets)
# [OK] Temps réel (données disponibles en ~1 seconde)
# [OK] Flexible (supporte tout type de données)
# [OK] Communauté énorme (beaucoup de ressources)
# [OK] Ecosystem riche (plugins, intégrations)
# [OK] Interface intuitive (Kibana)
# [OK] Recherche puissante (type Google)

# INCONVÉNIENTS:
# [X] Courbe d'apprentissage (complexe au début)
# [X] Consomme des ressources (RAM, CPU)
# [X] Nécessite maintenance (backups, mises à jour)
# [X] Certaines features payantes (Machine Learning, Alerting avancé)
# [X] Peut être overkill pour petits projets

# === ALTERNATIVES À ELK ===

# SPLUNK:
# + Plus simple à utiliser
# + Support entreprise excellent
# - Très cher (commence à $5k/an)
# - Propriétaire (pas open source)

# GRAYLOG:
# + Plus léger que ELK
# + Open source
# - Moins de features
# - Communauté plus petite

# LOKI + GRAFANA:
# + Très léger
# + Gratuit
# + Bien pour logs simples
# - Pas de recherche full-text
# - Moins puissant pour agrégations

# DATADOG / NEW RELIC / SUMO LOGIC:
# + SaaS (pas besoin gérer infrastructure)
# + Setup très rapide
# - Coûteux à l'usage
# - Vendor lock-in

# === QUAND UTILISER ELK? ===

# PARFAIT POUR:
# [OK] Centraliser logs de plusieurs serveurs
# [OK] Monitoring infrastructure
# [OK] Analyse de sécurité
# [OK] Recherche full-text
# [OK] Dashboards temps réel
# [OK] Analyse métier avec beaucoup de données
# [OK] Tu as compétences techniques

# PAS ADAPTÉ SI:
# [X] Petit projet (1 serveur, peu de logs)
# [X] Pas de compétences techniques dans l'équipe
# [X] Budget très limité (infrastructure)
# [X] Besoin juste de logs simples
# -> Utilise plutôt: Loki, logs natifs cloud (CloudWatch), ou outils SaaS

# === PRÉREQUIS POUR DÉMARRER ===

# CONNAISSANCES:
# - Linux de base (commandes shell)
# - Notion de JSON
# - Comprendre HTTP/REST
# - Regex (expressions régulières) - utile pour parsing
# - Docker (recommandé pour tests)

# INFRASTRUCTURE MINIMALE:
# Pour débuter (développement):
# - 1 serveur avec 8 GB RAM minimum
# - 20 GB espace disque
# - Linux Ubuntu/Debian/CentOS

# Pour production (petit):
# - 3 serveurs (HA - High Availability)
# - 16 GB RAM par serveur minimum
# - SSD pour Elasticsearch (performances)
# - 100+ GB espace disque

# Pour production (moyen):
# - 5-10 serveurs
# - 32 GB RAM par serveur
# - SSD rapides
# - Load balancer

# === COMBIEN ÇA COÛTE? ===

# ELASTICSEARCH OPEN SOURCE (Free):
# [OK] Toutes fonctionnalités core
# [OK] Elasticsearch + Logstash + Kibana + Beats
# [OK] Recherche, agrégations, visualisations
# [X] Pas de Machine Learning
# [X] Pas d'alerting avancé
# [X] Pas de sécurité avancée (LDAP, SAML)

# ELASTIC BASIC (Free):
# [OK] Tout Open Source +
# [OK] Sécurité de base
# [OK] Monitoring Stack
# [X] Pas de ML
# [X] Pas d'alerting

# ELASTIC GOLD ($$):
# [OK] Tout Basic +
# [OK] Alerting
# [OK] Machine Learning
# [OK] Graph analytics
# Prix: ~$6,000/an par nœud

# ELASTIC PLATINUM ($$):
# [OK] Tout Gold +
# [OK] Support entreprise
# [OK] SAML, LDAP auth
# Prix: Contactez Elastic

# ELASTIC CLOUD (SaaS):
# Prix: À partir de $45/mois (petit)
# Jusqu'à $1000+/mois (moyen)

# COÛT INFRASTRUCTURE (Hors licences):
# Petit (dev): $50-200/mois (AWS/Azure/GCP)
# Moyen (prod): $500-2000/mois
# Large (entreprise): $5000+/mois

# === COMBIEN DE TEMPS POUR APPRENDRE? ===

# NIVEAU DÉBUTANT (2-3 semaines):
# - Installer ELK Stack
# - Envoyer logs basiques
# - Créer dashboards simples
# - Recherches basiques

# NIVEAU INTERMÉDIAIRE (2-3 mois):
# - Pipelines Logstash complexes
# - Grok patterns personnalisés
# - Optimisation performances
# - ILM (Index Lifecycle Management)
# - Sécurité basique

# NIVEAU AVANCÉ (6-12 mois):
# - Architecture cluster multi-nœuds
# - Tuning performances poussé
# - Machine Learning
# - Scripting avancé
# - Troubleshooting expert

# CERTIFICATION:
# - Elastic Certified Engineer
# - Elastic Certified Analyst
# Coût: ~$400 par exam


[OK] INSTALLATION - ELASTICSEARCH

# === QU'EST-CE QU'ON INSTALLE? ===
# Elasticsearch = Base de données NoSQL + Moteur de recherche
# C'est le cœur du système qui stocke et recherche dans les données
# Version: 8.x (dernière stable au moment de l'écriture)
# Port par défaut: 9200 (HTTP API) et 9300 (communication entre nœuds)

# === CHOIX DE LA MÉTHODE D'INSTALLATION ===

# 1. PACKAGE MANAGER (apt/yum) - RECOMMANDÉ POUR PRODUCTION
#    Avantages: 
#    - Démarrage automatique au boot
#    - Gestion des mises à jour facile
#    - Configuration système propre
#    Inconvénients:
#    - Nécessite droits root
#    - Plus de configuration initiale

# 2. ARCHIVE TAR.GZ - BON POUR TEST/DEV
#    Avantages:
#    - Pas besoin de droits root
#    - Portable
#    - Plusieurs versions possible sur une machine
#    Inconvénients:
#    - Pas de démarrage automatique
#    - Gestion manuelle

# 3. DOCKER - IDÉAL POUR DÉVELOPPEMENT
#    Avantages:
#    - Setup ultra rapide (1 commande)
#    - Isolation complète
#    - Facile à détruire/recréer
#    Inconvénients:
#    - Nécessite Docker
#    - Moins performant que natif
#    - Pas idéal pour production (possible mais complexe)

# === LINUX (Ubuntu/Debian) ===

# ÉTAPE 1: Importer la clé GPG Elastic
# Pourquoi? Vérifier que les packages viennent bien d'Elastic (sécurité)
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg

# Explication:
# - wget: télécharge la clé
# - gpg --dearmor: convertit la clé au format binaire
# - /usr/share/keyrings/: dossier standard pour clés GPG

# ÉTAPE 2: Ajouter le repository Elastic
# Cela dit à apt où télécharger Elasticsearch
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list

# Explication:
# - deb: type de package Debian
# - signed-by: quelle clé utiliser pour vérifier
# - 8.x: version majeure (8.0, 8.1, 8.2, etc.)
# - stable main: canal de distribution

# ÉTAPE 3: Mettre à jour la liste des packages
sudo apt-get update
# Cela télécharge la liste des versions disponibles

# ÉTAPE 4: Installer Elasticsearch
sudo apt-get install elasticsearch
# Télécharge et installe Elasticsearch + dépendances

# [ATTENTION] IMPORTANT: Noter le mot de passe généré automatiquement!
# À la fin de l'installation, tu verras:
# "The generated password for the elastic built-in superuser is : XYZ123..."
# COPIE CE MOT DE PASSE! Tu en auras besoin pour Kibana

# ÉTAPE 5: Recharger systemd
# systemd = gestionnaire de services Linux
sudo systemctl daemon-reload
# Cela recharge la configuration pour reconnaître le nouveau service

# ÉTAPE 6: Activer démarrage automatique
sudo systemctl enable elasticsearch.service
# "enable" = démarre automatiquement au boot de la machine
# Si tu redémarres le serveur, Elasticsearch redémarre tout seul

# ÉTAPE 7: Démarrer Elasticsearch
sudo systemctl start elasticsearch.service
# Démarre le service maintenant

# ÉTAPE 8: Vérifier que c'est démarré
sudo systemctl status elasticsearch.service
# Tu devrais voir "active (running)" en vert

# Sortie attendue:
# [BLACK_CIRCLE] elasticsearch.service - Elasticsearch
#    Loaded: loaded (/lib/systemd/system/elasticsearch.service)
#    Active: active (running) since Mon 2024-01-15 10:30:00 UTC
#    ...

# ÉTAPE 9: Vérifier les logs si problème
sudo journalctl -u elasticsearch.service -f
# -f: suit les logs en temps réel (comme tail -f)
# Ctrl+C pour quitter

# Logs également disponibles dans:
tail -f /var/log/elasticsearch/elasticsearch.log

# ÉTAPE 10: Tester la connexion
curl -X GET "localhost:9200"
# Si ça marche, tu verras une réponse JSON avec version, cluster name, etc.

# Avec sécurité activée (v8+), utilise:
curl -X GET "https://localhost:9200" -u elastic:ton_mot_de_passe -k
# -u: username:password
# -k: ignore erreur SSL (certificat auto-signé)

# === LINUX (RHEL/CentOS/Fedora) ===

# Similaire à Ubuntu mais avec yum/dnf au lieu de apt

# ÉTAPE 1: Importer clé GPG
sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

# ÉTAPE 2: Créer fichier repository
sudo tee /etc/yum.repos.d/elasticsearch.repo <<EOF
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md
EOF

# Explication du fichier:
# - name: nom descriptif
# - baseurl: où télécharger les packages
# - gpgcheck=1: vérifier signature GPG
# - enabled=0: ne pas activer par défaut (on active manuellement)

# ÉTAPE 3: Installer
sudo yum install --enablerepo=elasticsearch elasticsearch
# --enablerepo: active temporairement ce repo

# Pour Fedora (dnf):
sudo dnf install --enablerepo=elasticsearch elasticsearch

# ÉTAPE 4-8: Identiques à Ubuntu
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch.service
sudo systemctl start elasticsearch.service
sudo systemctl status elasticsearch.service

# === MAC (Homebrew) ===

# PRÉREQUIS: Avoir Homebrew installé
# Si pas installé: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# ÉTAPE 1: Ajouter le tap Elastic
brew tap elastic/tap
# "tap" = ajouter un repository tiers à Homebrew

# ÉTAPE 2: Installer Elasticsearch
brew install elastic/tap/elasticsearch-full
# "full" = toutes les features (incluant X-Pack)

# ÉTAPE 3: Démarrer
brew services start elastic/tap/elasticsearch-full
# Démarre et configure démarrage automatique

# Ou démarrer manuellement (pour tests):
elasticsearch
# Ctrl+C pour arrêter

# ÉTAPE 4: Vérifier
curl -X GET "localhost:9200"

# Fichiers de configuration sur Mac:
# Config: /usr/local/etc/elasticsearch/elasticsearch.yml
# Data: /usr/local/var/lib/elasticsearch/
# Logs: /usr/local/var/log/elasticsearch/

# === WINDOWS ===

# MÉTHODE 1: Fichier ZIP (Recommandé)

# ÉTAPE 1: Télécharger
# Aller sur: https://www.elastic.co/downloads/elasticsearch
# Télécharger: elasticsearch-8.x.x-windows-x86_64.zip

# ÉTAPE 2: Extraire
# Clic droit > Extraire tout
# Par exemple dans: C:\elasticsearch

# ÉTAPE 3: Ouvrir PowerShell ou CMD en Administrateur
# Clic droit sur PowerShell > "Exécuter en tant qu'administrateur"

# ÉTAPE 4: Aller dans le dossier
cd C:\elasticsearch\elasticsearch-8.x.x

# ÉTAPE 5: Démarrer
bin\elasticsearch.bat

# La première fois, ça va:
# - Générer certificats SSL
# - Créer mot de passe elastic
# - Démarrer le service

# [ATTENTION] NOTER LE MOT DE PASSE AFFICHÉ!

# ÉTAPE 6: Tester (nouveau terminal)
curl http://localhost:9200

# MÉTHODE 2: Installer comme service Windows

# Après étapes 1-4:
bin\elasticsearch-service.bat install
# Installe comme service Windows

bin\elasticsearch-service.bat start
# Démarre le service

# Gérer via services.msc (gestionnaire services Windows)

# === DOCKER ===

# POURQUOI DOCKER?
# - Setup en 30 secondes
# - Environnement isolé
# - Facile à détruire/recréer
# - Idéal pour apprendre et tester

# PRÉREQUIS: Docker installé
# Installer Docker: https://docs.docker.com/get-docker/

# VERSION SIMPLE (1 seul nœud, développement)

docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  docker.elastic.co/elasticsearch/elasticsearch:8.11.0

# Explication ligne par ligne:
# docker run: lance un container
# -d: mode détaché (en arrière-plan)
# --name elasticsearch: nom du container (pour le retrouver)
# -p 9200:9200: map port 9200 container -> port 9200 hôte
# -p 9300:9300: port communication entre nœuds (pas utilisé ici mais bon à avoir)
# -e "discovery.type=single-node": dit à ES qu'il est seul (pas de cluster)
# -e "xpack.security.enabled=false": désactive sécurité (DEV SEULEMENT!)
# docker.elastic.co/...:8.11.0: image Docker officielle version 8.11.0

# Vérifier que ça tourne:
docker ps
# Tu devrais voir le container "elasticsearch"

# Voir les logs:
docker logs -f elasticsearch
# -f: suit les logs en temps réel

# Tester:
curl http://localhost:9200

# Arrêter:
docker stop elasticsearch

# Redémarrer:
docker start elasticsearch

# Supprimer complètement:
docker rm -f elasticsearch

# VERSION AVEC PERSISTENCE (Les données survivent au redémarrage)

docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -v elasticsearch-data:/usr/share/elasticsearch/data \
  docker.elastic.co/elasticsearch/elasticsearch:8.11.0

# -v elasticsearch-data:/usr/share/elasticsearch/data
# Crée un volume Docker "elasticsearch-data" monté dans le container
# Les données sont stockées sur l'hôte, pas dans le container
# Si tu détruis le container, les données restent!

# Voir les volumes:
docker volume ls

# Supprimer volume ([ATTENTION] PERD LES DONNÉES):
docker volume rm elasticsearch-data

# === VÉRIFICATION INSTALLATION ===

# TEST 1: Cluster Health
curl -X GET "localhost:9200/_cluster/health?pretty"

# Réponse attendue:
{
  "cluster_name" : "elasticsearch",
  "status" : "green",        # <- IMPORTANT: doit être "green" ou "yellow"
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  ...
}

# Status signification:
# - GREEN: Tout va bien, tous les shards sont alloués
# - YELLOW: Données accessibles mais replicas manquants (normal avec 1 nœud)
# - RED: Certaines données primaires manquantes (PROBLÈME!)

# TEST 2: Info version
curl -X GET "localhost:9200"

# Réponse:
{
  "name" : "node-1",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "abc123...",
  "version" : {
    "number" : "8.11.0",      # <- Version installée
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "...",
    "build_date" : "2024-01-15T10:00:00.000Z",
    "lucene_version" : "9.8.0"
  },
  "tagline" : "You Know, for Search"
}

# TEST 3: Lister les nœuds
curl -X GET "localhost:9200/_cat/nodes?v"

# Sortie:
# ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
# 127.0.0.1 45          60          5   0.10    0.15    0.20     cdfhilmrstw *      node-1

# Explication colonnes:
# - heap.percent: % mémoire JVM utilisée (alerte si > 85%)
# - ram.percent: % RAM totale utilisée
# - cpu: % CPU
# - load_*: charge système (contexte Unix)
# - node.role: rôles du nœud (c=cold, d=data, f=frozen, etc.)
# - master: * si c'est le master actuel
# - name: nom du nœud

# === DÉPANNAGE INSTALLATION ===

# PROBLÈME 1: "Connection refused" sur port 9200
# Causes possibles:
# 1. Elasticsearch pas démarré
sudo systemctl status elasticsearch
sudo systemctl start elasticsearch

# 2. Écoute seulement sur localhost
# Vérifier dans /etc/elasticsearch/elasticsearch.yml:
network.host: 0.0.0.0  # Écoute sur toutes interfaces

# 3. Firewall bloque
sudo ufw allow 9200/tcp  # Ubuntu
sudo firewall-cmd --permanent --add-port=9200/tcp  # CentOS
sudo firewall-cmd --reload

# PROBLÈME 2: Elasticsearch ne démarre pas
# Regarder les logs:
sudo journalctl -u elasticsearch -n 100
# ou
tail -100 /var/log/elasticsearch/elasticsearch.log

# Erreur commune: "max virtual memory areas vm.max_map_count [65530] is too low"
# Solution:
sudo sysctl -w vm.max_map_count=262144
# Permanent:
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

# PROBLÈME 3: Out of Memory
# Elasticsearch prend 50% RAM par défaut
# Ajuster dans /etc/elasticsearch/jvm.options:
-Xms2g  # Mémoire minimum (ex: 2GB)
-Xmx2g  # Mémoire maximum (doit être = min)
# Règle: Ne jamais dépasser 32GB (perd compression pointeurs)
# Redémarrer après modif:
sudo systemctl restart elasticsearch

# PROBLÈME 4: Permission denied
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/log/elasticsearch

# === PROCHAINES ÉTAPES ===
# Maintenant qu'Elasticsearch est installé:
# 1. Configurer elasticsearch.yml (voir section Configuration)
# 2. Configurer sécurité si version 8+ (voir section Sécurité)
# 3. Installer Kibana pour interface graphique
# 4. Installer Logstash ou Filebeat pour envoyer des données


[OK] INSTALLATION - LOGSTASH

# === LINUX (Ubuntu/Debian) ===

# Si repo Elastic déjà configuré (voir Elasticsearch)
sudo apt-get install logstash

# Activer au démarrage
sudo systemctl enable logstash.service

# === LINUX (RHEL/CentOS/Fedora) ===

sudo yum install --enablerepo=elasticsearch logstash

# === MAC (Homebrew) ===

brew install elastic/tap/logstash-full

# === WINDOWS ===

# Télécharger ZIP depuis https://www.elastic.co/downloads/logstash
# Extraire et exécuter bin\logstash.bat

# === DOCKER ===

docker run -d \
  --name logstash \
  -p 5000:5000 -p 5044:5044 -p 9600:9600 \
  -v ~/logstash/pipeline:/usr/share/logstash/pipeline \
  docker.elastic.co/logstash/logstash:8.11.0


[OK] INSTALLATION - KIBANA

# === LINUX (Ubuntu/Debian) ===

sudo apt-get install kibana

# Activer au démarrage
sudo systemctl enable kibana.service
sudo systemctl start kibana.service

# === LINUX (RHEL/CentOS/Fedora) ===

sudo yum install --enablerepo=elasticsearch kibana

# === MAC (Homebrew) ===

brew install elastic/tap/kibana-full
brew services start elastic/tap/kibana-full

# === WINDOWS ===

# Télécharger ZIP depuis https://www.elastic.co/downloads/kibana
# Extraire et exécuter bin\kibana.bat

# === DOCKER ===

docker run -d \
  --name kibana \
  -p 5601:5601 \
  -e "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" \
  docker.elastic.co/kibana/kibana:8.11.0

# Accéder à Kibana: http://localhost:5601


[OK] INSTALLATION - BEATS (FILEBEAT)

# Filebeat = Agent léger pour collecter et envoyer des logs

# === LINUX (Ubuntu/Debian) ===

sudo apt-get install filebeat

# === LINUX (RHEL/CentOS/Fedora) ===

sudo yum install --enablerepo=elasticsearch filebeat

# === MAC (Homebrew) ===

brew install elastic/tap/filebeat-full

# === WINDOWS ===

# Télécharger ZIP depuis https://www.elastic.co/downloads/beats/filebeat
# Extraire et installer avec install-service-filebeat.ps1

# === DOCKER ===

docker run -d \
  --name filebeat \
  --user=root \
  -v ~/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker.elastic.co/beats/filebeat:8.11.0

# Autres Beats:
# - Metricbeat: Métriques système et services
# - Packetbeat: Données réseau
# - Heartbeat: Monitoring de disponibilité
# - Auditbeat: Audit de sécurité
# - Winlogbeat: Logs événements Windows


[OK] DOCKER-COMPOSE - STACK COMPLET

# === docker-compose.yml ===

version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
      - "9300:9300"
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    networks:
      - elk

  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    container_name: logstash
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
    ports:
      - "5000:5000"
      - "5044:5044"
      - "9600:9600"
    environment:
      - "LS_JAVA_OPTS=-Xms256m -Xmx256m"
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    networks:
      - elk
    depends_on:
      - elasticsearch

  filebeat:
    image: docker.elastic.co/beats/filebeat:8.11.0
    container_name: filebeat
    user: root
    volumes:
      - ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - elk
    depends_on:
      - elasticsearch
      - logstash

volumes:
  elasticsearch-data:
    driver: local

networks:
  elk:
    driver: bridge

# Lancer le stack
docker-compose up -d

# Arrêter le stack
docker-compose down

# Voir les logs
docker-compose logs -f

# Voir les logs d'un service spécifique
docker-compose logs -f elasticsearch


[OK] CONFIGURATION - ELASTICSEARCH

# === FICHIER PRINCIPAL: elasticsearch.yml ===

# LOCALISATION DU FICHIER:
# Linux (package): /etc/elasticsearch/elasticsearch.yml
# Mac (Homebrew): /usr/local/etc/elasticsearch/elasticsearch.yml
# Windows: config\elasticsearch.yml (dans dossier installation)
# Docker: Monter un volume avec ton fichier personnalisé

# STRUCTURE DU FICHIER:
# - Format YAML (indentation importante!)
# - Lignes avec # sont des commentaires
# - key: value (attention aux espaces)

# === CONFIGURATION DE BASE (DÉVELOPPEMENT) ===

# 1. NOM DU CLUSTER
cluster.name: my-cluster
# Explication:
# - Tous les nœuds avec le même nom forment un cluster
# - Important pour rejoindre plusieurs nœuds
# - Par défaut: "elasticsearch"
# - Conseil: Utilise un nom descriptif (ex: "prod-logs", "dev-cluster")

# 2. NOM DU NŒUD
node.name: node-1
# Explication:
# - Identifie ce nœud spécifiquement dans le cluster
# - Par défaut: hostname de la machine
# - Doit être unique dans le cluster
# - Apparaît dans les logs et Kibana

# 3. CHEMINS DES DONNÉES
path.data: /var/lib/elasticsearch
# Explication:
# - Où Elasticsearch stocke les index (les données!)
# - CRITIQUE: Doit avoir assez d'espace (pense aux backups aussi)
# - Sur SSD pour meilleures performances
# - Peut être un array de chemins: ["/disk1/data", "/disk2/data"]
# - [ATTENTION] Ne jamais partager ce dossier entre plusieurs nœuds!

# 4. CHEMIN DES LOGS
path.logs: /var/log/elasticsearch
# Explication:
# - Où sont écrits les logs d'Elasticsearch
# - Utile pour debug
# - Logs rotationnés automatiquement (pour pas remplir le disque)

# 5. ADRESSE RÉSEAU
network.host: 0.0.0.0
# Explication:
# - Sur quelle adresse IP écouter
# - 0.0.0.0 = toutes les interfaces (accessible de l'extérieur)
# - 127.0.0.1 = localhost seulement (sécurisé mais pas accessible réseau)
# - IP spécifique = écoute seulement sur cette IP
# - Par défaut: 127.0.0.1 (localhost)
# [ATTENTION] Si tu changes ça, ES passe en "mode production" avec checks stricts!

# 6. PORT HTTP
http.port: 9200
# Explication:
# - Port pour API REST (requêtes curl, Kibana, etc.)
# - Par défaut: 9200
# - Peut être une plage: 9200-9300 (prendra le premier libre)
# - C'est ce port que tu utilises pour curl http://localhost:9200

# 7. PORT TRANSPORT
transport.port: 9300
# Explication:
# - Port pour communication entre nœuds Elasticsearch
# - Utilisé pour clustering (quand plusieurs nœuds)
# - Par défaut: 9300
# - Doit être ouvert sur firewall si cluster multi-machines

# 8. MODE DÉCOUVERTE (NŒUD UNIQUE)
discovery.type: single-node
# Explication:
# - Pour développement avec 1 seul nœud
# - Désactive la découverte automatique d'autres nœuds
# - Pas pour production multi-nœuds!
# - Évite les warnings "master not discovered"

# === EXEMPLE COMPLET CONFIGURATION DÉVELOPPEMENT ===

# elasticsearch.yml - Configuration Dev
cluster.name: dev-cluster
node.name: dev-node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
http.port: 9200
transport.port: 9300
discovery.type: single-node
xpack.security.enabled: false  # Désactive sécurité pour dev

# Redémarrer après modifications:
sudo systemctl restart elasticsearch

# === CONFIGURATION PRODUCTION (CLUSTER MULTI-NŒUDS) ===

# NŒUD 1 (Master + Data)
cluster.name: production-cluster
node.name: node-1
node.roles: [ master, data ]  # Rôles du nœud
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 192.168.1.10
http.port: 9200
transport.port: 9300

# Découverte des autres nœuds
discovery.seed_hosts: ["192.168.1.10:9300", "192.168.1.11:9300", "192.168.1.12:9300"]
# Liste des nœuds à contacter pour former le cluster

# Nœuds éligibles master (pour élection)
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
# [ATTENTION] À configurer SEULEMENT au premier démarrage du cluster!

# NŒUD 2 (Master + Data)
cluster.name: production-cluster  # <- MÊME NOM!
node.name: node-2                 # <- NOM DIFFÉRENT!
node.roles: [ master, data ]
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 192.168.1.11       # <- IP DIFFÉRENTE!
http.port: 9200
transport.port: 9300
discovery.seed_hosts: ["192.168.1.10:9300", "192.168.1.11:9300", "192.168.1.12:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

# NŒUD 3 (similaire, changer node.name et network.host)

# === RÔLES DES NŒUDS (node.roles) ===

# MASTER:
node.roles: [ master ]
# - Gère le cluster (créer/supprimer index, allouer shards)
# - Léger en ressources
# - Au moins 3 nœuds master pour HA (High Availability)

# DATA:
node.roles: [ data ]
# - Stocke les données
# - Exécute les recherches et agrégations
# - Gourmand en CPU, RAM, disque

# DATA_HOT (données chaudes - fréquemment accédées):
node.roles: [ data_hot ]
# - Pour données récentes/actives
# - SSD rapide recommandé
# - Plus de RAM/CPU

# DATA_WARM (données tièdes - occasionnellement accédées):
node.roles: [ data_warm ]
# - Pour données moins récentes
# - Disque plus lent OK
# - Moins de ressources

# DATA_COLD (données froides - rarement accédées):
node.roles: [ data_cold ]
# - Pour archives
# - Disque lent/grand OK
# - Ressources minimales

# INGEST (traitement de données):
node.roles: [ ingest ]
# - Transforme données avant indexation
# - Comme Logstash mais intégré
# - Gourmand en CPU

# COORDINATING ONLY (load balancer):
node.roles: [ ]  # Liste vide!
# - Reçoit requêtes et les dispatche
# - Ne stocke rien
# - Comme un load balancer interne

# COMBINAISONS COURANTES:
node.roles: [ master, data ]           # Petit cluster
node.roles: [ master, data, ingest ]   # All-in-one
node.roles: [ data_hot, data_content ] # Nœud données chaudes

# === CONFIGURATION MÉMOIRE (jvm.options) ===

# Fichier: /etc/elasticsearch/jvm.options

# HEAP SIZE (Mémoire JVM)
-Xms4g  # Minimum heap
-Xmx4g  # Maximum heap
# Explication:
# - TOUJOURS mettre min = max (évite resize pendant exécution)
# - Règle générale: 50% de la RAM totale
# - JAMAIS plus de 32GB (perd compression pointeurs)
# - Exemple machine 16GB RAM -> -Xms8g -Xmx8g
# - Exemple machine 64GB RAM -> -Xms32g -Xmx32g (pas 50%!)
# - Exemple machine 128GB RAM -> -Xms32g -Xmx32g (toujours max 32GB)

# AUTRES OPTIONS JVM UTILES:
-XX:+UseG1GC                    # Garbage collector (déjà par défaut)
-XX:+HeapDumpOnOutOfMemoryError # Dump mémoire si crash
-XX:HeapDumpPath=/tmp           # Où sauver le dump

# === CONFIGURATION SÉCURITÉ DE BASE ===

# DÉSACTIVER SÉCURITÉ (DEV SEULEMENT!)
xpack.security.enabled: false
# [ATTENTION] NE JAMAIS faire ça en production!

# ACTIVER SÉCURITÉ (PRODUCTION)
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.http.ssl.enabled: true

# Après activation, configurer mots de passe:
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

# === CONFIGURATION PERFORMANCES ===

# 1. REFRESH INTERVAL
# Par défaut: 1s (données visibles après 1 seconde)
index.refresh_interval: 30s
# - Augmenter = meilleure performance indexation
# - Diminuer = données plus vite disponibles
# - "-1" = désactiver (pour bulk insert massif)

# 2. NUMBER OF SHARDS (Nombre de fragments)
index.number_of_shards: 1
# - 1 shard par défaut (v7+)
# - Plus de shards = meilleure distribution mais overhead
# - Règle: 1 shard = 10-50 GB de données
# - Petit index (< 50GB) = 1 shard
# - Gros index (500GB) = 10 shards
# - [ATTENTION] Impossible de changer après création index!

# 3. NUMBER OF REPLICAS (Copies de secours)
index.number_of_replicas: 1
# - 0 = pas de backup (DEV OK, PROD DANGER!)
# - 1 = 1 copie (standard production)
# - 2+ = haute disponibilité
# - Peut être changé à tout moment

# 4. TRANSLOG (Transaction log)
index.translog.durability: async
# - request (défaut): flush après chaque requête (lent mais sûr)
# - async: flush toutes les 5s (rapide mais risque perte 5s données)
# - Production critique: request
# - Logs non critiques: async

# === LIMITS & THRESHOLDS ===

# 1. CIRCUIT BREAKERS (Protection mémoire)
indices.breaker.total.limit: 70%
# - Arrête requêtes si dépassent 70% heap
# - Évite OutOfMemoryError
# - Augmenter seulement si queries légitimes échouent

# 2. DISK WATERMARKS (Alertes espace disque)
cluster.routing.allocation.disk.watermark.low: 85%
cluster.routing.allocation.disk.watermark.high: 90%
cluster.routing.allocation.disk.watermark.flood_stage: 95%
# Explication:
# - low (85%): Plus de nouveaux shards sur ce nœud
# - high (90%): Déplace shards existants ailleurs
# - flood_stage (95%): Index en read-only!

# 3. THREAD POOLS (Nombre de threads)
thread_pool.write.queue_size: 1000
thread_pool.search.queue_size: 1000
# - Augmenter si "rejected execution" dans logs
# - Trop haut = consomme trop RAM

# === CONFIGURATION LOGGING ===

# Fichier: /etc/elasticsearch/log4j2.properties

# Niveau de log
logger.level: info
# Niveaux: trace, debug, info, warn, error

# Logs spécifiques
logger.action.level: debug
logger.deprecation.level: warn

# === SETTINGS DYNAMIQUES (Sans redémarrage) ===

# Certains settings peuvent être changés sans redémarrer

# PERSISTENT (survit au redémarrage cluster):
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.recovery.max_bytes_per_sec": "50mb"
  }
}
'

# TRANSIENT (perdu au redémarrage cluster):
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "transient": {
    "cluster.routing.allocation.enable": "none"
  }
}
'

# Voir tous les settings:
curl -X GET "localhost:9200/_cluster/settings?pretty&include_defaults=true"

# === VÉRIFIER CONFIGURATION ===

# 1. Voir configuration actuelle
curl -X GET "localhost:9200/_nodes/_local/settings?pretty"

# 2. Voir stats nœud
curl -X GET "localhost:9200/_nodes/stats?pretty"

# 3. Voir santé cluster
curl -X GET "localhost:9200/_cluster/health?pretty"

# 4. Tester après changement config
sudo systemctl restart elasticsearch
sudo systemctl status elasticsearch
curl -X GET "localhost:9200"

# === ERREURS CONFIGURATION COURANTES ===

# ERREUR 1: "bootstrap checks failed"
# Cause: Mode production activé mais config insuffisante
# Solution: Corriger les checks échoués ou désactiver:
discovery.type: single-node

# ERREUR 2: "max virtual memory areas vm.max_map_count is too low"
sudo sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

# ERREUR 3: "max file descriptors too low"
# Éditer /etc/security/limits.conf:
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536

# ERREUR 4: Indentation YAML incorrecte
# [X] Mauvais:
cluster.name:my-cluster  # Pas d'espace après :

# [OK] Bon:
cluster.name: my-cluster  # Espace après :

# ERREUR 5: Port déjà utilisé
# Changer le port:
http.port: 9201

# Ou trouver qui utilise 9200:
sudo lsof -i :9200
sudo netstat -tulpn | grep 9200

# === BONNES PRATIQUES CONFIGURATION ===

# 1. TOUJOURS sauvegarder config avant modif
sudo cp /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml.backup

# 2. COMMENTER tes modifications
# Mon changement le 2024-01-15: augmenté heap pour meilleures perfs
-Xms8g
-Xmx8g

# 3. TESTER en dev avant production
# Ne jamais modifier prod directement!

# 4. DOCUMENTER la config
# Créer un README avec:
# - Pourquoi tel setting
# - Quand modifié
# - Par qui

# 5. VERSIONNER la config
git init /etc/elasticsearch
git add elasticsearch.yml
git commit -m "Configuration initiale"

# 6. MONITORER après changements
# Vérifier:
# - CPU usage
# - Memory usage
# - Search latency
# - Indexing throughput
# - Cluster health

# === TEMPLATES DE CONFIGURATION ===

# TEMPLATE 1: Développement local
cluster.name: dev
node.name: dev-node
path.data: /var/lib/elasticsearch
network.host: 127.0.0.1
discovery.type: single-node
xpack.security.enabled: false
-Xms1g
-Xmx1g

# TEMPLATE 2: Production petit cluster (3 nœuds)
cluster.name: prod-cluster
node.name: node-1  # Changer pour chaque nœud
node.roles: [ master, data ]
path.data: /mnt/elasticsearch/data
network.host: 0.0.0.0
discovery.seed_hosts: ["node1:9300", "node2:9300", "node3:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
xpack.security.enabled: true
-Xms8g
-Xmx8g

# TEMPLATE 3: Production grand cluster (nœud data)
cluster.name: prod-cluster
node.name: data-node-5
node.roles: [ data_hot ]
path.data: /mnt/ssd/elasticsearch
network.host: 0.0.0.0
discovery.seed_hosts: ["master1:9300", "master2:9300", "master3:9300"]
xpack.security.enabled: true
-Xms31g
-Xmx31g

# === OUTILS DE CONFIGURATION ===

# 1. Elasticsearch Configuration Tool (online)
# https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html

# 2. Validation config
/usr/share/elasticsearch/bin/elasticsearch -V

# 3. Dry-run (test config sans démarrer)
/usr/share/elasticsearch/bin/elasticsearch --help

# === PROCHAINES ÉTAPES ===
# Configuration terminée? Maintenant:
# 1. Sécuriser (voir section Sécurité)
# 2. Optimiser pour ton cas d'usage
# 3. Configurer backups (snapshots)
# 4. Mettre en place monitoring


[OK] CONFIGURATION - LOGSTASH (POUR DÉBUTANTS)

# === QU'EST-CE QUE LOGSTASH? ===

# Logstash = Pipeline de traitement de données
# Comme une usine qui transforme des données brutes en données structurées

# ANALOGIE:
# Imagine une usine de recyclage:
# 1. ENTRÉE (INPUT): Camions arrivent avec déchets mélangés
# 2. TRI (FILTER): On sépare plastique, verre, papier
# 3. SORTIE (OUTPUT): Matériaux triés partent vers recyclage

# Logstash fonctionne pareil:
# 1. INPUT: Logs bruts arrivent (fichiers, réseau, bases de données)
# 2. FILTER: Parse, nettoie, enrichit
# 3. OUTPUT: Envoie vers Elasticsearch (ou autre destination)

# === STRUCTURE D'UN PIPELINE ===

# Un fichier Logstash a TOUJOURS 3 sections:

input {
  # D'où viennent les données?
}

filter {
  # Comment les transformer?
}

output {
  # Où les envoyer?
}

# === SECTION INPUT (Sources de données) ===

# INPUT = D'où Logstash lit les données
# Comme choisir d'où tu veux recevoir du courrier

# 1. STDIN (Clavier - pour tests)
input {
  stdin { }    # Tape au clavier, appuie Entrée
}

# Utilisation:
echo "test message" | /usr/share/logstash/bin/logstash -f mon-pipeline.conf

# 2. FILE (Fichiers logs)
input {
  file {
    path => "/var/log/nginx/access.log"    # Quel fichier surveiller
    start_position => "beginning"          # Commence au début ou fin
    sincedb_path => "/dev/null"            # Où sauver position lecture
  }
}

# Explication paramètres:
# - path: Chemin fichier(s) à surveiller
#   Peut être pattern: "/var/log/*.log"
#   Peut être liste: ["/var/log/app1.log", "/var/log/app2.log"]
#
# - start_position:
#   "beginning" = lit depuis début (première fois)
#   "end" = lit seulement nouvelles lignes (défaut)
#
# - sincedb_path: Fichier mémorisant position lecture
#   Par défaut: ~/.sincedb_*
#   "/dev/null" = relit toujours depuis début (dev seulement!)

# Exemple surveillance plusieurs fichiers:
input {
  file {
    path => [
      "/var/log/nginx/access.log",
      "/var/log/nginx/error.log"
    ]
    start_position => "end"
    tags => ["nginx"]          # Tag pour identifier source
  }
}

# 3. BEATS (Filebeat, Metricbeat, etc.)
input {
  beats {
    port => 5044              # Port d'écoute pour Beats
  }
}

# Explication:
# Filebeat envoie logs vers ce port
# Logstash écoute et reçoit
# Architecture typique: Filebeat (serveurs) -> Logstash (central) -> Elasticsearch

# 4. TCP (Réseau)
input {
  tcp {
    port => 5000              # Port d'écoute
    codec => json             # Format attendu (json, plain, etc.)
  }
}

# Application peut envoyer logs directement:
# echo '{"message":"test"}' | nc localhost 5000

# 5. UDP (Réseau, plus rapide mais peut perdre données)
input {
  udp {
    port => 5000
    codec => json
  }
}

# 6. HTTP (API REST)
input {
  http {
    host => "0.0.0.0"         # Écoute sur toutes interfaces
    port => 8080              # Port HTTP
  }
}

# Application envoie via HTTP POST:
# curl -X POST http://localhost:8080 -d '{"message":"test"}'

# 7. JDBC (Base de données SQL)
input {
  jdbc {
    jdbc_driver_library => "/path/to/mysql-connector.jar"
    jdbc_driver_class => "com.mysql.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://localhost:3306/database"
    jdbc_user => "username"
    jdbc_password => "password"
    statement => "SELECT * FROM logs WHERE id > :sql_last_value"
    schedule => "*/5 * * * *"    # Toutes les 5 minutes (cron)
  }
}

# 8. KAFKA (Message queue)
input {
  kafka {
    bootstrap_servers => "kafka:9092"
    topics => ["logs-topic"]
    group_id => "logstash-consumer"
  }
}

# 9. SYSLOG (Logs système Unix/Linux)
input {
  syslog {
    port => 514               # Port standard syslog
    type => "syslog"
  }
}

# === SECTION FILTER (Transformation) ===

# FILTER = Comment transformer/enrichir les données
# Comme un traducteur qui convertit une langue en une autre

# FILTERS PRINCIPAUX:

# 1. GROK (Parser de logs - LE PLUS IMPORTANT!)
# Grok = Regex avec noms pour extraire informations

# Log brut Apache/Nginx:
# 192.168.1.1 - - [15/Jan/2024:10:30:45 +0000] "GET /index.html HTTP/1.1" 200 1234

filter {
  grok {
    match => { 
      "message" => "%{COMBINEDAPACHELOG}" 
    }
  }
}

# Résultat:
# {
#   "clientip": "192.168.1.1",
#   "timestamp": "15/Jan/2024:10:30:45 +0000",
#   "verb": "GET",
#   "request": "/index.html",
#   "httpversion": "1.1",
#   "response": "200",
#   "bytes": "1234"
# }

# PATTERNS GROK COURANTS:
# %{IP:client_ip}               - Adresse IP
# %{TIMESTAMP_ISO8601:timestamp} - Date ISO (2024-01-15T10:30:00Z)
# %{WORD:method}                - Mot (GET, POST, etc.)
# %{NUMBER:response_code:int}   - Nombre (convertit en integer)
# %{GREEDYDATA:message}         - Reste de la ligne
# %{COMBINEDAPACHELOG}          - Format Apache complet
# %{SYSLOGBASE}                 - Format syslog

# Pattern personnalisé:
filter {
  grok {
    match => {
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:log_message}"
    }
  }
}

# Log: "2024-01-15T10:30:00Z [ERROR] Database connection failed"
# Extrait:
# - timestamp: "2024-01-15T10:30:00Z"
# - level: "ERROR"
# - log_message: "Database connection failed"

# 2. DATE (Parser dates)
filter {
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"    # Stocke dans @timestamp
  }
}

# Pourquoi?
# Elasticsearch utilise @timestamp pour tri chronologique
# Parse le timestamp du log et le met dans @timestamp

# Formats date courants:
# - "ISO8601": 2024-01-15T10:30:00Z
# - "dd/MMM/yyyy:HH:mm:ss Z": 15/Jan/2024:10:30:45 +0000
# - "yyyy-MM-dd HH:mm:ss": 2024-01-15 10:30:45
# - "UNIX": 1705318245 (epoch seconds)
# - "UNIX_MS": 1705318245000 (epoch milliseconds)

# 3. MUTATE (Modifier champs)
filter {
  mutate {
    # Ajouter champ
    add_field => { 
      "environment" => "production"
      "processed" => true
    }
    
    # Renommer champ
    rename => { 
      "old_name" => "new_name" 
    }
    
    # Supprimer champ
    remove_field => [ "unwanted_field", "temporary" ]
    
    # Convertir type
    convert => {
      "response_code" => "integer"    # String -> Integer
      "response_time" => "float"      # String -> Float
    }
    
    # Lowercase
    lowercase => [ "level" ]          # ERROR -> error
    
    # Uppercase  
    uppercase => [ "country_code" ]   # fr -> FR
    
    # Strip whitespace
    strip => [ "username" ]
    
    # Split string
    split => { "tags" => "," }        # "tag1,tag2" -> ["tag1", "tag2"]
    
    # Replace
    gsub => [
      "message", "/", "_",            # Remplace / par _
      "message", "\s+", " "           # Remplace espaces multiples par 1
    ]
    
    # Merge arrays
    merge => { "tags" => "new_tags" }
  }
}

# 4. JSON (Parser JSON)
filter {
  json {
    source => "message"       # Champ contenant JSON
    target => "parsed"        # Où stocker résultat (optionnel)
  }
}

# Log: {"level":"ERROR","msg":"Failed","code":500}
# Après json filter:
# {
#   "level": "ERROR",
#   "msg": "Failed",
#   "code": 500
# }

# 5. CSV (Parser CSV)
filter {
  csv {
    columns => ["timestamp", "level", "message"]
    separator => ","          # Séparateur (défaut: ,)
  }
}

# Log: "2024-01-15,ERROR,Database failed"
# Résultat:
# {
#   "timestamp": "2024-01-15",
#   "level": "ERROR",
#   "message": "Database failed"
# }

# 6. GEOIP (Géolocaliser IP)
filter {
  geoip {
    source => "client_ip"     # Champ contenant IP
    target => "geoip"         # Où stocker résultat
  }
}

# IP: 8.8.8.8
# Résultat:
# {
#   "geoip": {
#     "country_name": "United States",
#     "city_name": "Mountain View",
#     "latitude": 37.386,
#     "longitude": -122.0838,
#     "location": {
#       "lat": 37.386,
#       "lon": -122.0838
#     }
#   }
# }

# 7. USERAGENT (Parser User-Agent)
filter {
  useragent {
    source => "user_agent"
    target => "ua"
  }
}

# User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/96.0"
# Résultat:
# {
#   "ua": {
#     "name": "Chrome",
#     "os": "Windows 10",
#     "device": "Desktop"
#   }
# }

# 8. CONDITIONALS (If/Else)
filter {
  if [level] == "ERROR" {
    mutate {
      add_tag => ["error"]
    }
  } else if [level] == "WARN" {
    mutate {
      add_tag => ["warning"]
    }
  } else {
    mutate {
      add_tag => ["info"]
    }
  }
}

# Opérateurs disponibles:
# - == : égal
# - != : différent
# - < > <= >= : comparaisons
# - =~ : correspond à regex
# - !~ : ne correspond pas à regex
# - in : dans liste
# - not in : pas dans liste
# - and or not : logique booléenne

# Exemples conditions:
if [response_code] >= 400 { ... }
if "nginx" in [tags] { ... }
if [message] =~ /error/i { ... }    # Regex, i=case insensitive
if [level] in ["ERROR", "FATAL"] { ... }

# 9. DROP (Ignorer événement)
filter {
  if [message] =~ /healthcheck/ {
    drop { }              # N'envoie pas cet événement
  }
}

# Utilise pour:
# - Ignorer logs de debug
# - Filtrer health checks
# - Supprimer données inutiles

# === SECTION OUTPUT (Destinations) ===

# OUTPUT = Où envoyer les données traitées
# Comme choisir où envoyer le courrier trié

# 1. ELASTICSEARCH (Principal)
output {
  elasticsearch {
    hosts => ["localhost:9200"]           # Serveur(s) Elasticsearch
    index => "logs-%{+YYYY.MM.dd}"        # Nom de l'index
  }
}

# Paramètres importants:

# - hosts: Liste serveurs Elasticsearch
hosts => ["es1:9200", "es2:9200", "es3:9200"]

# - index: Nom index (peut être dynamique)
index => "logs-%{+YYYY.MM.dd}"            # logs-2024-01-15
index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}"  # filebeat-2024-01-15
index => "logs-%{environment}-%{+YYYY.MM.dd}"   # logs-prod-2024-01-15

# - user/password: Authentification
user => "elastic"
password => "mot_de_passe"

# - ssl: Connexion sécurisée
ssl => true
cacert => "/path/to/ca.crt"

# - document_id: ID document personnalisé
document_id => "%{id}"                    # Utilise champ 'id' comme _id

# - pipeline: Pipeline Ingest Elasticsearch
pipeline => "my-pipeline"

# 2. STDOUT (Console - pour debug)
output {
  stdout {
    codec => rubydebug        # Format lisible
  }
}

# Affiche dans terminal, pratique pour tester
# Exemple sortie:
# {
#   "@timestamp" => 2024-01-15T10:30:00.000Z,
#   "message" => "test",
#   "host" => "localhost"
# }

# Autres codecs:
codec => json                 # Format JSON compact
codec => line { format => "custom: %{message}" }  # Format personnalisé

# 3. FILE (Fichier)
output {
  file {
    path => "/var/log/logstash/output-%{+YYYY-MM-dd}.log"
    codec => line { format => "%{message}" }
  }
}

# Écrit dans fichier sur disque
# Utilise pour: backups, archives

# 4. KAFKA (Message queue)
output {
  kafka {
    bootstrap_servers => "kafka:9092"
    topic_id => "processed-logs"
  }
}

# 5. EMAIL (Alertes)
output {
  email {
    to => "ops@example.com"
    subject => "ERROR: %{message}"
    body => "Host: %{host}\nLevel: %{level}\n\n%{message}"
    address => "smtp.gmail.com"
    port => 587
    username => "user@gmail.com"
    password => "app_password"
  }
}

# 6. HTTP (Webhook)
output {
  http {
    url => "https://hooks.slack.com/services/XXX"
    http_method => "post"
    format => "json"
  }
}

# 7. MULTIPLES OUTPUTS (Conditionnel)
output {
  # Tous vers Elasticsearch
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "logs-%{+YYYY.MM.dd}"
  }
  
  # Erreurs vers fichier
  if [level] == "ERROR" {
    file {
      path => "/var/log/errors.log"
    }
  }
  
  # Erreurs critiques vers email
  if [level] == "FATAL" {
    email {
      to => "urgent@example.com"
      subject => "FATAL ERROR"
    }
  }
  
  # Debug vers console
  if [environment] == "dev" {
    stdout {
      codec => rubydebug
    }
  }
}

# === EXEMPLE COMPLET: LOGS NGINX ===

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    tags => ["nginx", "access"]
  }
}

filter {
  # Parser format Nginx
  grok {
    match => { 
      "message" => "%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code:int} %{NUMBER:bytes:int} \"%{DATA:referrer}\" \"%{DATA:user_agent}\""
    }
  }
  
  # Parser date
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
  }
  
  # Géolocaliser IP
  geoip {
    source => "client_ip"
    target => "geoip"
  }
  
  # Parser User-Agent
  useragent {
    source => "user_agent"
    target => "ua"
  }
  
  # Convertir types
  mutate {
    convert => {
      "bytes" => "integer"
      "response_code" => "integer"
    }
    remove_field => ["message", "timestamp"]
  }
  
  # Ajouter tags selon statut
  if [response_code] >= 500 {
    mutate { add_tag => ["server_error"] }
  } else if [response_code] >= 400 {
    mutate { add_tag => ["client_error"] }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-logs-%{+YYYY.MM.dd}"
  }
  
  # Alertes sur erreurs serveur
  if "server_error" in [tags] {
    email {
      to => "ops@example.com"
      subject => "Nginx 5xx error"
    }
  }
}

# === TESTER CONFIGURATION ===

# Test syntaxe (sans exécuter):
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/nginx.conf --config.test_and_exit

# Si OK: "Configuration OK"
# Si erreur: Affiche ligne et erreur

# Exécuter en mode debug:
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/nginx.conf --log.level=debug

# Test rapide avec stdin/stdout:
input { stdin { } }
filter { 
  grok { match => { "message" => "%{IP:ip}" } }
}
output { stdout { codec => rubydebug } }

# Lance:
echo "192.168.1.1" | /usr/share/logstash/bin/logstash -f test.conf

# === FICHIER CONFIG PRINCIPAL: logstash.yml ===

# /etc/logstash/logstash.yml

# Chemin pipelines
path.config: /etc/logstash/conf.d/*.conf

# Workers (threads de traitement)
pipeline.workers: 2           # Nombre de CPU disponibles

# Batch size (événements traités ensemble)
pipeline.batch.size: 125      # Défaut: 125

# Batch delay
pipeline.batch.delay: 50      # Millisecondes avant flush

# Dead Letter Queue (événements échoués)
dead_letter_queue.enable: true
path.dead_letter_queue: /var/lib/logstash/dead_letter_queue

# Monitoring
xpack.monitoring.enabled: true
xpack.monitoring.elasticsearch.hosts: ["http://localhost:9200"]

# === ERREURS COURANTES ===

# ERREUR 1: "Expected one of ..."
# Cause: Syntaxe incorrecte
# Vérifier: accolades {}, guillemets "", virgules

# ERREUR 2: Grok parse failure
# Tag "_grokparsefailure" ajouté
# Solution: Tester pattern sur https://grokdebug.herokuapp.com/

# ERREUR 3: Date parse failure
# Tag "_dateparsefailure"
# Solution: Vérifier format date

# ERREUR 4: Could not connect to Elasticsearch
# Vérifier: Elasticsearch running? URL correcte?

# === BONNES PRATIQUES ===

# 1. TOUJOURS tester avec stdout avant Elasticsearch
# 2. UTILISER tags pour identifier sources
# 3. SUPPRIMER champs inutiles (économise espace)
# 4. AJOUTER metadata (environment, server, etc.)
# 5. LOGGER erreurs parsing (don't drop silently)
# 6. SÉPARER pipelines par type de logs
# 7. DOCUMENTER patterns Grok custom

# === Input: Beats ===

input {
  beats {
    port => 5044
  }
}

# === Input: Fichiers ===

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"  # Relire depuis début (dev)
  }
}

# === Input: TCP/UDP ===

input {
  tcp {
    port => 5000
    codec => json
  }
}

input {
  udp {
    port => 5000
    codec => json
  }
}

# === Input: HTTP ===

input {
  http {
    host => "0.0.0.0"
    port => 8080
  }
}

# === Filters: Grok (Parser de logs) ===

filter {
  grok {
    match => { 
      "message" => "%{COMBINEDAPACHELOG}" 
    }
  }
}

# Patterns Grok communs:
# %{COMBINEDAPACHELOG} - Logs Apache
# %{SYSLOGBASE} - Logs syslog
# %{IP:client_ip} - Extraire IP
# %{TIMESTAMP_ISO8601:timestamp} - Timestamp
# %{NUMBER:response_time:float} - Nombre

# Exemple custom:
filter {
  grok {
    match => {
      "message" => "%{IP:client_ip} - - \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code:int} %{NUMBER:bytes:int}"
    }
  }
}

# === Filters: Date ===

filter {
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
  }
}

# === Filters: Mutate (Modifier champs) ===

filter {
  mutate {
    # Ajouter champ
    add_field => { "environment" => "production" }
    
    # Renommer champ
    rename => { "old_field" => "new_field" }
    
    # Supprimer champ
    remove_field => [ "unwanted_field" ]
    
    # Convertir type
    convert => {
      "response_code" => "integer"
      "bytes" => "integer"
    }
    
    # Lowercase
    lowercase => [ "method" ]
    
    # Uppercase
    uppercase => [ "log_level" ]
    
    # Split
    split => { "tags" => "," }
    
    # Replace
    gsub => [
      "message", "/", "_"
    ]
  }
}

# === Filters: JSON ===

filter {
  json {
    source => "message"
  }
}

# === Filters: CSV ===

filter {
  csv {
    columns => ["timestamp", "level", "message"]
    separator => ","
  }
}

# === Filters: GeoIP ===

filter {
  geoip {
    source => "client_ip"
    target => "geoip"
  }
}

# === Filters: Conditionnels ===

filter {
  if [log_level] == "ERROR" {
    mutate {
      add_tag => [ "error" ]
    }
  }
  
  if [response_code] >= 400 {
    mutate {
      add_field => { "error_type" => "http_error" }
    }
  }
  
  if "nginx" in [tags] {
    grok {
      match => { "message" => "%{COMBINEDAPACHELOG}" }
    }
  }
}

# === Output: Elasticsearch ===

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "logs-%{+YYYY.MM.dd}"
    
    # Avec authentification
    user => "elastic"
    password => "changeme"
    
    # Template personnalisé
    template_name => "my-template"
    template => "/path/to/template.json"
  }
}

# === Output: Fichier ===

output {
  file {
    path => "/var/log/logstash/output.log"
    codec => line { format => "custom format: %{message}"}
  }
}

# === Output: Multiples outputs ===

output {
  # Tous les logs vers Elasticsearch
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "all-logs-%{+YYYY.MM.dd}"
  }
  
  # Seulement erreurs vers fichier
  if [log_level] == "ERROR" {
    file {
      path => "/var/log/errors.log"
    }
  }
  
  # Debug vers stdout
  if [environment] == "development" {
    stdout {
      codec => rubydebug
    }
  }
}

# === Configuration: logstash.yml ===

# Localisation: /etc/logstash/logstash.yml

# Chemin des pipelines
path.config: /etc/logstash/conf.d/*.conf

# Workers (threads)
pipeline.workers: 2

# Batch size
pipeline.batch.size: 125

# Pipeline ID
pipeline.id: main

# Monitoring
xpack.monitoring.enabled: true
xpack.monitoring.elasticsearch.hosts: ["http://localhost:9200"]

# === Tester configuration ===

# Tester syntaxe
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --config.test_and_exit

# Mode debug
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --log.level=debug

# Exécuter pipeline
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf


[OK] CONFIGURATION - KIBANA

# === Fichier: kibana.yml ===

# Localisation:
# Linux: /etc/kibana/kibana.yml
# Mac: /usr/local/etc/kibana/kibana.yml
# Windows: config\kibana.yml

# === Configuration de base ===

# Port Kibana
server.port: 5601

# Hôte (0.0.0.0 = accessible depuis extérieur)
server.host: "0.0.0.0"

# Nom affiché
server.name: "my-kibana"

# URL Elasticsearch
elasticsearch.hosts: ["http://localhost:9200"]

# === Avec authentification ===

elasticsearch.username: "kibana_system"
elasticsearch.password: "password"

# === Configuration avancée ===

# Base path (si derrière proxy)
server.basePath: "/kibana"
server.rewriteBasePath: true

# SSL
server.ssl.enabled: true
server.ssl.certificate: /path/to/cert.crt
server.ssl.key: /path/to/cert.key

# Logging
logging.dest: /var/log/kibana/kibana.log
logging.verbose: false

# === Redémarrer Kibana ===

sudo systemctl restart kibana.service


[OK] CONFIGURATION - FILEBEAT (GUIDE COMPLET DÉBUTANT)

# === QU'EST-CE QUE FILEBEAT? ===

# Filebeat = Agent léger qui collecte et envoie des logs
# Comme un facteur qui ramasse le courrier et l'amène au centre de tri

# ANALOGIE:
# Tu as 10 serveurs qui génèrent des logs dans /var/log/
# Sans Filebeat: Te connecter à chaque serveur, chercher logs manuellement
# Avec Filebeat: Agent sur chaque serveur envoie logs automatiquement

# ARCHITECTURE:
# Serveur 1 -> Filebeat -> Logstash -> Elasticsearch
# Serveur 2 -> Filebeat ->
# Serveur 3 -> Filebeat ->

# Ou direct (sans Logstash):
# Serveur -> Filebeat -> Elasticsearch

# POURQUOI FILEBEAT ET PAS LOGSTASH PARTOUT?
# Filebeat:
# - Léger (~50 MB RAM)
# - Simple à configurer
# - Un par serveur
# - Collecte seulement
#
# Logstash:
# - Lourd (~1 GB RAM)
# - Transformations complexes
# - Un central pour tous
# - Collecte + Transformation

# === FICHIER PRINCIPAL: filebeat.yml ===

# Localisation:
# Linux: /etc/filebeat/filebeat.yml
# Windows: C:\Program Files\Filebeat\filebeat.yml
# Docker: Monter volume avec config

# STRUCTURE:
# - filebeat.inputs: Quels logs lire
# - processors: Transformations légères
# - output: Où envoyer (Logstash ou Elasticsearch)
# - setup: Configuration Kibana dashboards

# === CONFIGURATION DE BASE ===

# EXEMPLE SIMPLE: Lire fichier log

filebeat.inputs:
- type: log                        # Type: log (fichier)
  enabled: true                    # Activer cet input
  paths:                          # Quels fichiers surveiller
    - /var/log/*.log              # Tous .log dans /var/log

output.elasticsearch:             # Envoyer vers Elasticsearch
  hosts: ["localhost:9200"]       # Adresse Elasticsearch

# C'est tout! Configuration minimale fonctionnelle

# === INPUTS (SOURCES DE LOGS) ===

# INPUT = D'où Filebeat lit les données
# Types principaux: log, container, journald, redis, etc.

# 1. TYPE: LOG (Fichiers)
# Le plus courant, surveille fichiers

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log   # Fichier spécifique
    - /var/log/nginx/error.log
    - /var/log/app/*.log           # Pattern (tous .log)
    - /var/log/*/*.log             # Récursif

# Paramètres importants:

# EXCLUDE_FILES: Ignorer certains fichiers
  exclude_files: ['\.gz


[OK] ELASTICSEARCH - API REST (COMPRENDRE LES BASES)

# === QU'EST-CE QU'UNE API REST? ===

# REST = Representational State Transfer
# C'est comme envoyer des lettres à Elasticsearch:
# - Tu envoies une requête HTTP (GET, POST, PUT, DELETE)
# - Elasticsearch répond avec du JSON
# - Pas besoin d'interface graphique, juste curl ou un outil HTTP

# STRUCTURE D'UNE REQUÊTE:
curl -X <MÉTHODE> "<URL>" -H 'Content-Type: application/json' -d '<DONNÉES JSON>'

# Exemple concret:
curl -X GET "http://localhost:9200/_cluster/health"
#     ^    ^                ^                  ^
#     |    |                |                  |
#  Méthode URL de base    Port              Endpoint (ce qu'on veut)

# MÉTHODES HTTP:
# GET    = Lire/Récupérer (comme "affiche-moi")
# POST   = Créer (comme "ajoute ça")
# PUT    = Créer/Remplacer (comme "mets ça à cet endroit")
# DELETE = Supprimer (comme "efface ça")

# === CONCEPTS ELASTICSEARCH ===

# 1. INDEX (Pluriel: INDICES)
# C'est comme une base de données ou une table
# Exemples: logs-2024-01-15, utilisateurs, produits
# Un index contient des documents similaires

# 2. DOCUMENT
# C'est un enregistrement, une ligne de données
# Format: JSON (comme un dictionnaire Python)
# Exemple de document:
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "ville": "Paris"
}

# 3. MAPPING
# C'est le schéma/structure des données
# Définit les types de champs (text, integer, date, etc.)
# Comme définir les colonnes d'une table SQL

# 4. SHARD
# Fragment d'un index pour distribuer les données
# Comme découper un gros livre en plusieurs tomes
# Plus de shards = meilleure distribution

# 5. REPLICA
# Copie de backup d'un shard
# Pour sécurité et performances (load balancing)

# === SANTÉ DU CLUSTER (Première commande à connaître!) ===

curl -X GET "localhost:9200/_cluster/health?pretty"

# Explication:
# _cluster/health = endpoint pour santé cluster
# ?pretty = affiche JSON formaté (plus lisible)

# Réponse:
{
  "cluster_name" : "elasticsearch",
  "status" : "green",              # <- IMPORTANT!
  "timed_out" : false,
  "number_of_nodes" : 1,           # Nombre de nœuds actifs
  "number_of_data_nodes" : 1,      # Nœuds qui stockent données
  "active_primary_shards" : 5,     # Shards primaires actifs
  "active_shards" : 5,             # Total shards actifs
  "relocating_shards" : 0,         # Shards en déplacement
  "initializing_shards" : 0,       # Shards en initialisation
  "unassigned_shards" : 0          # Shards non assignés (problème si > 0)
}

# STATUS EXPLIQUÉ:
# GREEN  = Tout va bien, toutes données disponibles et répliquées
# YELLOW = Données dispo mais replicas manquants (OK pour dev 1 nœud)
# RED    = Certaines données primaires manquantes (PROBLÈME GRAVE!)

# === GESTION DES INDEX ===

# 1. LISTER TOUS LES INDEX
curl -X GET "localhost:9200/_cat/indices?v"

# Sortie exemple:
# health status index           pri rep docs.count docs.deleted store.size
# yellow open   logs-2024-01-15  1   1       1234            0      1.2mb
# green  open   utilisateurs     1   0        456            0      500kb

# Colonnes expliquées:
# - health: santé (green/yellow/red)
# - status: open (accessible) ou close (fermé)
# - index: nom de l'index
# - pri: nombre de shards primaires
# - rep: nombre de replicas
# - docs.count: nombre de documents
# - store.size: taille sur disque

# 2. CRÉER UN INDEX (Simple)
curl -X PUT "localhost:9200/mon-index"

# Explication:
# PUT = créer ou remplacer
# /mon-index = nom du nouvel index
# Répond: {"acknowledged":true}

# 3. CRÉER UN INDEX (Avec configuration)
curl -X PUT "localhost:9200/mon-index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1,      # Nombre de fragments
    "number_of_replicas": 1     # Nombre de copies
  }
}
'

# Pourquoi configurer shards/replicas?
# - 1 shard + 0 replica = Dev (rapide, pas de backup)
# - 1 shard + 1 replica = Prod petit (backup)
# - 5 shards + 2 replicas = Prod large (distribué + haute dispo)

# 4. CRÉER INDEX AVEC MAPPING (Structure)
curl -X PUT "localhost:9200/utilisateurs" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "nom": { 
        "type": "text"           # Texte recherchable (full-text)
      },
      "age": { 
        "type": "integer"        # Nombre entier
      },
      "email": { 
        "type": "keyword"        # Texte exact (pas de full-text)
      },
      "date_inscription": { 
        "type": "date"           # Date
      },
      "actif": { 
        "type": "boolean"        # Vrai/Faux
      },
      "localisation": { 
        "type": "geo_point"      # Coordonnées GPS
      }
    }
  }
}
'

# TYPES DE CHAMPS EXPLIQUÉS:

# TEXT vs KEYWORD:
# - text: "Jean Dupont" -> recherche "jean", "dupont", "Jean Dupont" (trouvé!)
#         Analyse le texte, supporte recherche partielle
#         Bon pour: messages, descriptions, articles
#
# - keyword: "Jean Dupont" -> recherche exacte "Jean Dupont" seulement
#           Pas d'analyse, recherche exacte
#           Bon pour: emails, IDs, statuts, tags, URLs

# INTEGER / LONG:
# - Nombres entiers (-2, 0, 42, 1000)
# - integer: -2^31 à 2^31-1
# - long: plus grand range

# FLOAT / DOUBLE:
# - Nombres décimaux (3.14, -0.5, 1000.99)
# - float: précision simple
# - double: précision double (plus précis)

# DATE:
# - Dates et timestamps
# - Format: "2024-01-15", "2024-01-15T10:30:00Z"
# - Stocké en millisecondes depuis 1970 (epoch)

# BOOLEAN:
# - true ou false
# - Pour flags, état actif/inactif

# GEO_POINT:
# - Coordonnées latitude/longitude
# - Pour recherches géographiques
# - Format: {"lat": 48.8566, "lon": 2.3522}

# 5. VOIR MAPPING D'UN INDEX
curl -X GET "localhost:9200/utilisateurs/_mapping?pretty"

# Répond avec la structure complète de l'index

# 6. AJOUTER UN CHAMP AU MAPPING (Update)
curl -X PUT "localhost:9200/utilisateurs/_mapping" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "telephone": { "type": "keyword" }
  }
}
'

# [ATTENTION] IMPORTANT: On peut AJOUTER des champs mais pas MODIFIER les existants!
# Pour modifier: il faut réindexer (copier dans nouvel index)

# 7. SUPPRIMER UN INDEX
curl -X DELETE "localhost:9200/mon-index"

# [ATTENTION] ATTENTION: Supprime TOUTES les données de l'index!
# Pas de corbeille, pas d'undo!

# 8. SUPPRIMER PLUSIEURS INDEX (Pattern)
curl -X DELETE "localhost:9200/logs-2023-*"

# Supprime tous les index commençant par "logs-2023-"
# Exemple: logs-2023-01-01, logs-2023-01-02, etc.

# 9. FERMER UN INDEX (Économiser mémoire)
curl -X POST "localhost:9200/mon-index/_close"

# Quand fermer?
# - Index rarement utilisé mais à garder
# - Libère la mémoire
# - Données toujours sur disque
# - Pas cherchable tant que fermé

# 10. OUVRIR INDEX FERMÉ
curl -X POST "localhost:9200/mon-index/_open"

# 11. VOIR INFO DÉTAILLÉE D'UN INDEX
curl -X GET "localhost:9200/mon-index?pretty"

# Affiche: settings, mappings, aliases

# === GESTION DES DOCUMENTS ===

# 1. AJOUTER UN DOCUMENT (ID automatique)
curl -X POST "localhost:9200/utilisateurs/_doc" -H 'Content-Type: application/json' -d'
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "date_inscription": "2024-01-15",
  "actif": true
}
'

# Réponse:
{
  "_index": "utilisateurs",        # Dans quel index
  "_id": "abc123xyz",              # ID généré automatiquement
  "_version": 1,                   # Version (pour détection conflits)
  "result": "created",             # Action effectuée
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

# 2. AJOUTER DOCUMENT (ID spécifique)
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 25,
  "email": "marie@example.com",
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT avec /1 à la fin = ID sera "1"
# Pratique si tu as déjà un ID (ex: ID de ta base SQL)

# 3. RÉCUPÉRER UN DOCUMENT PAR ID
curl -X GET "localhost:9200/utilisateurs/_doc/1?pretty"

# Réponse:
{
  "_index": "utilisateurs",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,                   # <- Document trouvé!
  "_source": {                     # <- Les données!
    "nom": "Marie Martin",
    "age": 25,
    "email": "marie@example.com",
    "date_inscription": "2024-02-01",
    "actif": true
  }
}

# Si document n'existe pas: "found": false

# 4. RÉCUPÉRER SEULEMENT CERTAINS CHAMPS
curl -X GET "localhost:9200/utilisateurs/_doc/1?_source=nom,email&pretty"

# Renvoie seulement nom et email (économise bande passante)

# 5. VÉRIFIER SI DOCUMENT EXISTE (Rapide)
curl -I "localhost:9200/utilisateurs/_doc/1"

# -I = HEAD request (juste les headers, pas le body)
# Répond 200 si existe, 404 si n'existe pas
# Plus rapide que GET car ne récupère pas les données

# 6. METTRE À JOUR DOCUMENT COMPLET
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 26,                       # <- Changé de 25 à 26
  "email": "marie.new@example.com", # <- Email mis à jour
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT remplace TOUT le document!
# [ATTENTION] Si tu oublies un champ, il sera supprimé!

# 7. METTRE À JOUR PARTIELLEMENT (Recommandé)
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26                      # <- Change seulement age
  }
}
'

# _update avec "doc" = met à jour seulement les champs spécifiés
# Les autres champs restent intacts
# Plus sûr que PUT complet!

# 8. METTRE À JOUR AVEC SCRIPT
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "script": {
    "source": "ctx._source.age += params.increment",
    "params": {
      "increment": 1
    }
  }
}
'

# Explication:
# ctx._source = le document actuel
# ctx._source.age += 1 = incrémente age de 1
# Pratique pour compteurs, accumulateurs

# 9. UPSERT (Update ou Insert)
curl -X POST "localhost:9200/utilisateurs/_update/999" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "nom": "Nouveau",
    "age": 30
  },
  "doc_as_upsert": true
}
'

# Comportement:
# - Si document ID=999 existe -> met à jour
# - Si n'existe pas -> crée avec ces données
# Pratique pour éviter erreur "document not found"

# 10. SUPPRIMER UN DOCUMENT
curl -X DELETE "localhost:9200/utilisateurs/_doc/1"

# Supprime le document ID=1
# Pas de confirmation, c'est immédiat!

# 11. SUPPRIMER PAR REQUÊTE (Plusieurs documents)
curl -X POST "localhost:9200/utilisateurs/_delete_by_query" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "actif": false             # Supprime tous les inactifs
    }
  }
}
'

# Pratique pour nettoyage en masse
# Exemple: supprimer tous users inactifs depuis 1 an

# === OPÉRATIONS EN MASSE (BULK API) ===

# Pourquoi Bulk?
# - Insérer 1 document à la fois = lent (1000 documents = 1000 requêtes)
# - Bulk = grouper plusieurs opérations en 1 requête (1000 docs = 1 requête!)
# - Beaucoup plus rapide pour gros volumes

curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "index": { "_index": "utilisateurs", "_id": "1" } }
{ "nom": "User 1", "age": 30 }
{ "index": { "_index": "utilisateurs", "_id": "2" } }
{ "nom": "User 2", "age": 25 }
{ "delete": { "_index": "utilisateurs", "_id": "3" } }
{ "update": { "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# Format Bulk:
# Ligne 1: Action (index, create, update, delete)
# Ligne 2: Données (sauf pour delete)
# Répéter...

# [ATTENTION] IMPORTANT: 
# - Chaque ligne doit être un JSON valide
# - Dernière ligne doit se terminer par \n (retour ligne)
# - Pas de virgule entre les lignes

# Meilleures pratiques Bulk:
# - Batches de 5-15 MB (pas trop gros)
# - 1000-5000 documents par batch
# - Ne pas envoyer tout d'un coup (risque timeout) "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# === RECHERCHE DE DOCUMENTS (QUERIES) ===

# COMPRENDRE LA RECHERCHE ELASTICSEARCH

# Elasticsearch = Moteur de recherche comme Google
# 2 types de recherches:
# 1. QUERY (Score de pertinence)
#    - Répond: "À quel point ce document correspond?"
#    - Score: 0.0 à X (plus haut = plus pertinent)
#    - Bon pour: recherche full-text, "trouve articles sur python"
#
# 2. FILTER (Oui/Non)
#    - Répond: "Ce document correspond ou pas?"
#    - Pas de score
#    - Plus rapide (mis en cache)
#    - Bon pour: filtres exacts, "articles de 2024", "statut=publié"

# ENDPOINT DE RECHERCHE
curl -X GET "localhost:9200/<index>/_search"

# Structure de base:
{
  "query": {           # Ce qu'on cherche
    ...
  },
  "size": 10,          # Nombre résultats (défaut: 10)
  "from": 0,           # Pagination (0 = première page)
  "sort": [...],       # Tri des résultats
  "_source": [...]     # Quels champs retourner
}

# === RECHERCHE SIMPLE (MATCH_ALL) ===

# Récupérer TOUS les documents
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}    # Match tout (comme SELECT * en SQL)
  }
}
'

# Réponse:
{
  "took": 5,                    # Temps en millisecondes
  "timed_out": false,           # Timeout dépassé?
  "hits": {
    "total": {
      "value": 1234,            # Nombre total de résultats
      "relation": "eq"          # eq=exact, gte=au moins
    },
    "max_score": 1.0,           # Score max trouvé
    "hits": [                   # Les documents (défaut: 10 premiers)
      {
        "_index": "utilisateurs",
        "_id": "1",
        "_score": 1.0,          # Score de pertinence
        "_source": {            # Le document
          "nom": "Jean",
          "age": 30
        }
      }
    ]
  }
}

# === PAGINATION ===

# Page 1 (premiers 10 résultats)
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 10,          # Nombre par page
  "from": 0            # Offset (commence à 0)
}
'

# Page 2 (résultats 11-20)
{
  "size": 10,
  "from": 10          # Saute les 10 premiers
}

# Page 3 (résultats 21-30)
{
  "size": 10,
  "from": 20          # Saute les 20 premiers
}

# [ATTENTION] LIMITE: from + size ne peut pas dépasser 10,000
# Pour plus: utiliser Scroll API ou Search After

# === RECHERCHE FULL-TEXT (MATCH) ===

# Chercher dans un champ texte
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "nom": "Jean"
    }
  }
}
'

# Comment ça marche?
# 1. "Jean" est analysé (lowercase, etc.)
# 2. Cherche documents contenant "jean"
# 3. Documents avec "Jean Dupont", "jean martin" sont trouvés
# 4. Score calculé selon pertinence

# Recherche plusieurs mots:
{
  "query": {
    "match": {
      "message": "erreur connexion base"
    }
  }
}

# Comportement par défaut (OR):
# Trouve: "erreur" OU "connexion" OU "base"
# Document avec juste "erreur" sera trouvé (score plus bas)

# Forcer AND (tous les mots):
{
  "query": {
    "match": {
      "message": {
        "query": "erreur connexion base",
        "operator": "and"      # Doit contenir TOUS les mots
      }
    }
  }
}

# === RECHERCHE PHRASE EXACTE (MATCH_PHRASE) ===

# Chercher phrase dans l'ordre exact
{
  "query": {
    "match_phrase": {
      "message": "base de données"
    }
  }
}

# Trouve: "erreur base de données" [OK]
# Ne trouve PAS: "base données de test" [X] (ordre différent)

# Avec proximité (slop):
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "base données",
        "slop": 2              # Max 2 mots entre
      }
    }
  }
}

# Trouve: "base de données" [OK] (1 mot entre)
# Trouve: "base des données" [OK] (1 mot entre)
# Trouve: "base et des données" [X] (3 mots entre, dépasse slop)

# === RECHERCHE EXACTE (TERM) ===

# Pour champs keyword (email, ID, statut, etc.)
{
  "query": {
    "term": {
      "email.keyword": "jean@example.com"
    }
  }
}

# [ATTENTION] IMPORTANT:
# term = recherche EXACTE (sensible à la casse)
# "jean@example.com" ≠ "Jean@example.com"
# "jean@example.com" ≠ "jean@EXAMPLE.com"

# Pour chercher parmi plusieurs valeurs (IN en SQL):
{
  "query": {
    "terms": {
      "statut.keyword": ["actif", "en_attente"]
    }
  }
}

# === RECHERCHE PAR PLAGE (RANGE) ===

# Pour nombres, dates
{
  "query": {
    "range": {
      "age": {
        "gte": 25,     # Greater Than or Equal (>=)
        "lte": 35      # Less Than or Equal (<=)
      }
    }
  }
}

# Opérateurs disponibles:
# - gte: >= (supérieur ou égal)
# - gt:  >  (strictement supérieur)
# - lte: <= (inférieur ou égal)
# - lt:  <  (strictement inférieur)

# Exemple dates:
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "2024-01-01",
        "lt": "2024-02-01"
      }
    }
  }
}

# Dates relatives (pratique!):
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-7d",      # Il y a 7 jours
        "lte": "now"          # Maintenant
      }
    }
  }
}

# Unités temps:
# - y: années
# - M: mois
# - w: semaines
# - d: jours
# - h: heures
# - m: minutes
# - s: secondes

# Exemples:
# "now-1h": il y a 1 heure
# "now-30d": il y a 30 jours
# "now+1d": dans 1 jour

# === RECHERCHE BOOLÉENNE (BOOL) ===

# Combiner plusieurs conditions (comme AND, OR, NOT en SQL)
{
  "query": {
    "bool": {
      "must": [         # AND (doit matcher, affecte score)
        ...
      ],
      "filter": [       # AND (doit matcher, pas de score, plus rapide)
        ...
      ],
      "should": [       # OR (au moins 1, boost score)
        ...
      ],
      "must_not": [     # NOT (ne doit PAS matcher)
        ...
      ]
    }
  }
}

# Explication des clauses:

# MUST: Doit matcher + calcule score
# Utilise pour: recherche principale avec pertinence
{
  "must": [
    {"match": {"message": "error"}}
  ]
}

# FILTER: Doit matcher + pas de score (plus rapide, cachable)
# Utilise pour: filtres exacts (date, statut, etc.)
{
  "filter": [
    {"term": {"status": "published"}},
    {"range": {"date": {"gte": "2024-01-01"}}}
  ]
}

# SHOULD: Au moins 1 doit matcher (optionnel)
# Utilise pour: boost pertinence
{
  "should": [
    {"match": {"tags": "python"}},
    {"match": {"tags": "javascript"}}
  ]
}

# MUST_NOT: Ne doit PAS matcher
# Utilise pour: exclusions
{
  "must_not": [
    {"term": {"status": "deleted"}}
  ]
}

# === EXEMPLE COMPLET BOOL ===

# "Trouve logs d'erreur des 7 derniers jours, 
#  niveau ERROR ou FATAL, mais pas de l'application 'test'"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"message": "error"}}     # Contient "error"
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-7d"               # 7 derniers jours
            }
          }
        }
      ],
      "should": [
        {"term": {"level": "ERROR"}},       # Préfère ERROR
        {"term": {"level": "FATAL"}}        # ou FATAL
      ],
      "must_not": [
        {"term": {"app": "test"}}           # Pas de l'app test
      ],
      "minimum_should_match": 1             # Au moins 1 should requis
    }
  }
}

# === RECHERCHE MULTI-CHAMPS (MULTI_MATCH) ===

# Chercher dans plusieurs champs en même temps
{
  "query": {
    "multi_match": {
      "query": "Jean Paris",
      "fields": ["nom", "ville"]    # Cherche dans nom ET ville
    }
  }
}

# Avec boost (donner plus d'importance à un champ):
{
  "query": {
    "multi_match": {
      "query": "python",
      "fields": ["titre^3", "contenu"]  # ^3 = titre 3x plus important
    }
  }
}

# === RECHERCHE WILDCARD (AVEC JOKER) ===

# * = n'importe quels caractères
# ? = 1 caractère exactement

{
  "query": {
    "wildcard": {
      "nom": "Je*"         # Jean, Jerome, Jessica
    }
  }
}

{
  "query": {
    "wildcard": {
      "code": "ABC-???"    # ABC-123, ABC-xyz, etc.
    }
  }
}

# [ATTENTION] ATTENTION: Wildcard est LENT sur gros volumes
# Évite de commencer par * (ex: "*test")

# === RECHERCHE FUZZY (TOLÉRANTE AUX FAUTES) ===

# Trouve documents même avec fautes de frappe
{
  "query": {
    "fuzzy": {
      "nom": {
        "value": "Jeen",           # Faute: "Jeen" au lieu de "Jean"
        "fuzziness": "AUTO"        # Distance d'édition automatique
      }
    }
  }
}

# Fuzziness expliqué:
# - AUTO: ajuste selon longueur mot (recommandé)
# - 0: pas de tolérance (exact)
# - 1: 1 caractère différent
# - 2: 2 caractères différents

# Exemples avec fuzziness=1:
# "Jean" trouve: "Jaan", "Jern", "Jdan"
# Ne trouve pas: "Jorn" (2 différences)

# === RECHERCHE PREFIX (AUTOCOMPLÉTION) ===

# Trouve documents commençant par...
{
  "query": {
    "prefix": {
      "nom": "Jea"         # Trouve: Jean, Jeanne, Jeanette
    }
  }
}

# Bon pour: autocomplétion, suggestion

# === RECHERCHE EXISTS (CHAMP EXISTE) ===

# Trouve documents ayant un champ (non null)
{
  "query": {
    "exists": {
      "field": "email"     # Uniquement docs avec email
    }
  }
}

# Inverse (n'existe PAS):
{
  "query": {
    "bool": {
      "must_not": [
        {"exists": {"field": "email"}}
      ]
    }
  }
}

# === TRI DES RÉSULTATS (SORT) ===

# Tri simple:
{
  "query": {"match_all": {}},
  "sort": [
    {"age": {"order": "desc"}}    # desc=décroissant, asc=croissant
  ]
}

# Tri multiple (comme ORDER BY en SQL):
{
  "sort": [
    {"age": {"order": "desc"}},          # D'abord par age
    {"nom.keyword": {"order": "asc"}}    # Puis par nom
  ]
}

# [ATTENTION] Pour trier sur texte, utilise .keyword
# "nom.keyword" (pas "nom" seul)

# Tri par pertinence (score):
{
  "sort": [
    {"_score": {"order": "desc"}}    # _score = pertinence
  ]
}

# Tri par date (plus récent d'abord):
{
  "sort": [
    {"@timestamp": {"order": "desc"}}
  ]
}

# === SÉLECTION DE CHAMPS (_source) ===

# Retourner tous les champs (défaut):
{
  "query": {"match_all": {}}
  # _source contient tout
}

# Retourner champs spécifiques (économise bande passante):
{
  "query": {"match_all": {}},
  "_source": ["nom", "email"]    # Seulement nom et email
}

# Exclure certains champs:
{
  "_source": {
    "excludes": ["description_longue", "metadata"]
  }
}

# Inclure/Exclure ensemble:
{
  "_source": {
    "includes": ["user.*"],        # Tous champs user.xxx
    "excludes": ["*.password"]     # Sauf passwords
  }
}

# === HIGHLIGHTING (SURLIGNER RÉSULTATS) ===

# Surligne les termes trouvés (comme Google)
{
  "query": {
    "match": {"message": "error"}
  },
  "highlight": {
    "fields": {
      "message": {}
    }
  }
}

# Réponse inclut:
{
  "hits": {
    "hits": [{
      "_source": {"message": "Database error occurred"},
      "highlight": {
        "message": ["Database <em>error</em> occurred"]  # <em> autour du mot
      }
    }]
  }
}

# Personnaliser tags:
{
  "highlight": {
    "pre_tags": ["<strong>"],
    "post_tags": ["</strong>"],
    "fields": {"message": {}}
  }
}

# === SCROLL API (PAGINER GROS VOLUMES) ===

# Pour récupérer TOUS les documents (> 10,000)
# Utilisé pour exports, backups

# Étape 1: Première requête avec scroll
curl -X GET "localhost:9200/utilisateurs/_search?scroll=1m" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 1000          # 1000 docs par batch
}
'

# Répond avec scroll_id:
{
  "_scroll_id": "abc123xyz...",
  "hits": {
    "hits": [...]      # Premiers 1000 docs
  }
}

# Étape 2: Récupérer batch suivant
curl -X POST "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll": "1m",                    # Keep alive 1 minute
  "scroll_id": "abc123xyz..."        # scroll_id de la réponse précédente
}
'

# Répéter jusqu'à hits vide

# Étape 3: Nettoyer (libérer ressources)
curl -X DELETE "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll_id": "abc123xyz..."
}
'

# === COMPTER DOCUMENTS (COUNT) ===

# Juste compter (sans récupérer docs):
curl -X GET "localhost:9200/utilisateurs/_count?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {"actif": true}
  }
}
'

# Réponse:
{
  "count": 1234
}

# Plus rapide que _search car ne récupère pas les docs

# === EXEMPLES PRATIQUES DE RECHERCHES ===

# 1. RECHERCHE E-COMMERCE
# "Trouve produits 'ordinateur portable', 
#  prix entre 500 et 1500€, en stock, triés par popularité"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"nom": "ordinateur portable"}}
      ],
      "filter": [
        {"range": {"prix": {"gte": 500, "lte": 1500}}},
        {"term": {"en_stock": true}}
      ]
    }
  },
  "sort": [
    {"ventes": {"order": "desc"}}
  ]
}

# 2. RECHERCHE LOGS
# "Logs d'erreur des dernières 24h, serveur web, pas health checks"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"level": "ERROR"}}
      ],
      "filter": [
        {"range": {"@timestamp": {"gte": "now-24h"}}},
        {"term": {"service": "web"}}
      ],
      "must_not": [
        {"match": {"url": "/health"}}
      ]
    }
  },
  "sort": [{"@timestamp": {"order": "desc"}}]
}

# 3. RECHERCHE UTILISATEURS
# "Utilisateurs actifs de Paris ou Lyon, inscrits en 2024"
{
  "query": {
    "bool": {
      "must": [
        {"term": {"actif": true}},
        {"range": {"date_inscription": {"gte": "2024-01-01"}}}
      ],
      "should": [
        {"match": {"ville": "Paris"}},
        {"match": {"ville": "Lyon"}}
      ],
      "minimum_should_match": 1
    }
  }
}

# 4. RECHERCHE ARTICLES BLOG
# "Articles contenant 'python' ou 'javascript', 
#  publiés, par pertinence puis date"
{
  "query": {
    "bool": {
      "should": [
        {"match": {"titre": {"query": "python", "boost": 2}}},  # Titre = 2x important
        {"match": {"contenu": "python"}},
        {"match": {"titre": {"query": "javascript", "boost": 2}}},
        {"match": {"contenu": "javascript"}}
      ],
      "filter": [
        {"term": {"statut": "publié"}}
      ],
      "minimum_should_match": 1
    }
  },
  "sort": [
    {"_score": {"order": "desc"}},
    {"date_publication": {"order": "desc"}}
  ]
}

# === CONSEILS PERFORMANCE RECHERCHE ===

# 1. UTILISER FILTER AU LIEU DE MUST QUAND POSSIBLE
# [OK] Bon (cachable):
{"bool": {"filter": [{"term": {"status": "active"}}]}}

# [X] Moins bon (calcule score inutilement):
{"bool": {"must": [{"term": {"status": "active"}}]}}

# 2. LIMITER SIZE
# Ne demande que ce dont tu as besoin
{"size": 10}  # Pas {"size": 10000}

# 3. UTILISER _source FILTERING
# Seulement les champs nécessaires
{"_source": ["id", "nom"]}  # Pas tous les champs

# 4. ÉVITER WILDCARD STARTING WITH *
# [X] Lent: {"wildcard": {"nom": "*test"}}
# [OK] OK: {"wildcard": {"nom": "test*"}}

# 5. PRÉFÉRER TERM À MATCH POUR KEYWORDS
# [OK] Rapide: {"term": {"status.keyword": "active"}}
# [X] Lent: {"match": {"status": "active"}}

# 6. UTILISER BOOL QUERY EFFICACEMENT
# Ordre optimal:
{
  "bool": {
    "filter": [...],     # D'abord (plus rapide, cachable)
    "must": [...],       # Puis (scoring nécessaire)
    "should": [...],     # Puis (bonus optionnels)
    "must_not": [...]    # Enfin (exclusions)
  }
}

[OK] AGRÉGATIONS ELASTICSEARCH (POUR DÉBUTANTS)

# === QU'EST-CE QU'UNE AGRÉGATION? ===

# Agrégation = Calculs statistiques sur les données
# Comme GROUP BY + fonctions en SQL

# ANALOGIE:
# Tu as un panier de fruits:
# - COUNT: Combien de fruits? (total)
# - TERMS: Combien de pommes, oranges, bananes? (par type)
# - AVG: Poids moyen des fruits?
# - SUM: Poids total?
# - MAX/MIN: Fruit le plus/moins lourd?

# Elasticsearch fait pareil avec tes documents!

# === TYPES D'AGRÉGATIONS ===

# 1. METRICS (Métriques)
# Calculs simples: count, sum, avg, min, max
# Comme calculer une valeur

# 2. BUCKETS (Groupements)
# Regrouper documents par critère
# Comme GROUP BY en SQL

# 3. PIPELINE
# Agrégations sur résultats d'autres agrégations
# Comme calculs dérivés

# === STRUCTURE DE BASE ===

GET /index/_search
{
  "size": 0,              # Ne renvoie pas documents (juste stats)
  "aggs": {               # Section agrégations
    "nom_agregation": {   # Nom que tu choisis
      "type": {           # Type d'agrégation
        ...               # Configuration
      }
    }
  }
}

# === AGRÉGATIONS METRICS (CALCULS) ===

# 1. COUNT (Compter)
# Déjà disponible sans agrégation:
GET /logs/_count

# Dans agrégation:
{
  "aggs": {
    "total_logs": {
      "value_count": {
        "field": "message"
      }
    }
  }
}

# 2. SUM (Somme)
# Exemple: Total des ventes
{
  "size": 0,
  "aggs": {
    "total_ventes": {
      "sum": {
        "field": "montant"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "total_ventes": {
      "value": 125430.50    # Total
    }
  }
}

# 3. AVG (Moyenne)
# Exemple: Âge moyen des utilisateurs
{
  "size": 0,
  "aggs": {
    "age_moyen": {
      "avg": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "age_moyen": {
      "value": 32.5
    }
  }
}

# 4. MIN/MAX (Minimum/Maximum)
# Exemple: Prix min et max produits
{
  "size": 0,
  "aggs": {
    "prix_minimum": {
      "min": {
        "field": "prix"
      }
    },
    "prix_maximum": {
      "max": {
        "field": "prix"
      }
    }
  }
}

# 5. STATS (Statistiques complètes)
# Tout en un: count, min, max, avg, sum
{
  "size": 0,
  "aggs": {
    "stats_age": {
      "stats": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "stats_age": {
      "count": 1000,        # Nombre valeurs
      "min": 18,            # Minimum
      "max": 65,            # Maximum
      "avg": 35.5,          # Moyenne
      "sum": 35500          # Somme
    }
  }
}

# 6. EXTENDED_STATS (Stats étendues)
# Stats + variance, écart-type, etc.
{
  "size": 0,
  "aggs": {
    "stats_detailles": {
      "extended_stats": {
        "field": "response_time"
      }
    }
  }
}

# Ajoute:
# - variance
# - std_deviation (écart-type)
# - std_deviation_bounds (limites)

# 7. PERCENTILES (Percentiles)
# Exemple: Temps réponse p50, p95, p99
{
  "size": 0,
  "aggs": {
    "temps_reponse_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]    # p50, p95, p99
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "temps_reponse_percentiles": {
      "values": {
        "50.0": 120,      # 50% requêtes < 120ms
        "95.0": 450,      # 95% requêtes < 450ms
        "99.0": 890       # 99% requêtes < 890ms
      }
    }
  }
}

# Pourquoi utile?
# p50 = médiane (milieu)
# p95 = expérience 95% utilisateurs
# p99 = expérience worst case (presque tous)

# 8. CARDINALITY (Valeurs uniques)
# Exemple: Nombre visiteurs uniques
{
  "size": 0,
  "aggs": {
    "visiteurs_uniques": {
      "cardinality": {
        "field": "user_id.keyword"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "visiteurs_uniques": {
      "value": 15234      # ~15234 users uniques
    }
  }
}

# Note: Approximatif (algorithme HyperLogLog)
# Précis à ~3% près (suffisant pour gros volumes)

# === AGRÉGATIONS BUCKETS (GROUPEMENTS) ===

# 1. TERMS (Grouper par valeur)
# Comme GROUP BY en SQL
# Exemple: Logs par niveau (ERROR, WARN, INFO)

{
  "size": 0,
  "aggs": {
    "par_niveau": {
      "terms": {
        "field": "level.keyword",    # Champ à grouper
        "size": 10                   # Top 10
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_niveau": {
      "buckets": [
        {
          "key": "INFO",             # Valeur
          "doc_count": 8000          # Nombre documents
        },
        {
          "key": "WARN",
          "doc_count": 1500
        },
        {
          "key": "ERROR",
          "doc_count": 500
        }
      ]
    }
  }
}

# Options utiles:
# - size: Nombre de buckets (défaut: 10)
# - order: Tri
#   {"_count": "desc"}     # Par nombre (défaut)
#   {"_key": "asc"}        # Par valeur alphabétique
# - min_doc_count: Minimum docs pour apparaître

# Exemple avec tri:
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"_count": "desc"}    # Plus visitées d'abord
      }
    }
  }
}

# 2. RANGE (Plages de valeurs)
# Exemple: Répartition par tranches d'âge

{
  "size": 0,
  "aggs": {
    "tranches_age": {
      "range": {
        "field": "age",
        "ranges": [
          {"to": 18},                  # < 18
          {"from": 18, "to": 30},      # 18-29
          {"from": 30, "to": 50},      # 30-49
          {"from": 50}                 # 50+
        ]
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_age": {
      "buckets": [
        {
          "key": "*-18.0",
          "to": 18,
          "doc_count": 234
        },
        {
          "key": "18.0-30.0",
          "from": 18,
          "to": 30,
          "doc_count": 1567
        },
        {
          "key": "30.0-50.0",
          "from": 30,
          "to": 50,
          "doc_count": 2890
        },
        {
          "key": "50.0-*",
          "from": 50,
          "doc_count": 1109
        }
      ]
    }
  }
}

# Labels personnalisés:
{
  "ranges": [
    {"key": "Enfants", "to": 18},
    {"key": "Jeunes", "from": 18, "to": 30},
    {"key": "Adultes", "from": 30, "to": 50},
    {"key": "Seniors", "from": 50}
  ]
}

# 3. DATE_HISTOGRAM (Histogramme temporel)
# Grouper par intervalle temps
# Exemple: Logs par jour

{
  "size": 0,
  "aggs": {
    "logs_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"    # Intervalle
      }
    }
  }
}

# Intervalles disponibles:
# - "minute" / "1m"
# - "hour" / "1h"
# - "day" / "1d"
# - "week" / "1w"
# - "month" / "1M"
# - "quarter" / "1q"
# - "year" / "1y"

# Intervalles fixes:
# - "fixed_interval": "30s"    # 30 secondes
# - "fixed_interval": "12h"    # 12 heures

# Réponse:
{
  "aggregations": {
    "logs_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15T00:00:00.000Z",
          "key": 1705276800000,        # Timestamp epoch
          "doc_count": 45678           # Logs ce jour
        },
        {
          "key_as_string": "2024-01-16T00:00:00.000Z",
          "key": 1705363200000,
          "doc_count": 52341
        }
      ]
    }
  }
}

# Options utiles:
# - format: Format date
#   "format": "yyyy-MM-dd"
# - time_zone: Fuseau horaire
#   "time_zone": "Europe/Paris"
# - min_doc_count: 0 pour buckets vides
#   "min_doc_count": 0    # Affiche jours sans logs

# 4. HISTOGRAM (Histogramme numérique)
# Tranches égales sur nombres
# Exemple: Prix par tranches de 100€

{
  "size": 0,
  "aggs": {
    "tranches_prix": {
      "histogram": {
        "field": "prix",
        "interval": 100        # Tranches de 100
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_prix": {
      "buckets": [
        {"key": 0, "doc_count": 234},      # 0-99€
        {"key": 100, "doc_count": 567},    # 100-199€
        {"key": 200, "doc_count": 890},    # 200-299€
        {"key": 300, "doc_count": 345}     # 300-399€
      ]
    }
  }
}

# 5. FILTER (Filtrer avant agrégation)
# Créer bucket avec filtre
# Exemple: Stats seulement sur erreurs

{
  "size": 0,
  "aggs": {
    "erreurs": {
      "filter": {
        "term": {"level": "ERROR"}
      },
      "aggs": {
        "par_service": {
          "terms": {
            "field": "service.keyword"
          }
        }
      }
    }
  }
}

# 6. FILTERS (Multiples filtres)
# Plusieurs buckets avec filtres différents
# Exemple: Compteurs par niveau

{
  "size": 0,
  "aggs": {
    "messages_par_niveau": {
      "filters": {
        "filters": {
          "errors": {"match": {"level": "ERROR"}},
          "warnings": {"match": {"level": "WARN"}},
          "info": {"match": {"level": "INFO"}}
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "messages_par_niveau": {
      "buckets": {
        "errors": {"doc_count": 500},
        "warnings": {"doc_count": 1500},
        "info": {"doc_count": 8000}
      }
    }
  }
}

# === AGRÉGATIONS IMBRIQUÉES (NESTED) ===

# Combiner agrégations pour analyses multi-niveaux
# Comme GROUP BY avec sous-requêtes

# EXEMPLE 1: Logs par service, puis par niveau
{
  "size": 0,
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword"
      },
      "aggs": {                          # <- Sous-agrégation!
        "par_niveau": {
          "terms": {
            "field": "level.keyword"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_service": {
      "buckets": [
        {
          "key": "web",
          "doc_count": 5000,
          "par_niveau": {                # <- Sous-résultats
            "buckets": [
              {"key": "INFO", "doc_count": 4000},
              {"key": "WARN", "doc_count": 800},
              {"key": "ERROR", "doc_count": 200}
            ]
          }
        },
        {
          "key": "api",
          "doc_count": 3000,
          "par_niveau": {
            "buckets": [
              {"key": "INFO", "doc_count": 2700},
              {"key": "WARN", "doc_count": 250},
              {"key": "ERROR", "doc_count": 50}
            ]
          }
        }
      ]
    }
  }
}

# EXEMPLE 2: Ventes par jour + revenus
{
  "size": 0,
  "aggs": {
    "ventes_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "montant"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "montant"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "montant"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "ventes_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15",
          "doc_count": 234,
          "revenus": {"value": 12450.50},
          "nombre_ventes": {"value": 234},
          "panier_moyen": {"value": 53.21}
        },
        {
          "key_as_string": "2024-01-16",
          "doc_count": 267,
          "revenus": {"value": 15678.90},
          "nombre_ventes": {"value": 267},
          "panier_moyen": {"value": 58.72}
        }
      ]
    }
  }
}

# EXEMPLE 3: URLs par statut + temps réponse
{
  "size": 0,
  "aggs": {
    "par_statut": {
      "range": {
        "field": "response_code",
        "ranges": [
          {"key": "2xx", "from": 200, "to": 300},
          {"key": "4xx", "from": 400, "to": 500},
          {"key": "5xx", "from": 500, "to": 600}
        ]
      },
      "aggs": {
        "top_urls": {
          "terms": {
            "field": "url.keyword",
            "size": 5
          },
          "aggs": {
            "temps_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}

# === TRIER RÉSULTATS AGRÉGATION ===

# Par count (défaut):
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "order": {"_count": "desc"}    # Plus de hits d'abord
      }
    }
  }
}

# Par clé (alphabétique):
{
  "terms": {
    "field": "service.keyword",
    "order": {"_key": "asc"}          # A-Z
  }
}

# Par métrique sous-agrégation:
{
  "aggs": {
    "par_produit": {
      "terms": {
        "field": "produit.keyword",
        "order": {"revenus": "desc"}   # <- Trie par revenus
      },
      "aggs": {
        "revenus": {                   # <- Nom référencé
          "sum": {
            "field": "prix"
          }
        }
      }
    }
  }
}

# === FILTRER BUCKETS ===

# Minimum documents:
{
  "terms": {
    "field": "tag.keyword",
    "min_doc_count": 100      # Seulement tags avec 100+ docs
  }
}

# Inclure/Exclure valeurs:
{
  "terms": {
    "field": "status.keyword",
    "include": ["active", "pending"],    # Seulement ces valeurs
    "exclude": ["deleted", "archived"]   # Exclure ces valeurs
  }
}

# Inclure par regex:
{
  "terms": {
    "field": "url.keyword",
    "include": "/api/.*",       # Seulement URLs commençant par /api/
    "exclude": ".*/test/.*"     # Exclure URLs contenant /test/
  }
}

# === EXEMPLES PRATIQUES COMPLETS ===

# EXEMPLE 1: Dashboard e-commerce
# "Revenus, conversions, panier moyen par jour"

GET /orders/_search
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {"gte": "now-30d"}
    }
  },
  "aggs": {
    "par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {"field": "montant"}
        },
        "nombre_commandes": {
          "value_count": {"field": "montant"}
        },
        "panier_moyen": {
          "avg": {"field": "montant"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "user_id"}
        },
        "taux_conversion": {
          "bucket_script": {
            "buckets_path": {
              "commandes": "nombre_commandes",
              "visiteurs": "visiteurs_uniques"
            },
            "script": "params.commandes / params.visiteurs * 100"
          }
        }
      }
    }
  }
}

# EXEMPLE 2: Analyse logs application
# "Erreurs par service, avec top messages"

GET /logs/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {"term": {"level": "ERROR"}},
        {"range": {"@timestamp": {"gte": "now-24h"}}}
      ]
    }
  },
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword",
        "size": 10
      },
      "aggs": {
        "top_messages": {
          "terms": {
            "field": "message.keyword",
            "size": 5
          }
        },
        "dernier_timestamp": {
          "max": {
            "field": "@timestamp"
          }
        }
      }
    }
  }
}

# EXEMPLE 3: Analyse performance web
# "Temps réponse par endpoint, percentiles"

GET /nginx-logs/_search
{
  "size": 0,
  "aggs": {
    "par_endpoint": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"hits": "desc"}
      },
      "aggs": {
        "hits": {
          "value_count": {"field": "response_time"}
        },
        "temps_moyen": {
          "avg": {"field": "response_time"}
        },
        "percentiles": {
          "percentiles": {
            "field": "response_time",
            "percents": [50, 90, 95, 99]
          }
        },
        "lents": {
          "filter": {
            "range": {"response_time": {"gte": 1000}}
          }
        }
      }
    }
  }
}

# EXEMPLE 4: Analyse géographique
# "Requêtes par pays + revenus"

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "par_pays": {
      "terms": {
        "field": "geoip.country_name.keyword",
        "size": 20
      },
      "aggs": {
        "requetes": {
          "value_count": {"field": "@timestamp"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "client_ip"}
        },
        "par_ville": {
          "terms": {
            "field": "geoip.city_name.keyword",
            "size": 5
          }
        }
      }
    }
  }
}

# === CONSEILS PERFORMANCE AGRÉGATIONS ===

# 1. UTILISER size: 0
# Ne pas retourner documents (seulement stats)
{"size": 0}

# 2. LIMITER size des terms
# Top 10-20 suffisant généralement
{"size": 10}

# 3. FILTRER AVANT d'agréger
# Réduire volume données
{
  "query": {"range": {"@timestamp": {"gte": "now-7d"}}},
  "aggs": {...}
}

# 4. ÉVITER terms sur champs high-cardinality
# Exemple: Ne pas faire terms sur:
# - UUIDs (millions de valeurs uniques)
# - Timestamps précis
# - Texte libre
# Préférer: cardinality pour compter uniques

# 5. UTILISER doc_values=false si pas d'agrégations
# Dans mapping, si champ jamais agrégé

# 6. CACHER résultats si possible
# Même query répétée = résultat caché

# 7. PRÉFÉRER filters à queries dans agrégations
# Plus rapide (cachable)

# === Index Templates ===

# Créer template
curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "message": { "type": "text" },
        "level": { "type": "keyword" }
      }
    }
  }
}
'

# Lister templates
curl -X GET "localhost:9200/_index_template?pretty"

# Voir template spécifique
curl -X GET "localhost:9200/_index_template/logs_template?pretty"

# Supprimer template
curl -X DELETE "localhost:9200/_index_template/logs_template?pretty"

# === Aliases ===

# Créer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Créer alias avec filtre
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-errors",
        "filter": {
          "term": { "level": "ERROR" }
        }
      }
    }
  ]
}
'

# Supprimer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "remove": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Déplacer alias (atomique)
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    { "remove": { "index": "logs-2024-01", "alias": "logs-current" } },
    { "add": { "index": "logs-2024-02", "alias": "logs-current" } }
  ]
}
'

# Lister aliases
curl -X GET "localhost:9200/_alias?pretty"
curl -X GET "localhost:9200/logs-*/_alias?pretty"

# === Snapshots (Backups) ===

# Créer repository (filesystem)
curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/elasticsearch"
  }
}
'

# Créer snapshot
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,users",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Lister snapshots
curl -X GET "localhost:9200/_snapshot/my_backup/_all?pretty"

# Voir détails snapshot
curl -X GET "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"

# Restaurer snapshot
curl -X POST "localhost:9200/_snapshot/my_backup/snapshot_1/_restore?pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-2024-01",
  "ignore_unavailable": true,
  "include_global_state": false,
  "rename_pattern": "(.+)",
  "rename_replacement": "restored_$1"
}
'

# Supprimer snapshot
curl -X DELETE "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"


[OK] KIBANA - INTERFACE & FONCTIONNALITÉS (GUIDE COMPLET)

# === QU'EST-CE QUE KIBANA? ===

# Kibana = Interface graphique pour Elasticsearch
# C'est comme Google Analytics mais pour TES données

# ANALOGIE:
# Elasticsearch = Bibliothèque géante avec millions de livres
# Kibana = Système de recherche et catalogues pour trouver/visualiser les livres

# URL d'accès: http://localhost:5601

# === PREMIÈRE CONNEXION ===

# 1. Ouvrir navigateur: http://localhost:5601
# 2. Si sécurité activée:
#    Username: elastic
#    Password: (celui noté à l'installation)
# 3. Tu arrives sur la page d'accueil Kibana

# === NAVIGATION KIBANA ===

# Menu latéral gauche (principales sections):

# [GRAPHIQUE] ANALYTICS
#   - Discover: Explorer les données brutes
#   - Dashboard: Tableaux de bord
#   - Canvas: Présentations pixel-perfect
#   - Maps: Cartes géographiques
#   - Machine Learning: Détection anomalies (licence payante)

# [HAUSSE] OBSERVABILITY
#   - Logs: Vue centralisée logs
#   - APM: Application Performance Monitoring
#   - Metrics: Métriques infrastructure
#   - Uptime: Monitoring disponibilité

# [VERROUILLE] SECURITY
#   - SIEM: Security Information Event Management
#   - Endpoint: Sécurité endpoints

# [CONFIG] MANAGEMENT
#   - Stack Management: Configuration
#   - Dev Tools: Console pour requêtes
#   - Stack Monitoring: Monitoring ELK

# === DISCOVER (EXPLORATION DE DONNÉES) ===

# C'EST QUOI?
# Discover = Google Search pour tes données
# Cherche, filtre, explore les documents indexés

# ÉTAPES POUR COMMENCER:

# 1. CRÉER INDEX PATTERN
# Index Pattern = Dis à Kibana quels index explorer
# Exemple: "logs-*" pour tous index commençant par "logs-"

# Comment créer:
# a) Menu hamburger ([TRIGRAM_FOR_HEAVEN]) > Stack Management > Index Patterns
# b) "Create index pattern"
# c) Pattern name: logs-* (ou ton pattern)
# d) Time field: @timestamp (champ date pour tri chronologique)
# e) "Create index pattern"

# 2. ALLER DANS DISCOVER
# Menu hamburger > Analytics > Discover

# 3. SÉLECTIONNER INDEX PATTERN
# En haut à gauche: dropdown pour choisir "logs-*"

# 4. CHOISIR PÉRIODE
# En haut à droite: Time picker
# Options:
# - Last 15 minutes (défaut)
# - Last 1 hour
# - Last 24 hours
# - Last 7 days
# - Custom (choix précis)

# INTERFACE DISCOVER:

# BARRE DE RECHERCHE (KQL)
# Au milieu en haut, pour chercher dans les données

# KQL = Kibana Query Language
# Syntaxe simple pour rechercher

# Exemples KQL:
response_code: 200                    # Champ = valeur exacte
response_code >= 400                  # Comparaison
message: "error"                      # Contient "error"
message: "database error"             # Phrase (plusieurs mots)
level: ERROR and service: web         # AND logique
status: active or status: pending     # OR logique
NOT status: deleted                   # NOT logique
response_code: (200 or 201)           # Groupement
client_ip: "192.168.1.*"             # Wildcard
@timestamp >= "2024-01-01"           # Date

# HISTOGRAMME (Graphique en haut)
# Montre distribution temporelle des logs
# - Pic = beaucoup d'événements à ce moment
# - Creux = peu d'événements
# - Clique sur barre = zoom sur cette période

# LISTE DES CHAMPS (Colonne gauche)
# Tous les champs disponibles dans les documents
# 
# Actions sur champs:
# 1. Survoler champ -> Icônes apparaissent:
#    (+) Ajouter comme colonne
#    ([RECHERCHE]) Filtrer pour cette valeur
#    ([EYE]) Voir top values
#
# 2. Cliquer champ -> Voir statistiques:
#    - Top 5 valeurs
#    - Nombre d'occurrences
#    - Distribution

# TABLE DES DOCUMENTS (Centre)
# Liste des documents trouvés
# - Par défaut: 500 derniers
# - Triés par @timestamp (plus récent d'abord)
# 
# Pour chaque document:
# - Flèche ([BLACK_RIGHT-POINTING_TRIANGLE]) = Expand pour voir tous champs
# - Table view = Vue tabulaire
# - JSON view = Vue JSON brut

# FILTRES (Au-dessus recherche)
# Filtres visuels appliqués

# AJOUTER FILTRE:
# Méthode 1: Cliquer sur valeur dans document
# - Loupe (+) = "Filter for value" (inclure)
# - Loupe (-) = "Filter out value" (exclure)
#
# Méthode 2: Bouton "+ Add filter"
# - Choisir champ
# - Choisir opérateur (is, is not, exists, etc.)
# - Entrer valeur
# - "Save"

# Exemple:
# Filtre: level "is" "ERROR"
# -> Montre seulement logs niveau ERROR

# Combiner filtres:
# Filter 1: level is ERROR
# Filter 2: service is web
# -> Montre logs ERROR du service web

# SAUVEGARDER RECHERCHE:
# 1. Bouton "Save" (en haut à droite)
# 2. Nom: "Erreurs service web"
# 3. "Save"
# 
# Pour réutiliser:
# Bouton "Open" > Choisir recherche sauvegardée

# CAS D'USAGE DISCOVER:

# 1. DEBUGGING
# "Utilisateur X a eu une erreur hier à 14h30"
# - Time picker: Hier 14:00 - 15:00
# - Filtre: user_id is X
# - Filtre: level is ERROR
# -> Trouver l'erreur exacte en quelques secondes

# 2. INVESTIGATION INCIDENT
# "Le site était lent ce matin entre 9h et 10h"
# - Time picker: Aujourd'hui 09:00 - 10:00
# - Histogramme: Pic visible?
# - Ajouter colonne: response_time
# - Trier par response_time desc
# -> Voir quelles requêtes étaient lentes

# 3. ANALYSE PATTERN
# "Combien d'erreurs 404 par jour?"
# - Time picker: Last 7 days
# - Filtre: response_code is 404
# - Histogramme: Voir distribution
# - Cliquer champ "url.keyword" -> Top 5 URLs 404

# === VISUALIZATIONS (GRAPHIQUES) ===

# C'EST QUOI?
# Transformer données en graphiques visuels
# Comme Excel Charts mais pour Elasticsearch

# TYPES DE VISUALIZATIONS:

# 1. LINE CHART (Graphique ligne)
# Pour: Évolution temporelle
# Exemple: Nombre de logs par heure

# 2. AREA CHART (Graphique aire)
# Pour: Évolution avec remplissage
# Exemple: CPU usage dans le temps

# 3. BAR CHART (Graphique barres)
# Pour: Comparaisons
# Exemple: Logs par service

# 4. PIE CHART (Camembert)
# Pour: Proportions
# Exemple: Répartition logs par niveau (80% INFO, 15% WARN, 5% ERROR)

# 5. DATA TABLE (Tableau)
# Pour: Listes et rankings
# Exemple: Top 10 URLs les plus visitées

# 6. METRIC (Métrique unique)
# Pour: Chiffre clé
# Exemple: Nombre total de logs aujourd'hui

# 7. GAUGE (Jauge)
# Pour: Indicateur avec seuils
# Exemple: CPU usage avec zones vert/orange/rouge

# 8. TAG CLOUD (Nuage de mots)
# Pour: Fréquence termes
# Exemple: Mots les plus fréquents dans messages

# 9. HEAT MAP (Carte chaleur)
# Pour: Matrice de valeurs
# Exemple: Activité par heure et jour de semaine

# 10. MAPS (Carte géographique)
# Pour: Données géolocalisées
# Exemple: Requêtes par pays

# CRÉER UNE VISUALIZATION:

# MÉTHODE 1: LENS (Moderne, recommandé)

# 1. Menu > Visualize Library
# 2. "Create visualization"
# 3. "Lens" (outil drag-and-drop)
# 4. Choisir index pattern: logs-*
# 
# Interface Lens:
# - Gauche: Champs disponibles (glisser-déposer)
# - Centre: Aperçu graphique
# - Droite: Configuration

# EXEMPLE: Graphique ligne - Logs par heure

# 1. Type: Line
# 2. Axe X (horizontal):
#    - Glisser "@timestamp" depuis gauche
#    - Automatiquement: Date Histogram
#    - Intervalle: Hourly (par heure)
# 3. Axe Y (vertical):
#    - Par défaut: Count (nombre de documents)
# 4. Aperçu s'affiche!
# 5. Personnaliser:
#    - Titre axe Y: "Nombre de logs"
#    - Titre axe X: "Temps"
#    - Couleur ligne: Bleu
# 6. "Save" > Nom: "Logs par heure"

# EXEMPLE: Camembert - Répartition par niveau

# 1. Type: Pie
# 2. Slice by (découper par):
#    - Glisser "level.keyword"
#    - Automatiquement: Top 10 values
# 3. Size by:
#    - Count (nombre de docs par niveau)
# 4. Voir: 80% INFO, 15% WARN, 5% ERROR
# 5. "Save" > Nom: "Logs par niveau"

# EXEMPLE: Tableau - Top 10 URLs

# 1. Type: Table
# 2. Rows (lignes):
#    - Glisser "url.keyword"
#    - Top 10 values
# 3. Metrics:
#    - Count (nombre de fois visitée)
# 4. Tri: Par Count descending
# 5. "Save" > Nom: "Top 10 URLs"

# EXEMPLE: Métrique - Total logs aujourd'hui

# 1. Type: Metric
# 2. Metric value:
#    - Count
# 3. Time range: Today
# 4. Format: Nombre (ex: 1,234,567)
# 5. "Save" > Nom: "Total logs"

# MÉTHODE 2: VISUALIZATION TYPES (Classique)

# Plus de contrôle mais moins intuitif
# Menu > Visualize Library > Create visualization > Choisir type

# EXEMPLE: Vertical Bar - Logs par service

# 1. Choisir: "Vertical Bar"
# 2. Source: logs-*
# 3. Y-axis (Metrics):
#    - Aggregation: Count
#    - Label: "Nombre de logs"
# 4. X-axis (Buckets):
#    - Aggregation: Terms
#    - Field: service.keyword
#    - Size: 10
#    - Order: Metric - Count descending
#    - Label: "Service"
# 5. "Update" ([BLACK_RIGHT-POINTING_TRIANGLE]) pour voir
# 6. "Save" > Nom: "Logs par service"

# PERSONNALISATION VISUALIZATIONS:

# COULEURS:
# - Single color: Une couleur
# - By value: Couleur selon valeur
# - Custom palette: Palette personnalisée

# LÉGENDES:
# - Position: Right, Left, Top, Bottom
# - Afficher/Masquer

# AXES:
# - Titre
# - Échelle: Linear, Log
# - Min/Max

# TOOLTIPS:
# - Infos au survol
# - Format

# === DASHBOARDS (TABLEAUX DE BORD) ===

# C'EST QUOI?
# Dashboard = Collection de visualizations
# Comme un tableau de bord de voiture: tout en un coup d'œil

# CRÉER DASHBOARD:

# 1. Menu > Dashboard
# 2. "Create dashboard"
# 3. "Add from library" ou "Create visualization"
# 
# AJOUTER VISUALIZATIONS:
# 4. "Add from library"
# 5. Cocher: "Logs par heure", "Logs par niveau", "Top 10 URLs"
# 6. "Add"
# 
# ARRANGER:
# 7. Glisser-déposer pour positionner
# 8. Coins pour redimensionner
# 9. Layout automatique ou manuel
#
# SAUVEGARDER:
# 10. "Save" > Nom: "Dashboard Logs Production"
# 11. Description: "Vue d'ensemble logs prod"
# 12. "Save"

# FONCTIONNALITÉS DASHBOARD:

# 1. FILTRES GLOBAUX
# Appliqués à TOUTES les visualizations
# - Ajouter filtre en haut
# - Ex: level is ERROR
# -> Toutes les viz montrent seulement erreurs

# 2. TIME PICKER GLOBAL
# Change période pour tout le dashboard
# - Last 15 minutes
# - Last 24 hours
# - Custom range

# 3. DRILL-DOWN
# Cliquer sur élément -> Filtre ajouté
# Ex: Cliquer "ERROR" dans camembert
# -> Dashboard filtré sur erreurs seulement

# 4. REFRESH AUTO
# Actualisation automatique
# - Cliquer horloge ([HEURE])
# - Choisir intervalle: 10s, 30s, 1m, 5m
# -> Dashboard se rafraîchit automatiquement

# 5. MODE PLEIN ÉCRAN
# Pour affichage grand écran (TV, monitoring room)
# - Bouton "Full screen"
# - Appuyer ESC pour sortir

# 6. PARTAGE
# - Share -> Permalink (lien permanent)
# - Share -> Embed code (iframe HTML)
# - Share -> PDF/PNG (export image)

# EXEMPLE DASHBOARD E-COMMERCE:

# Visualizations:
# 1. Metric: Visiteurs actuels (rafraîchi 10s)
# 2. Line: Visites par heure (24h)
# 3. Pie: Répartition devices (Desktop/Mobile/Tablet)
# 4. Bar: Top 10 produits vus
# 5. Table: Derniers achats
# 6. Map: Visiteurs par pays
# 7. Gauge: Taux conversion (%)
# 8. Line: Revenus par heure

# Layout:
# +------------------+------------------+
# | Visiteurs: 1,234 | Taux conv: 3.2% |
# +------------------+------------------+
# | Visites (line - 24h)                |
# +-------------------------------------+
# | Devices (pie) | Top produits (bar) |
# +---------------+--------------------+
# | Map mondial   | Derniers achats    |
# +---------------+--------------------+
# | Revenus (line)                      |
# +-------------------------------------+

# === CANVAS (PRÉSENTATIONS) ===

# C'EST QUOI?
# Canvas = PowerPoint mais avec données temps réel
# Design pixel-perfect pour présentations

# QUAND UTILISER?
# - Présentation executive
# - Affichage TV monitoring
# - Rapport visuel marketing
# - Infographie dynamique

# CRÉER WORKPAD:

# 1. Menu > Canvas
# 2. "Create workpad"
# 3. Template ou "Start from scratch"
#
# INTERFACE:
# - Toolbar haut: Éléments à ajouter
# - Canvas centre: Zone de design
# - Sidebar droite: Propriétés élément

# ÉLÉMENTS DISPONIBLES:

# 1. TEXT (Texte)
# - Titres, labels, descriptions
# - Font, size, color personnalisables

# 2. SHAPE (Formes)
# - Rectangle, cercle, ligne
# - Pour structure visuelle

# 3. IMAGE
# - Logo, icônes, illustrations
# - Upload ou URL

# 4. ELEMENT (Données)
# - Metric: Chiffre de Elasticsearch
# - Chart: Graphique
# - Table: Tableau
# - Markdown: Texte formaté

# 5. FILTER
# - Time filter
# - Dropdown filter

# EXEMPLE: Rapport mensuel

# Page 1: Couverture
# - Background: Dégradé bleu
# - Logo entreprise
# - Titre: "Rapport Janvier 2024"
# - Sous-titre: "Analyse trafic web"

# Page 2: KPIs
# - 4 grandes métriques:
#   * Visiteurs uniques
#   * Pages vues
#   * Taux rebond
#   * Temps moyen session
# - Design carte avec icône

# Page 3: Graphiques
# - Évolution visites (line)
# - Top pages (bar horizontal)
# - Sources trafic (pie)

# Page 4: Géographie
# - Carte mondiale visiteurs
# - Top 10 pays (table)

# FONCTIONNALITÉS:

# - Multiple pages (slides)
# - Animations transitions
# - Auto-play (diaporama auto)
# - Export PDF/PNG
# - Partage via lien
# - Mode présentation plein écran

# === MAPS (CARTES GÉOGRAPHIQUES) ===

# C'EST QUOI?
# Visualiser données avec coordonnées géographiques
# Comme Google Maps avec tes données

# PRÉREQUIS:
# Données avec champ geo_point dans Elasticsearch
# Exemple:
# {
#   "client_ip": "8.8.8.8",
#   "geoip": {
#     "location": {
#       "lat": 37.386,
#       "lon": -122.0838
#     },
#     "country": "United States"
#   }
# }

# CRÉER MAP:

# 1. Menu > Maps
# 2. "Create map"
# 3. "Add layer"

# TYPES DE LAYERS:

# 1. DOCUMENTS (Points)
# - Chaque document = 1 point sur carte
# - Exemple: IP clientes
# Configuration:
# - Index: logs-*
# - Geospatial field: geoip.location
# - Tooltip: Afficher IP, country

# 2. CLUSTERS
# - Groupe points proches
# - Exemple: 100 requêtes Paris -> 1 cercle "100"
# - Zoom: Cercle se décompose

# 3. HEAT MAP
# - Carte de chaleur (densité)
# - Rouge = beaucoup, Bleu = peu
# - Exemple: Zones activité forte

# 4. CHOROPLETH
# - Régions colorées
# - Exemple: Pays colorés selon revenus
# - USA rouge (1M$), France orange (500K$), etc.

# EXEMPLE: Attaques réseau

# Layer 1: Choropleth - Pays sources attaques
# - Agrégation: Count par pays
# - Couleur: Rouge = beaucoup, Vert = peu

# Layer 2: Lines - Flux attaques
# - Source: IP attaquant
# - Destination: Serveur
# - Lignes rouges entre pays

# Layer 3: Points - Serveurs
# - Nos serveurs (points verts)

# PERSONNALISATION:

# - Basemap: Streets, Satellite, Dark, Light
# - Zoom initial
# - Centre initial
# - Bounds (limiter zone)
# - Tooltips (infos au survol)
# - Symboles (icônes custom)
# - Couleurs (palettes)

# === ALERTING (ALERTES) ===

# C'EST QUOI?
# Surveillance automatique + notifications
# "Préviens-moi si X arrive"

# CRÉER ALERTE:

# 1. Menu hamburger > Stack Management
# 2. "Rules and Connectors"
# 3. "Create rule"

# TYPES DE RÈGLES:

# 1. INDEX THRESHOLD
# "Si nombre de documents dépasse seuil"
# Exemple: Plus de 100 erreurs en 5 minutes

# Configuration:
# - Name: "Trop d'erreurs"
# - Index: logs-*
# - When: count()
# - Over: all documents
# - For the last: 5 minutes
# - Threshold: Is above 100
# - Group by: service (optionnel)
# - Filter: level: "ERROR"

# 2. ELASTICSEARCH QUERY
# Query DSL personnalisée
# Plus flexible mais plus complexe

# 3. ANOMALY DETECTION (ML)
# Détection automatique anomalies
# Nécessite licence Gold+

# ACTIONS (Que faire quand alerte?):

# 1. EMAIL
# - To: ops@example.com
# - Subject: "ALERTE: {{context.rule.name}}"
# - Body: "{{context.hits}} erreurs détectées"

# 2. SLACK
# - Connector: Webhook Slack
# - Channel: #alerts
# - Message: "[ATTENTION] Alerte: {{context.message}}"

# 3. WEBHOOK (HTTP)
# - URL: https://api.example.com/alert
# - Method: POST
# - Body: JSON avec détails

# 4. PAGERDUTY
# - Intégration PagerDuty
# - Severity: Critical
# - Description: Alerte détails

# 5. INDEX (Écrire dans Elasticsearch)
# - Index: alerts-*
# - Document: Détails alerte

# EXEMPLE COMPLET:

# Règle: "Erreurs 5xx serveur web"
# Type: Index threshold
# Check every: 1 minute
# Conditions:
# - Index: nginx-logs-*
# - When: count()
# - Over: all documents
# - For: last 5 minutes
# - Is above: 50
# - Filter: response_code >= 500 AND service: "web"
# Actions:
# - Email ops
# - Slack #incidents
# - PagerDuty si production

# === DEV TOOLS (CONSOLE) ===

# C'EST QUOI?
# Console pour envoyer requêtes Elasticsearch directement
# Comme terminal SQL mais pour Elasticsearch

# OUVRIR:
# Menu > Dev Tools

# INTERFACE:
# - Gauche: Éditeur requêtes
# - Droite: Résultats

# UTILISATION:

# 1. Taper requête:
GET /_cluster/health

# 2. Curseur sur ligne
# 3. Cliquer [BLACK_RIGHT-POINTING_TRIANGLE] ou Ctrl+Enter
# 4. Résultat s'affiche à droite

# FONCTIONNALITÉS:

# - AUTOCOMPLÉTION: Ctrl+Space
# - FORMATER: Ctrl+I
# - HISTORIQUE: ^v pour naviguer
# - MULTI-REQUÊTES: Séparer par ligne vide

# EXEMPLES:

# Santé cluster
GET /_cluster/health

# Lister index
GET /_cat/indices?v

# Recherche
GET /logs-*/_search
{
  "query": {
    "match": {
      "level": "ERROR"
    }
  }
}

# Créer document
POST /users/_doc
{
  "name": "Jean",
  "age": 30
}

# === STACK MANAGEMENT ===

# CONFIGURATION CENTRALE DE ELK

# INDEX PATTERNS:
# - Créer/gérer patterns
# - Définir champ timestamp
# - Refresh fields

# SAVED OBJECTS:
# - Importer/Exporter dashboards
# - Sauvegardes visualizations
# - Format: JSON (ndjson)

# Exporter dashboard:
# 1. Saved Objects
# 2. Cocher dashboard
# 3. "Export"
# 4. Télécharge .ndjson

# Importer:
# 1. "Import"
# 2. Glisser fichier .ndjson
# 3. Résoudre conflits
# 4. "Import"

# INDEX LIFECYCLE MANAGEMENT (ILM):
# - Politiques gestion cycle vie
# - Hot -> Warm -> Cold -> Delete
# - Automatisation retention

# ADVANCED SETTINGS:
# - Thème sombre: discover:enableDarkTheme
# - Langue UI
# - Format dates
# - Timezone

# === SPACES (ESPACES) ===

# C'EST QUOI?
# Espaces isolés pour organiser par équipe/projet
# Comme dossiers séparés

# EXEMPLE:
# - Space "Marketing": Dashboards trafic web
# - Space "DevOps": Dashboards infrastructure
# - Space "Security": Dashboards sécurité

# CRÉER SPACE:

# 1. Stack Management > Spaces
# 2. "Create space"
# 3. Name: "Marketing"
# 4. Initials: "MK" (avatar)
# 5. Color: Bleu
# 6. Description: "Espace équipe marketing"
# 7. "Create"

# CHANGER SPACE:
# Menu en haut à gauche > Choisir space

# === CONSEILS UTILISATION KIBANA ===

# PERFORMANCE:

# 1. LIMITER time range si beaucoup données
# - Last 15 min plutôt que Last 7 days
#
# 2. UTILISER filtres plutôt que queries larges
# - Filter: service is "web" (rapide)
# - Query: * (lent, tout scanner)
#
# 3. SAUVEGARDER recherches fréquentes
# - Évite retaper
#
# 4. REFRESH AUTO seulement si nécessaire
# - Consomme ressources
#
# 5. DASHBOARDS légers
# - 8-12 viz max par dashboard
# - Séparer si plus

# ORGANISATION:

# 1. NOMMER clairement
# - [OK] "Erreurs Production - Dernières 24h"
# - [X] "Dashboard 1"
#
# 2. DESCRIPTIONS
# - Ajouter description dashboards
# - Expliquer à quoi ça sert
#
# 3. TAGS
# - Tagger dashboards: "production", "monitoring"
# - Facilite recherche
#
# 4. DOSSIERS
# - Organiser dans Saved Objects
#
# 5. CONVENTIONS
# - Préfixe: "PROD -", "DEV -"
# - Cohérence nommage

# SÉCURITÉ:

# 1. RÔLES appropriés
# - Lecture seule pour viewers
# - Édition pour analysts
#
# 2. SPACES pour isolation
# - Équipe A ne voit pas équipe B
#
# 3. DASHBOARDS en read-only
# - Évite modifications accidentelles Elasticsearch

# === Machine Learning (Détection d'anomalies) ===

# Nécessite licence (Gold ou supérieure)
# 1. Aller dans "Machine Learning"
# 2. "Create job"
# 3. Choisir type:
#    - Single metric (une métrique)
#    - Multi metric (plusieurs métriques)
#    - Population (comportement groupe)
# 4. Configurer détecteurs
# 5. Lancer job

# === Alerting (Alertes) ===

# 1. Aller dans "Stack Management" > "Rules and Connectors"
# 2. "Create rule"
# 3. Types:
#    - Index threshold: Seuil sur nombre documents
#    - Elasticsearch query: Query personnalisée
#    - Anomaly detection: Basé sur ML
# 4. Configurer conditions
# 5. Configurer actions (email, Slack, webhook, etc.)

# Exemple: Alerte si erreurs > 100 en 5 minutes
# Rule type: Index threshold
# Index: logs-*
# When: count()
# Over: all documents
# For the last: 5 minutes
# Is above: 100
# Filter: level: "ERROR"

# === Dev Tools (Console) ===

# Console pour exécuter requêtes Elasticsearch
# 1. Aller dans "Dev Tools"
# 2. Taper requêtes:

GET /_cluster/health

GET /logs-*/_search
{
  "query": {
    "match_all": {}
  }
}

POST /logs-2024-01/_doc
{
  "message": "Test log",
  "level": "INFO",
  "@timestamp": "2024-01-15T10:00:00"
}

# Autocomplétion: Ctrl+Space
# Exécuter: Ctrl+Enter
# Formater: Ctrl+I

# === Stack Management ===

# 1. Index Patterns:
#    - Créer pattern pour découvrir données
#    - Ex: logs-*, filebeat-*
#    - Définir champ timestamp

# 2. Index Lifecycle Management (ILM):
#    - Gérer cycle de vie des index
#    - Hot > Warm > Cold > Delete

# 3. Saved Objects:
#    - Importer/Exporter dashboards, visualizations
#    - Format JSON

# 4. Advanced Settings:
#    - Personnaliser Kibana
#    - Thème sombre: discover:enableDarkTheme

# === Spaces (Espaces) ===

# Organiser dashboards par équipe/projet
# 1. Stack Management > Spaces
# 2. Create space
# 3. Assigner visualizations, dashboards
# 4. Changer d'espace: menu en haut à gauche


[OK] LOGSTASH - EXEMPLES COMPLETS

# === Pipeline: Logs Apache/Nginx ===

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    sincedb_path => "/var/lib/logstash/sincedb_nginx"
    tags => ["nginx", "access"]
  }
}

filter {
  if "nginx" in [tags] {
    grok {
      match => { 
        "message" => "%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code:int} %{NUMBER:bytes:int} \"%{DATA:referrer}\" \"%{DATA:user_agent}\"" 
      }
    }
    
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
      target => "@timestamp"
    }
    
    geoip {
      source => "client_ip"
      target => "geoip"
    }
    
    useragent {
      source => "user_agent"
      target => "user_agent_parsed"
    }
    
    mutate {
      remove_field => ["message", "timestamp"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs JSON ===

input {
  tcp {
    port => 5000
    codec => json
  }
}

filter {
  # Les données sont déjà en JSON, parser automatique
  
  if [level] == "ERROR" or [level] == "FATAL" {
    mutate {
      add_tag => ["error"]
    }
  }
  
  # Extraire info de stack trace
  if [stack_trace] {
    mutate {
      add_field => { "has_stack_trace" => true }
    }
  }
}

output {
  if "error" in [tags] {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-errors-%{+YYYY.MM.dd}"
    }
  } else {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-logs-%{+YYYY.MM.dd}"
    }
  }
}

# === Pipeline: Logs Syslog ===

input {
  syslog {
    port => 514
    type => "syslog"
  }
}

filter {
  if [type] == "syslog" {
    grok {
      match => { 
        "message" => "%{SYSLOGBASE} %{GREEDYDATA:syslog_message}" 
      }
    }
    
    date {
      match => [ "timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
      target => "@timestamp"
    }
    
    mutate {
      remove_field => ["message"]
      rename => { "syslog_message" => "message" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "syslog-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs Docker ===

input {
  file {
    path => "/var/lib/docker/containers/*/*.log"
    codec => json
    type => "docker"
  }
}

filter {
  if [type] == "docker" {
    json {
      source => "log"
    }
    
    mutate {
      rename => { "log" => "message" }
    }
    
    # Extraire container ID du path
    grok {
      match => { 
        "path" => "/var/lib/docker/containers/%{DATA:container_id}/%{GREEDYDATA}" 
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs application Java ===

input {
  file {
    path => "/var/log/app/*.log"
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601}"
      negate => true
      what => "previous"
    }
  }
}

filter {
  grok {
    match => { 
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:log_message}" 
    }
  }
  
  date {
    match => [ "timestamp", "yyyy-MM-dd HH:mm:ss,SSS" ]
    target => "@timestamp"
  }
  
  # Détecter stack traces
  if [log_message] =~ /^(\s+at\s|Caused by:)/ {
    mutate {
      add_tag => ["stacktrace"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "java-app-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Métriques système (depuis Metricbeat) ===

input {
  beats {
    port => 5044
    type => "metrics"
  }
}

filter {
  if [type] == "metrics" {
    # Calculer pourcentage CPU
    if [system][cpu] {
      ruby {
        code => "
          total = event.get('[system][cpu][total][pct]')
          if total
            event.set('[system][cpu][total][percent]', (total * 100).round(2))
          end
        "
      }
    }
    
    # Ajouter alertes si seuils dépassés
    if [system][cpu][total][pct] and [system][cpu][total][pct] > 0.9 {
      mutate {
        add_tag => ["high_cpu"]
      }
    }
    
    if [system][memory][used][pct] and [system][memory][used][pct] > 0.9 {
      mutate {
        add_tag => ["high_memory"]
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "metricbeat-%{+YYYY.MM.dd}"
  }
  
  # Alerte si ressources critiques
  if "high_cpu" in [tags] or "high_memory" in [tags] {
    email {
      to => "ops@example.com"
      subject => "Alert: High resource usage on %{host.name}"
      body => "CPU: %{[system][cpu][total][percent]}%\nMemory: %{[system][memory][used][pct]}%"
    }
  }
}


[OK] FILEBEAT - EXEMPLES COMPLETS

# === Configuration: Logs multiples applications ===

filebeat.inputs:

# Application web
- type: log
  enabled: true
  paths:
    - /var/log/webapp/*.log
  fields:
    app: webapp
    environment: production
  fields_under_root: true
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

# API logs
- type: log
  enabled: true
  paths:
    - /var/log/api/*.log
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    app: api
    environment: production
  fields_under_root: true

# Base de données logs
- type: log
  enabled: true
  paths:
    - /var/log/postgresql/*.log
  exclude_lines: ['^DEBUG']
  fields:
    app: database
    type: postgresql
  fields_under_root: true

processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~

output.logstash:
  hosts: ["localhost:5044"]
  loadbalance: true

# === Configuration: Docker containers ===

filebeat.inputs:
- type: container
  enabled: true
  paths:
    - '/var/lib/docker/containers/*/*.log'
  
  processors:
    - add_docker_metadata:
        host: "unix:///var/run/docker.sock"
    
    - decode_json_fields:
        fields: ["message"]
        target: ""
        overwrite_keys: true
    
    # Enrichir avec labels Docker
    - add_fields:
        target: docker
        fields:
          container.labels: ~

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "docker-%{[agent.version]}-%{+yyyy.MM.dd}"

setup.template.name: "docker"
setup.template.pattern: "docker-*"

# === Configuration: Module Nginx avec personnalisation ===

filebeat.modules:
- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log*"]
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log*"]

processors:
  - drop_event:
      when:
        or:
          - equals:
              http.response.status_code: 200
          - equals:
              http.response.status_code: 301
  
  - if:
      equals:
        http.response.status_code: 404
    then:
      - add_tags:
          tags: [not_found]
  
  - if:
        range:
          http.response.status_code:
            gte: 500
    then:
      - add_tags:
          tags: [server_error]

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "nginx-%{[agent.version]}-%{+yyyy.MM.dd}"

# === Configuration: Monitoring Kubernetes ===

filebeat.autodiscover:
  providers:
    - type: kubernetes
      node: ${NODE_NAME}
      hints.enabled: true
      hints.default_config:
        type: container
        paths:
          - /var/log/containers/*${data.kubernetes.container.id}.log

processors:
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      - logs_path:
          logs_path: "/var/log/containers/"
  
  - drop_event:
      when:
        equals:
          kubernetes.namespace: "kube-system"

output.elasticsearch:
  hosts: ["${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}"]
  username: ${ELASTICSEARCH_USERNAME}
  password: ${ELASTICSEARCH_PASSWORD}
  index: "k8s-logs-%{[agent.version]}-%{+yyyy.MM.dd}"


[OK] PATTERNS GROK PERSONNALISÉS

# Créer fichier: /etc/logstash/patterns/custom_patterns

# === Format ===
PATTERN_NAME regex

# === Exemples ===

# Log application custom
MYAPP_LOG %{TIMESTAMP_ISO8601:timestamp} \| %{LOGLEVEL:level} \| %{DATA:module} \| %{GREEDYDATA:message}

# Log avec user ID
MYAPP_USER_LOG \[%{DATA:user_id}\] %{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA:message}

# Format de transaction
TRANSACTION_ID TXN-%{INT:transaction_id}
TRANSACTION_LOG %{TRANSACTION_ID} - %{WORD:status} - %{NUMBER:amount:float} %{WORD:currency}

# Email pattern
EMAIL_ADDR [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}

# IP avec port
IPPORT %{IP:ip}:%{INT:port}

# === Utiliser dans Logstash ===

filter {
  grok {
    patterns_dir => ["/etc/logstash/patterns"]
    match => { 
      "message" => "%{MYAPP_LOG}" 
    }
  }
}


[OK] INDEX LIFECYCLE MANAGEMENT - ILM (GUIDE COMPLET)

# === QU'EST-CE QUE ILM? ===

# ILM = Index Lifecycle Management
# Gestion automatique du cycle de vie des index

# PROBLÈME SANS ILM:
# - Index logs grossissent indéfiniment
# - Disque plein après quelques mois
# - Recherches lentes sur gros index
# - Suppression manuelle fastidieuse

# SOLUTION AVEC ILM:
# - Rotation automatique (rollover)
# - Déplacement vers stockage moins cher (tiers)
# - Compression automatique
# - Suppression automatique après X jours
# - Tout automatisé!

# === PHASES DU CYCLE DE VIE ===

# HOT -> WARM -> COLD -> FROZEN -> DELETE

# 1. HOT (Chaud)
# - Données actives, récentes
# - Écriture + lecture fréquentes
# - SSD rapide, beaucoup de RAM
# - Exemple: Logs dernières 24h

# 2. WARM (Tiède)
# - Données moins actives
# - Lecture occasionnelle, pas d'écriture
# - Peut être sur disques plus lents
# - Compression, moins de réplicas
# - Exemple: Logs 1-7 jours

# 3. COLD (Froid)
# - Données rarement accédées
# - Lecture rare, archivage
# - Disque lent OK
# - Fortement compressé
# - Exemple: Logs 7-30 jours

# 4. FROZEN (Gelé) - Elasticsearch 7.12+
# - Données très rarement accédées
# - Searchable snapshots (dans S3, etc.)
# - Presque pas de RAM/disque local
# - Exemple: Logs 30-90 jours

# 5. DELETE (Suppression)
# - Supprime définitivement
# - Exemple: Logs > 90 jours

# === ANALOGIE ===

# Comme ranger une bibliothèque:
# HOT: Bureau (livres lus tous les jours)
# WARM: Étagère proche (livres lus parfois)
# COLD: Grenier (livres rarement lus)
# FROZEN: Stockage externe (archives)
# DELETE: Poubelle (vieux livres jetés)

# === CRÉER POLITIQUE ILM ===

# EXEMPLE SIMPLE: Suppression après 30 jours

curl -X PUT "localhost:9200/_ilm/policy/logs_policy" \
  -H 'Content-Type: application/json' -d'{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

# Explication:
# - Phase HOT:
#   * min_age: 0ms = immédiatement en hot
#   * rollover: Créer nouvel index si:
#     - Taille > 50GB OU
#     - Âge > 1 jour
#
# - Phase DELETE:
#   * min_age: 30d = 30 jours après création
#   * delete: Supprime index

# EXEMPLE COMPLET: Toutes les phases

curl -X PUT "localhost:9200/_ilm/policy/logs_policy" \
  -H 'Content-Type: application/json' -d'{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d",
            "max_docs": 10000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "set_priority": {
            "priority": 0
          },
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

# Explication détaillée:

# PHASE HOT:
# - rollover: Conditions pour créer nouvel index
#   * max_size: 50GB (taille maximum)
#   * max_age: 1d (âge maximum)
#   * max_docs: 10M (nombre docs max)
#   [ATTENTION] Si UNE condition atteinte -> rollover
#
# - set_priority: Priorité recovery (100 = haute)
#   En cas crash, récupère ces index en premier

# PHASE WARM (après 7 jours):
# - forcemerge: Optimise segments
#   * max_num_segments: 1 = fusionne en 1 segment
#   Plus rapide recherche, moins d'espace
#
# - shrink: Réduit nombre de shards
#   * number_of_shards: 1
#   Moins d'overhead si moins de données actives
#
# - set_priority: 50 (priorité moyenne)

# PHASE COLD (après 30 jours):
# - set_priority: 0 (priorité basse)
# - freeze: Rend index read-only, libère mémoire

# PHASE DELETE (après 90 jours):
# - delete: Supprime complètement index

# === ACTIONS DISPONIBLES ===

# ROLLOVER (Créer nouvel index):
"rollover": {
  "max_size": "50GB",      # Taille maximum
  "max_age": "7d",         # Âge maximum
  "max_docs": 10000000,    # Nombre docs maximum
  "max_primary_shard_size": "30GB"  # Taille shard primaire max
}

# FORCEMERGE (Optimiser segments):
"forcemerge": {
  "max_num_segments": 1    # Nombre segments final (1 = optimal)
}

# SHRINK (Réduire shards):
"shrink": {
  "number_of_shards": 1    # Nouveau nombre de shards
}

# ALLOCATE (Changer allocation):
"allocate": {
  "number_of_replicas": 1,           # Changer nombre replicas
  "require": {
    "box_type": "warm"               # Attribut nœud requis
  }
}

# SET_PRIORITY (Priorité recovery):
"set_priority": {
  "priority": 100          # 0-100, plus haut = prioritaire
}

# FREEZE (Geler - Elasticsearch 7.x):
"freeze": {}               # Read-only, libère mémoire

# SEARCHABLE_SNAPSHOT (ES 7.12+):
"searchable_snapshot": {
  "snapshot_repository": "my_backup"
}

# READONLY (Lecture seule):
"readonly": {}

# DELETE (Supprimer):
"delete": {}

# WAIT_FOR_SNAPSHOT (Attendre backup avant suppression):
"wait_for_snapshot": {
  "policy": "daily-snapshots"
}

# === APPLIQUER ILM À INDEX TEMPLATE ===

# Étape 1: Créer index template avec ILM

curl -X PUT "localhost:9200/_index_template/logs_template" \
  -H 'Content-Type: application/json' -d'{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs_policy",
      "index.lifecycle.rollover_alias": "logs"
    },
    "mappings": {
      "properties": {
        "@timestamp": {"type": "date"},
        "message": {"type": "text"},
        "level": {"type": "keyword"}
      }
    }
  }
}'

# Paramètres importants:
# - index.lifecycle.name: Quelle politique ILM utiliser
# - index.lifecycle.rollover_alias: Alias pour rollover

# Étape 2: Créer index initial avec alias

curl -X PUT "localhost:9200/logs-000001" \
  -H 'Content-Type: application/json' -d'{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}'

# Explication:
# - logs-000001: Nom index (000001 = numéro)
# - alias "logs": Nom pour écrire
# - is_write_index: true = index actif pour écriture

# Étape 3: Écrire dans alias

# Applications écrivent dans "logs" (alias), pas "logs-000001"
curl -X POST "localhost:9200/logs/_doc" \
  -H 'Content-Type: application/json' -d'{
  "message": "Test log",
  "level": "INFO",
  "@timestamp": "2024-01-15T10:00:00Z"
}'

# Quand rollover:
# - Nouvel index: logs-000002
# - Alias "logs" pointe vers logs-000002
# - logs-000001 devient read-only

# === VOIR ÉTAT ILM ===

# Lister toutes politiques:
curl -X GET "localhost:9200/_ilm/policy?pretty"

# Voir politique spécifique:
curl -X GET "localhost:9200/_ilm/policy/logs_policy?pretty"

# Expliquer état ILM d'un index:
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# Réponse:
{
  "indices": {
    "logs-000001": {
      "index": "logs-000001",
      "managed": true,                    # Géré par ILM
      "policy": "logs_policy",            # Quelle politique
      "phase": "hot",                     # Phase actuelle
      "action": "rollover",               # Action actuelle
      "step": "check-rollover-ready",     # Étape actuelle
      "age": "1d",                        # Âge index
      "phase_time_millis": 1705318245000  # Quand entré phase
    }
  }
}

# === GÉRER ILM ===

# ARRÊTER ILM (maintenance):
curl -X POST "localhost:9200/_ilm/stop?pretty"

# REDÉMARRER ILM:
curl -X POST "localhost:9200/_ilm/start?pretty"

# STATUT ILM:
curl -X GET "localhost:9200/_ilm/status?pretty"

# Réponse:
{
  "operation_mode": "RUNNING"    # RUNNING, STOPPING, STOPPED
}

# FORCER ROLLOVER MANUEL (test):
curl -X POST "localhost:9200/logs/_rollover?pretty"

# Crée logs-000002 si conditions rollover atteintes

# RÉESSAYER ACTION ÉCHOUÉE:
curl -X POST "localhost:9200/logs-000001/_ilm/retry?pretty"

# Si action ILM a échoué, réessaye

# RETIRER INDEX DE ILM:
curl -X POST "localhost:9200/logs-000001/_ilm/remove?pretty"

# Index n'est plus géré par ILM

# CHANGER POLITIQUE INDEX:
curl -X PUT "localhost:9200/logs-000001/_settings" \
  -H 'Content-Type: application/json' -d'{
  "index.lifecycle.name": "new_policy"
}'

# === EXEMPLES PRATIQUES ===

# EXEMPLE 1: Logs application (rétention 30j)

{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

# Usage:
# - Rollover quotidien (ou 50GB)
# - Suppression après 30 jours
# - Simple, efficace pour logs standards

# EXEMPLE 2: Logs production (HA, optimisé)

{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "30GB",
            "max_age": "1d"
          },
          "set_priority": {"priority": 100}
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "allocate": {
            "number_of_replicas": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          },
          "set_priority": {"priority": 50}
        }
      },
      "cold": {
        "min_age": "7d",
        "actions": {
          "allocate": {
            "number_of_replicas": 0
          },
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "wait_for_snapshot": {
            "policy": "daily-snapshots"
          },
          "delete": {}
        }
      }
    }
  }
}

# Usage:
# - Hot: 3 jours, haute priorité
# - Warm: 3-7 jours, optimisé, 1 replica
# - Cold: 7-90 jours, gelé, 0 replica
# - Delete: Après 90j (après snapshot)

# EXEMPLE 3: Métriques (peu d'écriture, beaucoup de données)

{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "100GB",
            "max_age": "7d"
          }
        }
      },
      "warm": {
        "min_age": "14d",
        "actions": {
          "forcemerge": {"max_num_segments": 1},
          "shrink": {"number_of_shards": 1}
        }
      },
      "delete": {
        "min_age": "180d",
        "actions": {"delete": {}}
      }
    }
  }
}

# Usage:
# - Rollover hebdomadaire ou 100GB
# - Warm après 14j: optimisé, 1 shard
# - Rétention 6 mois

# === DATA TIERS (ARCHITECTURE HOT/WARM/COLD) ===

# CONFIGURER NŒUDS PAR TIER:

# Nœud HOT (SSD rapide):
# elasticsearch.yml:
node.roles: [ data_hot, data_content ]

# Nœud WARM (SSD standard):
node.roles: [ data_warm ]

# Nœud COLD (HDD lent mais grand):
node.roles: [ data_cold ]

# ILM déplace automatiquement entre tiers!

# Politique utilisant tiers:
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {"max_age": "1d"}
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "allocate": {
            "require": {"data": "warm"}
          },
          "forcemerge": {"max_num_segments": 1}
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": {"data": "cold"}
          }
        }
      }
    }
  }
}

# === MONITORING ILM ===

# VIA KIBANA:
# Stack Management > Index Lifecycle Policies
# - Liste politiques
# - État de chaque index
# - Histogramme phases

# VIA API:
# Voir tous index gérés par ILM:
curl -X GET "localhost:9200/*/_ilm/explain?pretty"

# Filtrer par phase:
curl -X GET "localhost:9200/*/_ilm/explain?only_errors=true&pretty"

# Voir index avec erreurs ILM seulement

# === DÉPANNAGE ILM ===

# PROBLÈME: Index ne fait pas rollover

# 1. Vérifier alias is_write_index:
curl -X GET "localhost:9200/_alias/logs?pretty"

# Doit avoir "is_write_index": true

# 2. Vérifier conditions rollover:
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# Voir si conditions atteintes

# 3. Forcer rollover (test):
curl -X POST "localhost:9200/logs/_rollover?pretty"

# PROBLÈME: Index bloqué dans une phase

# Voir détails erreur:
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# Champ "step_info" contient erreur

# Réessayer:
curl -X POST "localhost:9200/logs-000001/_ilm/retry?pretty"

# PROBLÈME: Action forcemerge échoue

# Cause possible: Pas assez d'espace disque
# Vérifier:
curl -X GET "localhost:9200/_cat/allocation?v"

# Solution: Libérer espace ou augmenter disque

# PROBLÈME: Rollover crée index mais alias pas mis à jour

# Recréer alias manuellement:
curl -X POST "localhost:9200/_aliases" \
  -H 'Content-Type: application/json' -d'{
  "actions": [
    {
      "add": {
        "index": "logs-000002",
        "alias": "logs",
        "is_write_index": true
      }
    },
    {
      "add": {
        "index": "logs-000001",
        "alias": "logs",
        "is_write_index": false
      }
    }
  ]
}'

# === BONNES PRATIQUES ILM ===

# 1. TOUJOURS utiliser ILM en production
# Évite gestion manuelle fastidieuse

# 2. DIMENSIONNER rollover correctement
# - Trop petit (100MB): Beaucoup d'index, overhead
# - Trop grand (500GB): Recherches lentes
# - Idéal: 20-50GB par index

# 3. ADAPTER PHASES au cas d'usage
# Logs non-critiques:
# - Hot: 1 jour
# - Delete: 7 jours
#
# Logs production:
# - Hot: 3 jours
# - Warm: 7 jours
# - Cold: 30 jours
# - Delete: 90 jours
#
# Données analytiques:
# - Hot: 7 jours
# - Warm: 30 jours
# - Cold: 365 jours
# - Delete: Jamais (ou très long)

# 4. UTILISER wait_for_snapshot avant delete
# S'assure backup existe

# 5. TESTER politiques en dev
# Rollover manuel pour tester:
curl -X POST "localhost:9200/logs/_rollover?pretty"

# 6. MONITORER ILM régulièrement
# Vérifier pas d'erreurs:
curl -X GET "localhost:9200/*/_ilm/explain?only_errors=true"

# 7. SET_PRIORITY pour recovery
# Hot: 100, Warm: 50, Cold: 0
# Récupère données importantes d'abord

# 8. FORCEMERGE en phase warm
# Optimise avant archivage
# Jamais en hot! (encore des écritures)

# 9. SHRINK si trop de shards
# 5 shards en hot -> 1 shard en warm
# Réduit overhead

# 10. DOCUMENTER rétentions
# Politique claire écrite
# Conformité réglementaire

# === EXEMPLES COMMANDES COMPLÈTES ===

# Setup complet ILM pour logs:

# 1. Créer politique
curl -X PUT "localhost:9200/_ilm/policy/logs-30d-policy" \
  -H 'Content-Type: application/json' -d'{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {"delete": {}}
      }
    }
  }
}'

# 2. Créer template
curl -X PUT "localhost:9200/_index_template/logs" \
  -H 'Content-Type: application/json' -d'{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs-30d-policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}'

# 3. Créer index initial
curl -X PUT "localhost:9200/logs-000001" \
  -H 'Content-Type: application/json' -d'{
  "aliases": {
    "logs": {"is_write_index": true}
  }
}'

# 4. Écrire données
curl -X POST "localhost:9200/logs/_doc" \
  -H 'Content-Type: application/json' -d'{
  "message": "Application started",
  "level": "INFO",
  "@timestamp": "2024-01-15T10:00:00Z"
}'

# 5. Vérifier
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# Tout automatisé à partir de maintenant!
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "searchable_snapshot": {
            "snapshot_repository": "my_backup"
          },
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
'

# === Appliquer politique à index template ===

curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs_policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}
'

# === Créer index initial avec alias ===

curl -X PUT "localhost:9200/logs-000001?pretty" -H 'Content-Type: application/json' -d'
{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}
'

# === Voir statut ILM ===

# Lister politiques
curl -X GET "localhost:9200/_ilm/policy?pretty"

# Voir politique spécifique
curl -X GET "localhost:9200/_ilm/policy/logs_policy?pretty"

# Expliquer état ILM d'un index
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# === Gestion ILM ===

# Arrêter ILM
curl -X POST "localhost:9200/_ilm/stop?pretty"

# Démarrer ILM
curl -X POST "localhost:9200/_ilm/start?pretty"

# Statut ILM
curl -X GET "localhost:9200/_ilm/status?pretty"

# Forcer rollover manuel
curl -X POST "localhost:9200/logs/_rollover?pretty"

# Réessayer action échouée
curl -X POST "localhost:9200/logs-000001/_ilm/retry?pretty"

# Supprimer index de ILM
curl -X POST "localhost:9200/logs-000001/_ilm/remove?pretty"


[OK] SÉCURITÉ - X-PACK (GUIDE COMPLET DÉBUTANT)

# === POURQUOI LA SÉCURITÉ? ===

# SANS SÉCURITÉ:
# - N'importe qui peut accéder à Elasticsearch
# - Pas de mot de passe
# - Pas de chiffrement des données
# - Pas d'audit des accès
# - DANGEREUX en production!

# AVEC X-PACK SECURITY:
# - Authentification (username/password)
# - Chiffrement SSL/TLS
# - Contrôle d'accès (qui peut faire quoi)
# - Audit trail (qui a fait quoi et quand)
# - Protection données sensibles

# ANALOGIE:
# Sans sécurité = Maison sans serrure, porte ouverte
# Avec sécurité = Maison avec serrures, alarme, caméras

# === VERSION ELASTICSEARCH ===

# Elasticsearch 8.x:
# - Sécurité ACTIVÉE par défaut
# - Génère automatiquement mots de passe
# - Configure SSL automatiquement

# Elasticsearch 7.x et avant:
# - Sécurité DÉSACTIVÉE par défaut
# - À activer manuellement

# Cette section couvre Elasticsearch 8.x

# === ACTIVER SÉCURITÉ (Si désactivée) ===

# Dans elasticsearch.yml:
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

# Redémarrer:
sudo systemctl restart elasticsearch

# === GÉNÉRER CERTIFICATS SSL ===

# POURQUOI SSL/TLS?
# - Chiffre communications (évite écoute réseau)
# - Vérifie identité serveurs (évite man-in-the-middle)

# ÉTAPE 1: Créer Certificate Authority (CA)
# CA = Autorité qui signe les certificats

cd /usr/share/elasticsearch
sudo bin/elasticsearch-certutil ca

# Questions:
# - Output file: elastic-stack-ca.p12 (Enter = défaut OK)
# - Password: [Enter pour pas de password, ou tape mot de passe]

# Génère: elastic-stack-ca.p12

# ÉTAPE 2: Créer certificats pour nœuds

sudo bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12

# Questions:
# - CA password: [Si défini à l'étape 1]
# - Output file: elastic-certificates.p12 (Enter = défaut OK)
# - Password: [Enter ou tape mot de passe]

# Génère: elastic-certificates.p12

# ÉTAPE 3: Copier certificats

sudo cp elastic-certificates.p12 /etc/elasticsearch/
sudo chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-certificates.p12
sudo chmod 660 /etc/elasticsearch/elastic-certificates.p12

# ÉTAPE 4: Configurer dans elasticsearch.yml

# SSL Transport (communication entre nœuds)
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.client_authentication: required
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12

# Si certificat a password:
# Stocker password dans keystore Elasticsearch
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password

# SSL HTTP (API REST, connexions clients)
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates.p12
xpack.security.http.ssl.truststore.path: elastic-certificates.p12

# ÉTAPE 5: Redémarrer
sudo systemctl restart elasticsearch

# ÉTAPE 6: Tester connexion HTTPS
curl -X GET "https://localhost:9200" -u elastic:password -k

# -k: Ignore erreur certificat auto-signé (dev seulement!)

# === CONFIGURER MOTS DE PASSE ===

# Elasticsearch 8.x génère automatiquement à l'installation
# Sinon, configurer manuellement:

# MÉTHODE 1: Interactive (recommandé)
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

# Demande mot de passe pour chaque utilisateur:
# - elastic (superuser)
# - kibana_system (pour Kibana)
# - logstash_system (pour Logstash)
# - beats_system (pour Beats)
# - apm_system (pour APM)
# - remote_monitoring_user (pour monitoring)

# Entre mots de passe forts!
# Exemple: gH8$kL2@pR9!mN4%

# MÉTHODE 2: Automatique (génère mots de passe aléatoires)
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto

# Affiche mots de passe générés:
# Changed password for user apm_system
# PASSWORD apm_system = abc123xyz...
# 
# Changed password for user kibana_system
# PASSWORD kibana_system = def456uvw...
# 
# ...

# [ATTENTION] COPIER ET SAUVEGARDER CES MOTS DE PASSE!

# === UTILISATEURS INTÉGRÉS ===

# 1. ELASTIC
# - Superuser (admin complet)
# - Tous les droits
# - À utiliser avec précaution!

# 2. KIBANA_SYSTEM
# - Pour connexion Kibana -> Elasticsearch
# - Droits minimaux pour fonctionnement Kibana
# - Configurer dans kibana.yml

# 3. LOGSTASH_SYSTEM
# - Pour Logstash -> Elasticsearch
# - Monitoring et stats
# - Pas pour envoyer données (créer user custom)

# 4. BEATS_SYSTEM
# - Pour Beats -> Elasticsearch
# - Monitoring seulement
# - Pas pour envoyer données (créer user custom)

# 5. APM_SYSTEM
# - Pour APM Server
# - Monitoring et events

# 6. REMOTE_MONITORING_USER
# - Pour Metricbeat monitoring
# - Lecture métriques

# === CHANGER MOT DE PASSE ===

# Via API:
curl -X POST "https://localhost:9200/_security/user/elastic/_password" \
  -u elastic:ancien_password \
  -H 'Content-Type: application/json' \
  -d'{"password":"nouveau_password"}' \
  -k

# Via Kibana:
# Stack Management > Security > Users > elastic > Edit > Change password

# === CRÉER UTILISATEUR PERSONNALISÉ ===

# POURQUOI?
# - Ne pas utiliser elastic pour applications
# - Principe du moindre privilège (least privilege)
# - Traçabilité (audit)

# VIA API:

curl -X POST "https://localhost:9200/_security/user/mon_utilisateur" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d'{
    "password": "mot_de_passe_fort",
    "roles": ["kibana_admin", "monitoring_user"],
    "full_name": "Jean Dupont",
    "email": "jean@example.com",
    "metadata": {
      "department": "IT",
      "team": "Platform"
    }
  }' \
  -k

# VIA KIBANA:
# 1. Stack Management > Security > Users
# 2. "Create user"
# 3. Remplir formulaire:
#    - Username: logstash_writer
#    - Password: ********
#    - Full name: Logstash Writer
#    - Email: logstash@example.com
#    - Roles: logstash_writer (créer rôle d'abord)
# 4. "Create user"

# === RÔLES PRÉDÉFINIS ===

# SUPERUSER:
# - Accès complet (DANGEREUX!)
# - elastic user

# KIBANA_ADMIN:
# - Admin Kibana complet
# - Gérer dashboards, visualizations
# - Pas accès Elasticsearch direct

# KIBANA_USER:
# - Utilisateur Kibana standard
# - Voir dashboards
# - Pas créer/modifier

# MONITORING_USER:
# - Voir monitoring Stack
# - Lecture seule métriques

# INGEST_ADMIN:
# - Gérer pipelines Ingest
# - Créer/modifier/supprimer pipelines

# LOGSTASH_ADMIN:
# - Gérer pipelines Logstash
# - Config centrale pipelines

# BEATS_ADMIN:
# - Gérer configuration Beats
# - Central management Beats

# REPORTING_USER:
# - Générer rapports
# - Export PDF/CSV

# VIEWER:
# - Lecture seule
# - Voir données, pas modifier

# EDITOR:
# - Créer/modifier saved objects
# - Dashboards, visualizations, searches

# === CRÉER RÔLE PERSONNALISÉ ===

# EXEMPLE: Rôle pour Logstash écrire dans logs-*

# VIA API:

curl -X POST "https://localhost:9200/_security/role/logstash_writer" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d'{
    "cluster": ["monitor", "manage_index_templates", "manage_ilm"],
    "indices": [
      {
        "names": ["logs-*"],
        "privileges": ["create_index", "write", "auto_configure"]
      }
    ]
  }' \
  -k

# Explication:
# - cluster: Permissions niveau cluster
#   * monitor: Voir stats cluster
#   * manage_index_templates: Gérer templates
#   * manage_ilm: Gérer ILM policies
#
# - indices: Permissions niveau index
#   * names: Quels index (pattern possible)
#   * privileges:
#     - create_index: Créer index automatiquement
#     - write: Écrire documents
#     - auto_configure: Auto-config datastreams

# VIA KIBANA:
# 1. Stack Management > Security > Roles
# 2. "Create role"
# 3. Role name: logs_reader
# 4. Cluster privileges: (aucun ou monitor)
# 5. Index privileges:
#    - Indices: logs-*
#    - Privileges: read, view_index_metadata
# 6. "Create role"

# EXEMPLE: Rôle lecture seule sur logs

curl -X POST "https://localhost:9200/_security/role/logs_reader" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "cluster": ["monitor"],
    "indices": [
      {
        "names": ["logs-*"],
        "privileges": ["read", "view_index_metadata"]
      }
    ]
  }' -k

# EXEMPLE: Rôle avec Field Level Security

curl -X POST "https://localhost:9200/_security/role/limited_user" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "indices": [
      {
        "names": ["users"],
        "privileges": ["read"],
        "field_security": {
          "grant": ["name", "email", "age"],
          "except": ["password", "ssn", "credit_card"]
        }
      }
    ]
  }' -k

# Utilisateur avec ce rôle:
# - Voit: name, email, age
# - NE voit PAS: password, ssn, credit_card

# EXEMPLE: Rôle avec Document Level Security

curl -X POST "https://localhost:9200/_security/role/sales_team" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "indices": [
      {
        "names": ["orders"],
        "privileges": ["read"],
        "query": "{\"term\": {\"department\": \"sales\"}}"
      }
    ]
  }' -k

# Utilisateur avec ce rôle:
# - Voit seulement documents où department=sales
# - Autres documents invisibles

# === ASSIGNER RÔLES À UTILISATEUR ===

# Via API:
curl -X POST "https://localhost:9200/_security/user/jean" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "password": "password123",
    "roles": ["logs_reader", "kibana_user"]
  }' -k

# Via Kibana:
# Users > jean > Edit > Roles > Cocher: logs_reader, kibana_user > Save

# === CONFIGURER KIBANA AVEC SÉCURITÉ ===

# Fichier: /etc/kibana/kibana.yml

# Connexion à Elasticsearch:
elasticsearch.hosts: ["https://localhost:9200"]
elasticsearch.username: "kibana_system"
elasticsearch.password: "password_kibana_system"

# SSL/TLS:
elasticsearch.ssl.verificationMode: certificate
elasticsearch.ssl.certificateAuthorities: ["/path/to/ca.crt"]

# Ou désactiver vérif (DEV SEULEMENT!):
elasticsearch.ssl.verificationMode: none

# Redémarrer Kibana:
sudo systemctl restart kibana

# Accéder Kibana:
# http://localhost:5601
# Login: elastic (ou autre user)
# Password: mot_de_passe

# === CONFIGURER LOGSTASH AVEC SÉCURITÉ ===

# Dans pipeline Logstash:

output {
  elasticsearch {
    hosts => ["https://localhost:9200"]
    user => "logstash_writer"
    password => "password_logstash"
    ssl => true
    cacert => "/path/to/ca.crt"
    index => "logs-%{+YYYY.MM.dd}"
  }
}

# Ou sans vérif SSL (DEV SEULEMENT!):
ssl_certificate_verification => false

# === CONFIGURER FILEBEAT AVEC SÉCURITÉ ===

# Dans filebeat.yml:

output.elasticsearch:
  hosts: ["https://localhost:9200"]
  username: "filebeat_writer"
  password: "password_filebeat"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

setup.kibana:
  host: "https://localhost:5601"
  username: "elastic"
  password: "password_elastic"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

# Ou sans vérif (DEV SEULEMENT!):
ssl.verification_mode: none

# === API KEYS (Alternative mots de passe) ===

# POURQUOI API KEYS?
# - Plus sécurisé que passwords
# - Peut être révoqué facilement
# - Pas besoin changer password
# - Scope limité (permissions spécifiques)

# CRÉER API KEY:

curl -X POST "https://localhost:9200/_security/api_key" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "name": "my-api-key",
    "expiration": "1d",
    "role_descriptors": {
      "logs_writer": {
        "cluster": ["monitor"],
        "index": [
          {
            "names": ["logs-*"],
            "privileges": ["create_index", "write"]
          }
        ]
      }
    }
  }' -k

# Réponse:
{
  "id": "abc123",
  "name": "my-api-key",
  "api_key": "xyz789...",
  "encoded": "base64_encoded_key"
}

# UTILISER API KEY:

# Méthode 1: Encoded (plus simple)
curl -X GET "https://localhost:9200/_cluster/health" \
  -H "Authorization: ApiKey base64_encoded_key" \
  -k

# Méthode 2: ID + Key
curl -X GET "https://localhost:9200/_cluster/health" \
  -H "Authorization: ApiKey $(echo -n 'abc123:xyz789' | base64)" \
  -k

# Dans Filebeat:
output.elasticsearch:
  hosts: ["https://localhost:9200"]
  api_key: "abc123:xyz789"

# Dans Logstash:
output {
  elasticsearch {
    hosts => ["https://localhost:9200"]
    api_key => "abc123:xyz789"
  }
}

# LISTER API KEYS:
curl -X GET "https://localhost:9200/_security/api_key" \
  -u elastic:password -k

# RÉVOQUER API KEY:
curl -X DELETE "https://localhost:9200/_security/api_key" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "id": "abc123"
  }' -k

# === AUDIT LOGGING ===

# POURQUOI AUDIT?
# - Tracer qui a fait quoi
# - Conformité (RGPD, SOX, etc.)
# - Investigation incidents sécurité
# - Détection comportements suspects

# ACTIVER AUDIT (Nécessite licence Gold+):

# Dans elasticsearch.yml:
xpack.security.audit.enabled: true

# Types d'événements audités:
# - Authentification (login success/fail)
# - Accès refusé
# - Modification utilisateurs/rôles
# - Requêtes système

# Logs audit dans:
# /var/log/elasticsearch/*_audit.json

# Format JSON:
{
  "@timestamp": "2024-01-15T10:30:00.000Z",
  "event.type": "authentication_success",
  "user.name": "jean",
  "origin.address": "192.168.1.100",
  "request.method": "POST",
  "url.path": "/_search"
}

# Indexer logs audit dans Elasticsearch:
xpack.security.audit.enabled: true
xpack.security.audit.outputs: [ index, logfile ]

# Crée index: .security_audit_log-*

# === ESPACES KIBANA (ISOLATION) ===

# Spaces = Isolation dashboards/visualizations par équipe

# CRÉER SPACE:
# Kibana > Stack Management > Spaces > Create space
# - Name: Marketing
# - Initials: MK
# - Color: Blue
# - Description: Marketing team dashboards

# PERMISSIONS PAR SPACE:

# Rôle avec accès seulement space Marketing:
curl -X POST "https://localhost:9200/_security/role/marketing_user" \
  -u elastic:password \
  -H 'Content-Type: application/json' -d'{
    "kibana": [
      {
        "spaces": ["marketing"],
        "base": ["read"],
        "feature": {
          "dashboard": ["read"]
        }
      }
    ]
  }' -k

# === CHIFFREMENT DES DONNÉES ===

# 1. IN-TRANSIT (pendant transmission):
# [OK] SSL/TLS (configuré plus haut)
# Chiffre données entre:
# - Client <-> Elasticsearch
# - Elasticsearch <-> Kibana
# - Elasticsearch <-> Logstash
# - Nœud <-> Nœud Elasticsearch

# 2. AT-REST (sur disque):
# Chiffrement disque OS:
# - Linux: LUKS, dm-crypt
# - Windows: BitLocker
# - Cloud: EBS encryption (AWS), etc.

# Elasticsearch lui-même ne chiffre pas données au repos
# Utiliser chiffrement disque OS

# === MEILLEURES PRATIQUES SÉCURITÉ ===

# 1. TOUJOURS activer sécurité en production
xpack.security.enabled: true

# 2. TOUJOURS utiliser SSL/TLS
xpack.security.http.ssl.enabled: true

# 3. MOTS DE PASSE FORTS
# - 12+ caractères
# - Majuscules, minuscules, chiffres, symboles
# - Pas de mots dictionnaire

# 4. PRINCIPE MOINDRE PRIVILÈGE
# - Créer rôles spécifiques
# - Pas utiliser elastic partout
# - Limiter permissions au minimum nécessaire

# 5. API KEYS pour applications
# - Plus facile révoquer
# - Permissions limitées

# 6. AUDIT LOGGING
# - Tracer accès
# - Détecter anomalies

# 7. ROTATION CERTIFICATS
# - Renouveler avant expiration
# - Tester process de renouvellement

# 8. FIREWALL
# - Limiter accès réseau
# - Seulement IPs autorisées

# 9. BACKUP CONFIGURATION
# - Sauvegarder certificats
# - Sauvegarder users/roles

# 10. MONITORING SÉCURITÉ
# - Surveiller logins échoués
# - Alertes sur comportements suspects

# === DÉPANNAGE SÉCURITÉ ===

# PROBLÈME: "Unable to authenticate user"

# 1. Vérifier password:
curl -X GET "https://localhost:9200/_security/_authenticate" \
  -u username:password -k

# 2. Vérifier utilisateur existe:
curl -X GET "https://localhost:9200/_security/user/username" \
  -u elastic:password -k

# 3. Vérifier rôles:
curl -X GET "https://localhost:9200/_security/user/username" \
  -u elastic:password -k | jq '.roles'

# PROBLÈME: "SSL connection error"

# 1. Vérifier certificat valide:
openssl x509 -in cert.pem -text -noout

# 2. Tester connexion SSL:
openssl s_client -connect localhost:9200

# 3. Vérifier chemin certificat dans config

# 4. Permissions certificat:
ls -la /etc/elasticsearch/elastic-certificates.p12
# Doit être lisible par elasticsearch user

# PROBLÈME: "Authorization required"

# Vérifier permissions rôle:
curl -X GET "https://localhost:9200/_security/role/role_name" \
  -u elastic:password -k

# PROBLÈME: Kibana ne se connecte pas

# 1. Vérifier kibana.yml:
elasticsearch.username: "kibana_system"
elasticsearch.password: "correct_password"

# 2. Tester connexion:
curl -X GET "https://localhost:9200" \
  -u kibana_system:password -k

# 3. Voir logs Kibana:
sudo tail -f /var/log/kibana/kibana.log

# === COMMANDES UTILES ===

# Lister tous users:
curl -X GET "https://localhost:9200/_security/user" \
  -u elastic:password -k

# Lister tous rôles:
curl -X GET "https://localhost:9200/_security/role" \
  -u elastic:password -k

# Voir qui je suis:
curl -X GET "https://localhost:9200/_security/_authenticate" \
  -u username:password -k

# Vérifier privilèges user:
curl -X GET "https://localhost:9200/_security/user/_privileges" \
  -u username:password -k

# Supprimer user:
curl -X DELETE "https://localhost:9200/_security/user/username" \
  -u elastic:password -k

# Supprimer rôle:
curl -X DELETE "https://localhost:9200/_security/role/role_name" \
  -u elastic:password -k

# Désactiver user (sans supprimer):
curl -X PUT "https://localhost:9200/_security/user/username/_disable" \
  -u elastic:password -k

# Réactiver user:
curl -X PUT "https://localhost:9200/_security/user/username/_enable" \
  -u elastic:password -k


[OK] MONITORING & PERFORMANCE (GUIDE PRATIQUE)

# === POURQUOI MONITORER? ===

# Détecter problèmes AVANT qu'ils deviennent critiques:
# - Disque bientôt plein -> Agir maintenant
# - Recherches lentes -> Optimiser
# - CPU élevé -> Investiguer
# - Mémoire saturée -> Ajuster heap

# ANALOGIE:
# Comme tableau de bord voiture:
# - Jauge essence -> Niveau disque
# - Compte-tours -> CPU usage
# - Température -> Heap usage
# - Voyants -> Alertes

# === MÉTRIQUES CLÉS À SURVEILLER ===

# 1. CLUSTER HEALTH
# Green, Yellow, Red
# [ATTENTION] Yellow/Red = Investigation immédiate

# 2. HEAP USAGE (Mémoire JVM)
# < 75%: OK
# 75-85%: Attention
# > 85%: DANGER (GC thrashing)

# 3. DISK USAGE
# < 85%: OK
# 85-90%: Attention (watermark low)
# 90-95%: Critique (watermark high)
# > 95%: DANGER (flood stage, read-only!)

# 4. CPU USAGE
# < 70%: OK
# 70-90%: Attention
# > 90%: Saturé

# 5. SEARCH LATENCY
# Temps moyen requête
# < 100ms: Excellent
# 100-500ms: Bon
# > 1s: Problème

# 6. INDEXING LATENCY
# Temps indexation document
# < 10ms: Excellent
# 10-50ms: Bon
# > 100ms: Problème

# 7. REJECTED REQUESTS
# Requêtes refusées (thread pools pleins)
# > 0: Problème capacité

# === COMMANDES MONITORING ===

# SANTÉ CLUSTER:
curl -X GET "localhost:9200/_cluster/health?pretty"

# Réponse rapide:
{
  "status": "green",              # <- PRINCIPAL INDICATEUR
  "number_of_nodes": 3,
  "active_shards": 50,
  "unassigned_shards": 0          # Doit être 0!
}

# STATS NŒUDS (Vue d'ensemble):
curl -X GET "localhost:9200/_nodes/stats?pretty"

# Trop verbose! Filtrer:
curl -X GET "localhost:9200/_nodes/stats/jvm,os,process,fs?pretty"

# Résumé lisible:
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m,disk.used_percent"

# Exemple sortie:
# name    heap.percent ram.percent cpu load_1m disk.used_percent
# node-1  45          60          15  0.50    65
# node-2  52          58          12  0.45    63
# node-3  48          62          18  0.55    67

# ALLOCATION SHARDS:
curl -X GET "localhost:9200/_cat/allocation?v"

# Voir répartition disque par nœud

# SHARDS DÉTAIL:
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,docs,store,node"

# INDEX STATS:
curl -X GET "localhost:9200/_cat/indices?v&h=index,docs.count,store.size,pri,rep,health"

# THREAD POOLS:
curl -X GET "localhost:9200/_cat/thread_pool?v&h=node_name,name,active,queue,rejected"

# IMPORTANT: Regarder "rejected"!
# > 0 = Thread pool saturé

# HOT THREADS (Debug performance):
curl -X GET "localhost:9200/_nodes/hot_threads"

# Affiche threads les plus actifs
# Utile pour diagnostiquer CPU élevé

# PENDING TASKS:
curl -X GET "localhost:9200/_cluster/pending_tasks?pretty"

# Tâches en attente
# File longue = Problème

# === MONITORING AVEC STACK MONITORING ===

# Stack Monitoring = Monitoring intégré Elastic Stack
# Monitore Elasticsearch, Logstash, Kibana, Beats

# ACTIVER:

# 1. Dans elasticsearch.yml:
xpack.monitoring.collection.enabled: true

# 2. Redémarrer Elasticsearch

# 3. Kibana > Stack Monitoring

# Dashboard automatique avec:
# - Cluster overview
# - Nœuds stats
# - Indices stats
# - Métriques JVM
# - GC activity
# - Index rate / Search rate

# === METRICBEAT MONITORING (Recommandé) ===

# Plus moderne, moins d'overhead

# 1. Installer Metricbeat sur chaque nœud ES

# 2. Activer module elasticsearch:
metricbeat modules enable elasticsearch-xpack

# 3. Configurer module:
# modules.d/elasticsearch-xpack.yml
- module: elasticsearch
  xpack.enabled: true
  period: 10s
  hosts: ["http://localhost:9200"]
  username: "remote_monitoring_user"
  password: "password"

# 4. Setup et démarrer:
metricbeat setup
metricbeat -e

# 5. Voir dans Kibana Stack Monitoring

# === ALERTES IMPORTANTES ===

# ALERTE 1: Cluster status YELLOW/RED

# Règle Kibana:
# Type: Elasticsearch query
# Index: .monitoring-es-*
# Query: cluster_stats.status: "yellow" OR cluster_stats.status: "red"
# Check every: 1 minute
# Action: Email + Slack

# ALERTE 2: Heap > 85%

# Type: Index threshold
# Index: metricbeat-*
# Aggregation: max of elasticsearch.node.jvm.memory.heap.used.pct
# When: max() is above 85
# For the last: 5 minutes
# Group by: elasticsearch.node.name

# ALERTE 3: Disk > 90%

# Type: Index threshold
# Index: metricbeat-*
# Aggregation: max of system.filesystem.used.pct
# When: max() is above 0.90
# For the last: 5 minutes

# ALERTE 4: Recherches lentes

# Type: Index threshold
# Index: .monitoring-es-*
# Aggregation: avg of indices_stats.total.search.query_time_in_millis
# When: avg() is above 1000
# For the last: 10 minutes

# ALERTE 5: Rejected requests

# Type: Elasticsearch query
# Query: node_stats.thread_pool.*.rejected: >0
# Check every: 1 minute

# === OPTIMISATION PERFORMANCE ===

# PROBLÈME: Recherches lentes

# 1. PROFILER query:
curl -X GET "localhost:9200/logs-*/_search?pretty" \
  -H 'Content-Type: application/json' -d'{
  "profile": true,
  "query": {
    "match": {"message": "error"}
  }
}'

# Réponse inclut breakdown détaillé:
# - Temps par phase
# - Quelle partie est lente

# 2. VÉRIFIER slow logs:
tail -f /var/log/elasticsearch/*_index_search_slowlog.log

# 3. OPTIMISATIONS:

# a) Utiliser filters (cachés):
# [OK] Bon:
{"bool": {"filter": [{"term": {"status": "active"}}]}}

# [X] Moins bon:
{"bool": {"must": [{"term": {"status": "active"}}]}}

# b) Limiter size:
{"size": 10}  # Pas {"size": 10000}

# c) Utiliser _source filtering:
{"_source": ["id", "name"]}

# d) Éviter wildcard starting with *:
# [X] Lent: {"wildcard": {"name": "*test"}}
# [OK] OK: {"wildcard": {"name": "test*"}}

# e) Forcemerge index read-only:
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1"

# PROBLÈME: Indexation lente

# 1. BULK API (batch):
# Au lieu de 1000 requêtes individuelles:
curl -X POST "localhost:9200/_bulk" -d @data.ndjson

# 2. REFRESH INTERVAL:
# Augmenter pendant bulk insert:
curl -X PUT "localhost:9200/logs-*/_settings" -d'{
  "index": {"refresh_interval": "30s"}
}'

# Remettre après:
curl -X PUT "localhost:9200/logs-*/_settings" -d'{
  "index": {"refresh_interval": "1s"}
}'

# 3. REPLICAS:
# Désactiver pendant bulk:
curl -X PUT "localhost:9200/logs-*/_settings" -d'{
  "index": {"number_of_replicas": 0}
}'

# Réactiver après:
curl -X PUT "localhost:9200/logs-*/_settings" -d'{
  "index": {"number_of_replicas": 1}
}'

# 4. TRANSLOG:
# Mode async (plus rapide, moins sûr):
curl -X PUT "localhost:9200/logs-*/_settings" -d'{
  "index": {"translog.durability": "async"}
}'

# PROBLÈME: Heap élevé

# 1. VÉRIFIER heap settings:
# /etc/elasticsearch/jvm.options
-Xms8g
-Xmx8g

# Règle: Min = Max = 50% RAM (max 32GB)

# 2. VÉRIFIER field data cache:
curl -X GET "localhost:9200/_nodes/stats/indices/fielddata?pretty"

# Si élevé: Limiter ou utiliser doc_values

# 3. VÉRIFIER segments:
curl -X GET "localhost:9200/_cat/segments?v"

# Trop de segments = Heap élevé
# Solution: Forcemerge

# 4. CLEAR CACHE:
curl -X POST "localhost:9200/_cache/clear?pretty"

# PROBLÈME: Disque plein

# 1. VÉRIFIER utilisation:
curl -X GET "localhost:9200/_cat/allocation?v"
df -h

# 2. SUPPRIMER vieux index:
curl -X DELETE "localhost:9200/logs-2023-*"

# 3. ACTIVER ILM (automatique):
# Voir section ILM

# 4. CURATOR (nettoyage automatique):
pip install elasticsearch-curator

# curator_actions.yml:
actions:
  1:
    action: delete_indices
    filters:
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 30

# 5. AUGMENTER WATERMARKS:
curl -X PUT "localhost:9200/_cluster/settings" -d'{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "90%",
    "cluster.routing.allocation.disk.watermark.high": "95%"
  }
}'

# [ATTENTION] Temporaire seulement! Mieux: Libérer espace

# PROBLÈME: Circuit breaker

# Erreur: "Data too large, circuit breaker"

# 1. VÉRIFIER breakers:
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

# 2. AUGMENTER (temporaire):
curl -X PUT "localhost:9200/_cluster/settings" -d'{
  "persistent": {
    "indices.breaker.total.limit": "80%"
  }
}'

# 3. MIEUX: Optimiser query
# - Réduire size
# - Filtrer plus
# - Augmenter RAM

# PROBLÈME: Split brain (cluster multi-nœuds)

# Symptôme: Multiple masters élus
# Cause: Problème réseau entre nœuds

# Prévention:
# minimum_master_nodes = (N/2) + 1
# N = nombre nœuds master-eligible

# 3 nœuds master: minimum = 2
# 5 nœuds master: minimum = 3

# Dans elasticsearch.yml (ES 7+):
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

# === TUNIN


[OK] DÉPANNAGE & PROBLÈMES COURANTS

# === Problème: Elasticsearch ne démarre pas ===

# Vérifier logs
sudo journalctl -u elasticsearch.service -f
tail -f /var/log/elasticsearch/elasticsearch.log

# Vérifier configuration
/usr/share/elasticsearch/bin/elasticsearch -V

# Vérifier ports
sudo netstat -tulpn | grep 9200
sudo lsof -i :9200

# Vérifier permissions
ls -la /var/lib/elasticsearch
ls -la /var/log/elasticsearch

# Réparer permissions
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/log/elasticsearch

# === Problème: Mémoire insuffisante ===

# Erreur: "OutOfMemoryError"
# Solution: Augmenter heap JVM

# Éditer /etc/elasticsearch/jvm.options
-Xms4g
-Xmx4g

# Règle: 50% RAM max, ne pas dépasser 32GB

# Vérifier utilisation mémoire
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# === Problème: Cluster status YELLOW ===

# Cause: Replicas non assignés
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Solution 1: Réduire nombre de replicas
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# Solution 2: Ajouter nœuds au cluster

# === Problème: Cluster status RED ===

# Cause: Shards primaires manquants (GRAVE!)
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Expliquer pourquoi shard non assigné
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Solution: Restaurer depuis snapshot si possible
# Ou réallouer manuellement (risque perte données)
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "mon-index",
        "shard": 0,
        "node": "node-1",
        "accept_data_loss": true
      }
    }
  ]
}
'

# === Problème: Disque plein ===

# Elasticsearch bloque écriture si disque > 95% plein

# Vérifier espace disque
df -h
curl -X GET "localhost:9200/_cat/allocation?v"

# Supprimer vieux index
curl -X DELETE "localhost:9200/logs-2023-*?pretty"

# Ou utiliser Curator (outil de gestion)
pip install elasticsearch-curator

# curator.yml
curator --config curator.yml actions.yml

# === Problème: Recherches lentes ===

# Vérifier slow logs
tail -f /var/log/elasticsearch/*_search_slowlog.log

# Profiler query
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "profile": true,
  "query": {
    "match": {
      "message": "error"
    }
  }
}
'

# Optimisations:
# - Utiliser filters au lieu de queries (cachés)
# - Réduire number_of_shards
# - Forcemerge index anciens
# - Augmenter refresh_interval

curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "30s"
  }
}
'

# === Problème: Indexation lente ===

# Désactiver refresh temporairement
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "-1"
  }
}
'

# Bulk insert
# Réactiver après
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "1s"
  }
}
'

# Réduire replicas pendant indexation
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# === Problème: Trop de segments ===

# Vérifier
curl -X GET "localhost:9200/_cat/segments?v"

# Forcemerge
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1&pretty"

# === Problème: Circuit breaker ===

# Erreur: "Data too large, circuit breaker"
# Cause: Query trop gourmande en mémoire

# Vérifier breakers
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

# Augmenter limite (temporaire)
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "80%"
  }
}
'

# Meilleures solutions:
# - Optimiser query
# - Augmenter RAM
# - Réduire taille résultats

# === Problème: Version conflict ===

# Erreur: "version_conflict_engine_exception"
# Cause: Document modifié entre lecture et écriture

# Solutions:
# - Utiliser retry_on_conflict
curl -X POST "localhost:9200/users/_update/1?retry_on_conflict=3&pretty" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26
  }
}
'

# - Utiliser version externe
# - Utiliser scripts pour updates

# === Problème: Connexion refusée ===

# Vérifier Elasticsearch écoute
curl -X GET "localhost:9200"

# Vérifier network.host dans elasticsearch.yml
network.host: 0.0.0.0

# Vérifier firewall
sudo ufw status
sudo ufw allow 9200/tcp

# === Problème: Kibana ne se connecte pas à Elasticsearch ===

# Vérifier kibana.yml
elasticsearch.hosts: ["http://localhost:9200"]

# Tester connexion
curl -X GET "http://localhost:9200"

# Vérifier logs Kibana
tail -f /var/log/kibana/kibana.log

# Avec sécurité: vérifier username/password
elasticsearch.username: "kibana_system"
elasticsearch.password: "correct_password"

# === Problème: Logstash ne démarre pas ===

# Tester config
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --config.test_and_exit

# Vérifier logs
tail -f /var/log/logstash/logstash-plain.log

# Mode debug
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --log.level=debug

# === Problème: Filebeat ne envoie pas de données ===

# Test config
filebeat test config
filebeat test output

# Mode debug
filebeat -e -d "*"

# Vérifier registry (position lecture fichiers)
cat /var/lib/filebeat/registry/filebeat/data.json

# Reset registry (relit depuis début)
sudo systemctl stop filebeat
sudo rm /var/lib/filebeat/registry/filebeat/data.json
sudo systemctl start filebeat


[OK] COMMANDES UTILES - ELASTICSEARCH

# === Cat API (Format lisible) ===

# Tous les cat endpoints
curl -X GET "localhost:9200/_cat?pretty"

# Indices
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET "localhost:9200/_cat/indices?v&s=store.size:desc"
curl -X GET "localhost:9200/_cat/indices?v&h=index,docs.count,store.size"

# Shards
curl -X GET "localhost:9200/_cat/shards?v"
curl -X GET "localhost:9200/_cat/shards/logs-*?v"

# Nœuds
curl -X GET "localhost:9200/_cat/nodes?v"
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m"

# Master
curl -X GET "localhost:9200/_cat/master?v"

# Allocation
curl -X GET "localhost:9200/_cat/allocation?v"

# Count
curl -X GET "localhost:9200/_cat/count?v"
curl -X GET "localhost:9200/_cat/count/logs-*?v"

# Health
curl -X GET "localhost:9200/_cat/health?v"

# Segments
curl -X GET "localhost:9200/_cat/segments?v"

# Templates
curl -X GET "localhost:9200/_cat/templates?v"

# Aliases
curl -X GET "localhost:9200/_cat/aliases?v"

# Plugins
curl -X GET "localhost:9200/_cat/plugins?v"

# Tasks
curl -X GET "localhost:9200/_cat/tasks?v"

# === Scripts utiles ===

# Compter documents dans tous les index
for index in $(curl -s 'localhost:9200/_cat/indices?h=index'); do
  count=$(curl -s "localhost:9200/${index}/_count" | jq -r '.count')
  echo "${index}: ${count}"
done

# Supprimer tous les index vieux de +30 jours
curl -s 'localhost:9200/_cat/indices?h=index' | grep 'logs-2023' | xargs -I {} curl -X DELETE "localhost:9200/{}"

# Backup tous les index
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_$(date +%Y%m%d)?wait_for_completion=false&pretty"

# === Requêtes complexes ===

# Aggregation multi-niveaux
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_status": {
      "terms": {
        "field": "response_code",
        "size": 10
      },
      "aggs": {
        "par_heure": {
          "date_histogram": {
            "field": "@timestamp",
            "calendar_interval": "hour"
          },
          "aggs": {
            "temps_reponse_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}
'

# Percentiles
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "response_time_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]
      }
    }
  }
}
'

# Top hits (exemples dans chaque bucket)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_ip": {
      "terms": {
        "field": "client_ip.keyword",
        "size": 10
      },
      "aggs": {
        "exemples": {
          "top_hits": {
            "size": 3,
            "_source": ["@timestamp", "request", "response_code"]
          }
        }
      }
    }
  }
}
'


[OK] BONNES PRATIQUES

# === Naming conventions ===

# Index: lowercase, pattern avec date
# [OK] logs-nginx-2024-01-15
# [OK] metrics-system-2024-01
# [X] Logs_Nginx_20240115

# Aliases: utiliser pour applications
# [OK] logs-current -> logs-2024-01-15
# [OK] logs-errors -> logs-* (avec filtre)

# === Structure des données ===

# Utiliser types appropriés
# - keyword: ID, email, username (exact match)
# - text: Message, description (full-text search)
# - date: Timestamps
# - integer/long: Compteurs, IDs numériques
# - float/double: Valeurs décimales
# - boolean: Flags
# - ip: Adresses IP
# - geo_point: Coordonnées GPS

# Éviter nested/object si possible (plus lent)

# === Sharding ===

# Règle: 1 shard = 10-50 GB max
# Trop de shards = overhead
# Trop peu = distribution inégale

# Petit cluster (< 50 GB data): 1 shard
# Cluster moyen: 3-5 shards
# Grand cluster: calculer selon volume

# Replicas:
# - Production: minimum 1 replica
# - Dev: 0 replica OK
# - HA critique: 2+ replicas

# === Refresh interval ===

# Défaut: 1s (bon pour recherche temps réel)
# Indexation bulk: augmenter à 30s ou -1 (désactiver)
# Logs anciens: 30s ou plus

# === Index lifecycle ===

# Utiliser ILM pour:
# - Rollover automatique
# - Compression (warm phase)
# - Suppression automatique
# - Économiser espace/ressources

# === Monitoring ===

# Surveiller:
# - Heap usage (< 75%)
# - Disk usage (< 85%)
# - Cluster health
# - Search/indexing latency
# - Node count

# Alertes sur:
# - Cluster RED/YELLOW
# - Heap > 80%
# - Disk > 90%
# - Slow queries
# - Failed shards

# === Sécurité ===

# [OK] Activer X-Pack Security
# [OK] Utiliser HTTPS
# [OK] Authentification forte
# [OK] Principe least privilege (rôles)
# [OK] API keys pour applications
# [OK] Firewall (limiter accès 9200/9300)
# [OK] Monitoring accès
# [OK] Backups réguliers

# === Performance ===

# Indexation:
# - Bulk API (batch 5-15 MB)
# - Désactiver refresh si bulk important
# - Réduire replicas temporairement
# - Utiliser pipelines Ingest pour transformations

# Recherche:
# - Utiliser filters (cachés)
# - Limiter size des résultats
# - Utiliser scroll API pour grandes données
# - Index appropriate fields as keyword
# - Utiliser routing pour cibler shards

# Optimisation index:
# - Forcemerge index read-only
# - Désactiver _source si non nécessaire
# - Utiliser _source includes/excludes
# - Doc values pour aggregations

# === Backups ===

# Stratégie 3-2-1:
# - 3 copies
# - 2 médias différents
# - 1 offsite

# Automatiser snapshots:
# - Quotidien pour données critiques
# - Hebdomadaire pour archives
# - Tester restauration régulièrement


[OK] CAS D'USAGE PRATIQUES

# === Use Case 1: Centralisation logs applications ===

# Architecture:
# Applications -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat sur chaque serveur:
filebeat.inputs:
- type: log
  paths:
    - /var/log/app/*.log
  fields:
    app: mon-app
    env: production
  fields_under_root: true

output.logstash:
  hosts: ["logstash:5044"]

# Logstash pipeline:
input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:log_message}" }
  }
  
  if [level] == "ERROR" {
    mutate {
      add_tag => ["alert"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "%{[fields][app]}-logs-%{+YYYY.MM.dd}"
  }
}

# Kibana: Dashboard avec visualizations
# - Logs par niveau (pie chart)
# - Timeline des erreurs (line chart)
# - Top erreurs (data table)
# - Alertes sur erreurs critiques

# === Use Case 2: Monitoring infrastructure ===

# Architecture:
# Serveurs -> Metricbeat -> Elasticsearch -> Kibana

# Metricbeat configuration:
metricbeat.modules:
- module: system
  metricsets:
    - cpu
    - memory
    - network
    - diskio
    - filesystem
  period: 10s

- module: docker
  metricsets:
    - container
    - cpu
    - diskio
    - memory
    - network
  period: 10s

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  index: "metricbeat-%{+yyyy.MM.dd}"

setup.kibana:
  host: "kibana:5601"

# Setup dashboards:
metricbeat setup --dashboards

# Kibana: Dashboards automatiques
# - System Overview
# - CPU usage
# - Memory usage
# - Network traffic
# - Docker containers

# Alertes:
# - CPU > 80% pendant 5 min
# - Memory > 90%
# - Disk > 85%

# === Use Case 3: Analyse e-commerce ===

# Architecture:
# Application -> HTTP input -> Logstash -> Elasticsearch -> Kibana

# Application envoie events JSON:
POST http://logstash:8080
{
  "event_type": "purchase",
  "user_id": "12345",
  "product_id": "ABC123",
  "amount": 49.99,
  "currency": "EUR",
  "timestamp": "2024-01-15T10:30:00Z"
}

# Logstash:
input {
  http {
    port => 8080
    codec => json
  }
}

filter {
  date {
    match => [ "timestamp", "ISO8601" ]
  }
  
  mutate {
    convert => {
      "amount" => "float"
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "ecommerce-events-%{+YYYY.MM}"
  }
}

# Kibana visualizations:
# - Revenus par jour (line chart)
# - Top produits (bar chart)
# - Conversion funnel
# - Heatmap achats par heure
# - Geo map des ventes

# Aggregations utiles:
curl -X GET "localhost:9200/ecommerce-events-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "revenus_quotidiens": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "total_revenus": {
          "sum": {
            "field": "amount"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "amount"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "amount"
          }
        }
      }
    },
    "top_produits": {
      "terms": {
        "field": "product_id.keyword",
        "size": 10
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "amount"
          }
        }
      }
    }
  }
}
'

# === Use Case 4: Security monitoring (SIEM) ===

# Architecture:
# Firewalls/IDS -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat modules:
filebeat.modules:
- module: iptables
- module: suricata
- module: zeek

# Logstash enrichissement:
filter {
  # GeoIP
  geoip {
    source => "source_ip"
    target => "source_geo"
  }
  
  # Threat intelligence
  translate {
    field => "source_ip"
    destination => "threat_level"
    dictionary_path => "/etc/logstash/threat_ips.yml"
    fallback => "unknown"
  }
  
  # Détection patterns suspects
  if [destination_port] in [22, 3389] and [failed_login] {
    mutate {
      add_tag => ["brute_force_attempt"]
    }
  }
}

# Kibana SIEM:
# - Timeline événements
# - Carte attaques géographiques
# - Top IPs suspectes
# - Anomalies détectées

# Alertes:
# - Multiple failed logins
# - Traffic suspect
# - Port scans
# - Malware detected

# === Use Case 5: IoT data collection ===

# Architecture:
# IoT devices -> MQTT -> Logstash -> Elasticsearch -> Kibana

# Logstash MQTT input:
input {
  mqtt {
    host => "mqtt-broker"
    port => 1883
    topic => "sensors/#"
    codec => json
  }
}

filter {
  # Ajouter metadata
  mutate {
    add_field => {
      "device_type" => "sensor"
    }
  }
  
  # Convertir types
  mutate {
    convert => {
      "temperature" => "float"
      "humidity" => "float"
    }
  }
  
  # Alertes sur seuils
  if [temperature] > 30 {
    mutate {
      add_tag => ["high_temperature"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "iot-sensors-%{+YYYY.MM.dd}"
  }
}

# Kibana:
# - Time series température/humidité
# - Heatmap par location
# - Alertes sur anomalies
# - Prédictions ML


[OK] OUTILS COMPLÉMENTAIRES

# === Curator (Gestion automatique index) ===

# Installer
pip install elasticsearch-curator

# Configuration: curator.yml
client:
  hosts:
    - localhost
  port: 9200
  timeout: 30

# Actions: actions.yml
actions:
  1:
    action: delete_indices
    description: Supprimer index > 30 jours
    options:
      ignore_empty_list: True
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 30
  
  2:
    action: forcemerge
    description: Forcemerge index > 2 jours
    options:
      max_num_segments: 1
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 2

# Exécuter
curator --config curator.yml actions.yml

# Cron quotidien
# crontab -e
0 2 * * * /usr/local/bin/curator --config /etc/curator/curator.yml /etc/curator/actions.yml

# === ElastAlert (Alerting avancé) ===

# Installer
pip install elastalert

# Configuration: config.yaml
rules_folder: rules
run_every:
  minutes: 1
buffer_time:
  minutes: 15
es_host: localhost
es_port: 9200
writeback_index: elastalert_status

# Règle: spike_rule.yaml
name: Spike in errors
type: spike
index: logs-*
timeframe:
  minutes: 10
threshold_cur: 5
threshold_ref: 5
spike_height: 2
spike_type: up
filter:
- term:
    level: "ERROR"
alert:
- email
email:
- ops@example.com

# Lancer
elastalert --config config.yaml --rule spike_rule.yaml

# === Elasticsearch SQL ===

# Requêtes SQL sur Elasticsearch (v7+)
curl -X POST "localhost:9200/_sql?format=txt&pretty" -H 'Content-Type: application/json' -d'
{
  "query": "SELECT @timestamp, level, message FROM \"logs-*\" WHERE level = '\''ERROR'\'' LIMIT 10"
}
'

# Avec Kibana Console:
POST _sql?format=txt
{
  "query": "SELECT COUNT(*) FROM \"logs-*\" GROUP BY level"
}

# Translate to Query DSL:
POST _sql/translate
{
  "query": "SELECT * FROM \"logs-*\" WHERE response_code >= 400"
}

# === Elastic APM (Application Performance Monitoring) ===

# Installer APM Server
apt-get install apm-server

# Configuration: apm-server.yml
apm-server:
  host: "0.0.0.0:8200"

output.elasticsearch:
  hosts: ["localhost:9200"]

setup.kibana:
  host: "localhost:5601"

# Instrumenter application (Python exemple)
pip install elastic-apm

# app.py
from elasticapm import Client
from elasticapm.contrib.flask import ElasticAPM

app = Flask(__name__)
app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': 'my-app',
    'SERVER_URL': 'http://localhost:8200',
    'ENVIRONMENT': 'production',
}
apm = ElasticAPM(app)

# Voir traces dans Kibana APM

# === Elasticsearch Watcher (Alerting natif) ===

# Créer watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate?pretty" -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": {
      "interval": "5m"
    }
  },
  "input": {
    "search": {
      "request": {
        "indices": ["logs-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "level": "ERROR"
                  }
                },
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-5m"
                    }
                  }
                }
              ]
            }
          },
          "aggs": {
            "error_count": {
              "value_count": {
                "field": "level"
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.aggregations.error_count.value": {
        "gt": 100
      }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "ops@example.com",
        "subject": "High error rate detected",
        "body": "Detected {{ctx.payload.aggregations.error_count.value}} errors in last 5 minutes"
      }
    }
  }
}
'

# Lister watches
curl -X GET "localhost:9200/_watcher/_query/watches?pretty"

# Activer/Désactiver watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_activate?pretty"
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_deactivate?pretty"

# === Elasticsearch Hadoop ===

# Connecter Elasticsearch avec Hadoop/Spark

# Spark exemple (Scala):
import org.elasticsearch.spark.sql._

val df = spark.read
  .format("es")
  .load("logs-*/doc")

df.filter(df("level") === "ERROR")
  .groupBy("source")
  .count()
  .show()


[OK] RESSOURCES & DOCUMENTATION

# === Documentation officielle ===

# Elasticsearch
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

# Logstash
https://www.elastic.co/guide/en/logstash/current/index.html

# Kibana
https://www.elastic.co/guide/en/kibana/current/index.html

# Beats
https://www.elastic.co/guide/en/beats/libbeat/current/index.html

# === Guides pratiques ===

# Getting Started
https://www.elastic.co/guide/en/elastic-stack-get-started/current/index.html

# Elasticsearch: The Definitive Guide (livre)
https://www.elastic.co/guide/en/elasticsearch/guide/current/index.html

# Blog Elastic
https://www.elastic.co/blog/

# === Forums & Support ===

# Discuss Elastic (forum communauté)
https://discuss.elastic.co/

# GitHub Issues
https://github.com/elastic/elasticsearch/issues

# Stack Overflow
https://stackoverflow.com/questions/tagged/elasticsearch

# === Formations ===

# Elastic Training (officiel)
https://www.elastic.co/training/

# Free fundamentals courses
https://www.elastic.co/training/free

# === Outils en ligne ===

# Grok Debugger (tester patterns)
http://grokdebug.herokuapp.com/

# JSON formatter
https://jsonformatter.org/

# Elasticsearch Head (plugin navigateur)
https://github.com/mobz/elasticsearch-head

# === Communauté ===

# Meetups Elastic
https://www.elastic.co/community/

# ElasticON (conférence annuelle)
https://www.elastic.co/elasticon/

# === Versions & Compatibilité ===

# Support matrix
https://www.elastic.co/support/matrix

# Release notes
https://www.elastic.co/downloads/past-releases

# Breaking changes
https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html


[OK] EXEMPLES DE SCRIPTS MAINTENANCE

# === Backup automatique quotidien (Bash) ===

#!/bin/bash
# backup_elasticsearch.sh

REPOSITORY="my_backup"
SNAPSHOT_NAME="snapshot_$(date +%Y%m%d_%H%M%S)"
ES_HOST="localhost:9200"

# Créer snapshot
curl -X PUT "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}?wait_for_completion=false" \
  -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,metrics-*",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Vérifier statut
sleep 10
STATUS=$(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}" | jq -r '.snapshots[0].state')

if [ "$STATUS" == "SUCCESS" ]; then
  echo "Backup réussi: ${SNAPSHOT_NAME}"
  
  # Supprimer snapshots > 7 jours
  CUTOFF_DATE=$(date -d "7 days ago" +%Y%m%d)
  for snapshot in $(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/_all" | jq -r '.snapshots[].snapshot'); do
    SNAPSHOT_DATE=$(echo $snapshot | grep -oP '\d{8}')
    if [ "$SNAPSHOT_DATE" -lt "$CUTOFF_DATE" ]; then
      echo "Suppression ancien snapshot: $snapshot"
      curl -X DELETE "${ES_HOST}/_snapshot/${REPOSITORY}/${snapshot}"
    fi
  done
else
  echo "Erreur backup: ${STATUS}"
  exit 1
fi

# Cron: 0 2 * * * /usr/local/bin/backup_elasticsearch.sh

# === Monitoring santé cluster (Python) ===

#!/usr/bin/env python3
# monitor_cluster.py

import requests
import json
import sys
from datetime import datetime

ES_HOST = "http://localhost:9200"
WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

def check_cluster_health():
    try:
        response = requests.get(f"{ES_HOST}/_cluster/health")
        health = response.json()
        
        status = health['status']
        cluster_name = health['cluster_name']
        
        if status in ['yellow', 'red']:
            message = {
                "text": f"[ATTENTION] Cluster {cluster_name} status: {status}",
                "attachments": [{
                    "color": "warning" if status == "yellow" else "danger",
                    "fields": [
                        {"title": "Status", "value": status, "short": True},
                        {"title": "Nodes", "value": str(health['number_of_nodes']), "short": True},
                        {"title": "Active Shards", "value": str(health['active_shards']), "short": True},
                        {"title": "Unassigned Shards", "value": str(health['unassigned_shards']), "short": True},
                    ],
                    "footer": f"Checked at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
                }]
            }
            
            # Envoyer alerte Slack
            requests.post(WEBHOOK_URL, json=message)
            return False
        
        return True
        
    except Exception as e:
        print(f"Erreur: {e}")
        sys.exit(1)

def check_disk_usage():
    try:
        response = requests.get(f"{ES_HOST}/_cat/allocation?format=json")
        allocations = response.json()
        
        for alloc in allocations:
            disk_percent = float(alloc['disk.percent'])
            if disk_percent > 85:
                message = {
                    "text": f"[ROUGE] Disk usage high on node {alloc['node']}: {disk_percent}%"
                }
                requests.post(WEBHOOK_URL, json=message)
                
    except Exception as e:
        print(f"Erreur: {e}")

if __name__ == "__main__":
    check_cluster_health()
    check_disk_usage()

# Cron: */5 * * * * /usr/local/bin/monitor_cluster.py

# === Nettoyage index anciens (Python) ===

#!/usr/bin/env python3
# cleanup_old_indices.py

import requests
from datetime import datetime, timedelta

ES_HOST = "http://localhost:9200"
RETENTION_DAYS = 30
INDEX_PATTERN = "logs-"

def get_indices():
    response = requests.get(f"{ES_HOST}/_cat/indices?h=index&format=json")
    return [idx['index'] for idx in response.json()]

def delete_old_indices():
    indices = get_indices()
    cutoff_date = datetime.now() - timedelta(days=RETENTION_DAYS)
    
    for index in indices:
        if not index.startswith(INDEX_PATTERN):
            continue
            
        try:
            # Extraire date du nom (format: logs-YYYY.MM.DD)
            date_str = index.replace(INDEX_PATTERN, "")
            index_date = datetime.strptime(date_str, "%Y.%m.%d")
            
            if index_date < cutoff_date:
                print(f"Suppression index: {index}")
                response = requests.delete(f"{ES_HOST}/{index}")
                if response.status_code == 200:
                    print(f"[OK] {index} supprimé")
                else:
                    print(f"[X] Erreur suppression {index}: {response.text}")
                    
        except ValueError:
            print(f"Format date invalide pour: {index}")
            continue

if __name__ == "__main__":
    delete_old_indices()

# Cron: 0 3 * * * /usr/local/bin/cleanup_old_indices.py


# === FIN DE LA CHEATSHEET ===

# Cette cheatsheet couvre:
# [OK] Installation complète ELK Stack
# [OK] Configuration Elasticsearch, Logstash, Kibana, Filebeat
# [OK] API REST Elasticsearch (CRUD, recherche, agrégations)
# [OK] Pipelines Logstash avec exemples réels
# [OK] Visualisations et dashboards Kibana
# [OK] Gestion de sécurité (X-Pack)
# [OK] Monitoring et performance
# [OK] ILM (Index Lifecycle Management)
# [OK] Dépannage et problèmes courants
# [OK] Cas d'usage pratiques
# [OK] Outils complémentaires
# [OK] Scripts de maintenance
# [OK] Bonnes pratiques

# Pour aller plus loin:
# - Elastic Certified Engineer
# - Architecture clusters multi-nœuds
# - Machine Learning avancé
# - Cross-cluster search
# - Elasticsearch SQL
# - Canvas pour présentations
# - APM (Application Performance Monitoring), '\.zip


[OK] ELASTICSEARCH - API REST (COMPRENDRE LES BASES)

# === QU'EST-CE QU'UNE API REST? ===

# REST = Representational State Transfer
# C'est comme envoyer des lettres à Elasticsearch:
# - Tu envoies une requête HTTP (GET, POST, PUT, DELETE)
# - Elasticsearch répond avec du JSON
# - Pas besoin d'interface graphique, juste curl ou un outil HTTP

# STRUCTURE D'UNE REQUÊTE:
curl -X <MÉTHODE> "<URL>" -H 'Content-Type: application/json' -d '<DONNÉES JSON>'

# Exemple concret:
curl -X GET "http://localhost:9200/_cluster/health"
#     ^    ^                ^                  ^
#     |    |                |                  |
#  Méthode URL de base    Port              Endpoint (ce qu'on veut)

# MÉTHODES HTTP:
# GET    = Lire/Récupérer (comme "affiche-moi")
# POST   = Créer (comme "ajoute ça")
# PUT    = Créer/Remplacer (comme "mets ça à cet endroit")
# DELETE = Supprimer (comme "efface ça")

# === CONCEPTS ELASTICSEARCH ===

# 1. INDEX (Pluriel: INDICES)
# C'est comme une base de données ou une table
# Exemples: logs-2024-01-15, utilisateurs, produits
# Un index contient des documents similaires

# 2. DOCUMENT
# C'est un enregistrement, une ligne de données
# Format: JSON (comme un dictionnaire Python)
# Exemple de document:
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "ville": "Paris"
}

# 3. MAPPING
# C'est le schéma/structure des données
# Définit les types de champs (text, integer, date, etc.)
# Comme définir les colonnes d'une table SQL

# 4. SHARD
# Fragment d'un index pour distribuer les données
# Comme découper un gros livre en plusieurs tomes
# Plus de shards = meilleure distribution

# 5. REPLICA
# Copie de backup d'un shard
# Pour sécurité et performances (load balancing)

# === SANTÉ DU CLUSTER (Première commande à connaître!) ===

curl -X GET "localhost:9200/_cluster/health?pretty"

# Explication:
# _cluster/health = endpoint pour santé cluster
# ?pretty = affiche JSON formaté (plus lisible)

# Réponse:
{
  "cluster_name" : "elasticsearch",
  "status" : "green",              # <- IMPORTANT!
  "timed_out" : false,
  "number_of_nodes" : 1,           # Nombre de nœuds actifs
  "number_of_data_nodes" : 1,      # Nœuds qui stockent données
  "active_primary_shards" : 5,     # Shards primaires actifs
  "active_shards" : 5,             # Total shards actifs
  "relocating_shards" : 0,         # Shards en déplacement
  "initializing_shards" : 0,       # Shards en initialisation
  "unassigned_shards" : 0          # Shards non assignés (problème si > 0)
}

# STATUS EXPLIQUÉ:
# GREEN  = Tout va bien, toutes données disponibles et répliquées
# YELLOW = Données dispo mais replicas manquants (OK pour dev 1 nœud)
# RED    = Certaines données primaires manquantes (PROBLÈME GRAVE!)

# === GESTION DES INDEX ===

# 1. LISTER TOUS LES INDEX
curl -X GET "localhost:9200/_cat/indices?v"

# Sortie exemple:
# health status index           pri rep docs.count docs.deleted store.size
# yellow open   logs-2024-01-15  1   1       1234            0      1.2mb
# green  open   utilisateurs     1   0        456            0      500kb

# Colonnes expliquées:
# - health: santé (green/yellow/red)
# - status: open (accessible) ou close (fermé)
# - index: nom de l'index
# - pri: nombre de shards primaires
# - rep: nombre de replicas
# - docs.count: nombre de documents
# - store.size: taille sur disque

# 2. CRÉER UN INDEX (Simple)
curl -X PUT "localhost:9200/mon-index"

# Explication:
# PUT = créer ou remplacer
# /mon-index = nom du nouvel index
# Répond: {"acknowledged":true}

# 3. CRÉER UN INDEX (Avec configuration)
curl -X PUT "localhost:9200/mon-index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1,      # Nombre de fragments
    "number_of_replicas": 1     # Nombre de copies
  }
}
'

# Pourquoi configurer shards/replicas?
# - 1 shard + 0 replica = Dev (rapide, pas de backup)
# - 1 shard + 1 replica = Prod petit (backup)
# - 5 shards + 2 replicas = Prod large (distribué + haute dispo)

# 4. CRÉER INDEX AVEC MAPPING (Structure)
curl -X PUT "localhost:9200/utilisateurs" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "nom": { 
        "type": "text"           # Texte recherchable (full-text)
      },
      "age": { 
        "type": "integer"        # Nombre entier
      },
      "email": { 
        "type": "keyword"        # Texte exact (pas de full-text)
      },
      "date_inscription": { 
        "type": "date"           # Date
      },
      "actif": { 
        "type": "boolean"        # Vrai/Faux
      },
      "localisation": { 
        "type": "geo_point"      # Coordonnées GPS
      }
    }
  }
}
'

# TYPES DE CHAMPS EXPLIQUÉS:

# TEXT vs KEYWORD:
# - text: "Jean Dupont" -> recherche "jean", "dupont", "Jean Dupont" (trouvé!)
#         Analyse le texte, supporte recherche partielle
#         Bon pour: messages, descriptions, articles
#
# - keyword: "Jean Dupont" -> recherche exacte "Jean Dupont" seulement
#           Pas d'analyse, recherche exacte
#           Bon pour: emails, IDs, statuts, tags, URLs

# INTEGER / LONG:
# - Nombres entiers (-2, 0, 42, 1000)
# - integer: -2^31 à 2^31-1
# - long: plus grand range

# FLOAT / DOUBLE:
# - Nombres décimaux (3.14, -0.5, 1000.99)
# - float: précision simple
# - double: précision double (plus précis)

# DATE:
# - Dates et timestamps
# - Format: "2024-01-15", "2024-01-15T10:30:00Z"
# - Stocké en millisecondes depuis 1970 (epoch)

# BOOLEAN:
# - true ou false
# - Pour flags, état actif/inactif

# GEO_POINT:
# - Coordonnées latitude/longitude
# - Pour recherches géographiques
# - Format: {"lat": 48.8566, "lon": 2.3522}

# 5. VOIR MAPPING D'UN INDEX
curl -X GET "localhost:9200/utilisateurs/_mapping?pretty"

# Répond avec la structure complète de l'index

# 6. AJOUTER UN CHAMP AU MAPPING (Update)
curl -X PUT "localhost:9200/utilisateurs/_mapping" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "telephone": { "type": "keyword" }
  }
}
'

# [ATTENTION] IMPORTANT: On peut AJOUTER des champs mais pas MODIFIER les existants!
# Pour modifier: il faut réindexer (copier dans nouvel index)

# 7. SUPPRIMER UN INDEX
curl -X DELETE "localhost:9200/mon-index"

# [ATTENTION] ATTENTION: Supprime TOUTES les données de l'index!
# Pas de corbeille, pas d'undo!

# 8. SUPPRIMER PLUSIEURS INDEX (Pattern)
curl -X DELETE "localhost:9200/logs-2023-*"

# Supprime tous les index commençant par "logs-2023-"
# Exemple: logs-2023-01-01, logs-2023-01-02, etc.

# 9. FERMER UN INDEX (Économiser mémoire)
curl -X POST "localhost:9200/mon-index/_close"

# Quand fermer?
# - Index rarement utilisé mais à garder
# - Libère la mémoire
# - Données toujours sur disque
# - Pas cherchable tant que fermé

# 10. OUVRIR INDEX FERMÉ
curl -X POST "localhost:9200/mon-index/_open"

# 11. VOIR INFO DÉTAILLÉE D'UN INDEX
curl -X GET "localhost:9200/mon-index?pretty"

# Affiche: settings, mappings, aliases

# === GESTION DES DOCUMENTS ===

# 1. AJOUTER UN DOCUMENT (ID automatique)
curl -X POST "localhost:9200/utilisateurs/_doc" -H 'Content-Type: application/json' -d'
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "date_inscription": "2024-01-15",
  "actif": true
}
'

# Réponse:
{
  "_index": "utilisateurs",        # Dans quel index
  "_id": "abc123xyz",              # ID généré automatiquement
  "_version": 1,                   # Version (pour détection conflits)
  "result": "created",             # Action effectuée
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

# 2. AJOUTER DOCUMENT (ID spécifique)
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 25,
  "email": "marie@example.com",
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT avec /1 à la fin = ID sera "1"
# Pratique si tu as déjà un ID (ex: ID de ta base SQL)

# 3. RÉCUPÉRER UN DOCUMENT PAR ID
curl -X GET "localhost:9200/utilisateurs/_doc/1?pretty"

# Réponse:
{
  "_index": "utilisateurs",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,                   # <- Document trouvé!
  "_source": {                     # <- Les données!
    "nom": "Marie Martin",
    "age": 25,
    "email": "marie@example.com",
    "date_inscription": "2024-02-01",
    "actif": true
  }
}

# Si document n'existe pas: "found": false

# 4. RÉCUPÉRER SEULEMENT CERTAINS CHAMPS
curl -X GET "localhost:9200/utilisateurs/_doc/1?_source=nom,email&pretty"

# Renvoie seulement nom et email (économise bande passante)

# 5. VÉRIFIER SI DOCUMENT EXISTE (Rapide)
curl -I "localhost:9200/utilisateurs/_doc/1"

# -I = HEAD request (juste les headers, pas le body)
# Répond 200 si existe, 404 si n'existe pas
# Plus rapide que GET car ne récupère pas les données

# 6. METTRE À JOUR DOCUMENT COMPLET
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 26,                       # <- Changé de 25 à 26
  "email": "marie.new@example.com", # <- Email mis à jour
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT remplace TOUT le document!
# [ATTENTION] Si tu oublies un champ, il sera supprimé!

# 7. METTRE À JOUR PARTIELLEMENT (Recommandé)
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26                      # <- Change seulement age
  }
}
'

# _update avec "doc" = met à jour seulement les champs spécifiés
# Les autres champs restent intacts
# Plus sûr que PUT complet!

# 8. METTRE À JOUR AVEC SCRIPT
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "script": {
    "source": "ctx._source.age += params.increment",
    "params": {
      "increment": 1
    }
  }
}
'

# Explication:
# ctx._source = le document actuel
# ctx._source.age += 1 = incrémente age de 1
# Pratique pour compteurs, accumulateurs

# 9. UPSERT (Update ou Insert)
curl -X POST "localhost:9200/utilisateurs/_update/999" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "nom": "Nouveau",
    "age": 30
  },
  "doc_as_upsert": true
}
'

# Comportement:
# - Si document ID=999 existe -> met à jour
# - Si n'existe pas -> crée avec ces données
# Pratique pour éviter erreur "document not found"

# 10. SUPPRIMER UN DOCUMENT
curl -X DELETE "localhost:9200/utilisateurs/_doc/1"

# Supprime le document ID=1
# Pas de confirmation, c'est immédiat!

# 11. SUPPRIMER PAR REQUÊTE (Plusieurs documents)
curl -X POST "localhost:9200/utilisateurs/_delete_by_query" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "actif": false             # Supprime tous les inactifs
    }
  }
}
'

# Pratique pour nettoyage en masse
# Exemple: supprimer tous users inactifs depuis 1 an

# === OPÉRATIONS EN MASSE (BULK API) ===

# Pourquoi Bulk?
# - Insérer 1 document à la fois = lent (1000 documents = 1000 requêtes)
# - Bulk = grouper plusieurs opérations en 1 requête (1000 docs = 1 requête!)
# - Beaucoup plus rapide pour gros volumes

curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "index": { "_index": "utilisateurs", "_id": "1" } }
{ "nom": "User 1", "age": 30 }
{ "index": { "_index": "utilisateurs", "_id": "2" } }
{ "nom": "User 2", "age": 25 }
{ "delete": { "_index": "utilisateurs", "_id": "3" } }
{ "update": { "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# Format Bulk:
# Ligne 1: Action (index, create, update, delete)
# Ligne 2: Données (sauf pour delete)
# Répéter...

# [ATTENTION] IMPORTANT: 
# - Chaque ligne doit être un JSON valide
# - Dernière ligne doit se terminer par \n (retour ligne)
# - Pas de virgule entre les lignes

# Meilleures pratiques Bulk:
# - Batches de 5-15 MB (pas trop gros)
# - 1000-5000 documents par batch
# - Ne pas envoyer tout d'un coup (risque timeout) "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# === RECHERCHE DE DOCUMENTS (QUERIES) ===

# COMPRENDRE LA RECHERCHE ELASTICSEARCH

# Elasticsearch = Moteur de recherche comme Google
# 2 types de recherches:
# 1. QUERY (Score de pertinence)
#    - Répond: "À quel point ce document correspond?"
#    - Score: 0.0 à X (plus haut = plus pertinent)
#    - Bon pour: recherche full-text, "trouve articles sur python"
#
# 2. FILTER (Oui/Non)
#    - Répond: "Ce document correspond ou pas?"
#    - Pas de score
#    - Plus rapide (mis en cache)
#    - Bon pour: filtres exacts, "articles de 2024", "statut=publié"

# ENDPOINT DE RECHERCHE
curl -X GET "localhost:9200/<index>/_search"

# Structure de base:
{
  "query": {           # Ce qu'on cherche
    ...
  },
  "size": 10,          # Nombre résultats (défaut: 10)
  "from": 0,           # Pagination (0 = première page)
  "sort": [...],       # Tri des résultats
  "_source": [...]     # Quels champs retourner
}

# === RECHERCHE SIMPLE (MATCH_ALL) ===

# Récupérer TOUS les documents
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}    # Match tout (comme SELECT * en SQL)
  }
}
'

# Réponse:
{
  "took": 5,                    # Temps en millisecondes
  "timed_out": false,           # Timeout dépassé?
  "hits": {
    "total": {
      "value": 1234,            # Nombre total de résultats
      "relation": "eq"          # eq=exact, gte=au moins
    },
    "max_score": 1.0,           # Score max trouvé
    "hits": [                   # Les documents (défaut: 10 premiers)
      {
        "_index": "utilisateurs",
        "_id": "1",
        "_score": 1.0,          # Score de pertinence
        "_source": {            # Le document
          "nom": "Jean",
          "age": 30
        }
      }
    ]
  }
}

# === PAGINATION ===

# Page 1 (premiers 10 résultats)
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 10,          # Nombre par page
  "from": 0            # Offset (commence à 0)
}
'

# Page 2 (résultats 11-20)
{
  "size": 10,
  "from": 10          # Saute les 10 premiers
}

# Page 3 (résultats 21-30)
{
  "size": 10,
  "from": 20          # Saute les 20 premiers
}

# [ATTENTION] LIMITE: from + size ne peut pas dépasser 10,000
# Pour plus: utiliser Scroll API ou Search After

# === RECHERCHE FULL-TEXT (MATCH) ===

# Chercher dans un champ texte
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "nom": "Jean"
    }
  }
}
'

# Comment ça marche?
# 1. "Jean" est analysé (lowercase, etc.)
# 2. Cherche documents contenant "jean"
# 3. Documents avec "Jean Dupont", "jean martin" sont trouvés
# 4. Score calculé selon pertinence

# Recherche plusieurs mots:
{
  "query": {
    "match": {
      "message": "erreur connexion base"
    }
  }
}

# Comportement par défaut (OR):
# Trouve: "erreur" OU "connexion" OU "base"
# Document avec juste "erreur" sera trouvé (score plus bas)

# Forcer AND (tous les mots):
{
  "query": {
    "match": {
      "message": {
        "query": "erreur connexion base",
        "operator": "and"      # Doit contenir TOUS les mots
      }
    }
  }
}

# === RECHERCHE PHRASE EXACTE (MATCH_PHRASE) ===

# Chercher phrase dans l'ordre exact
{
  "query": {
    "match_phrase": {
      "message": "base de données"
    }
  }
}

# Trouve: "erreur base de données" [OK]
# Ne trouve PAS: "base données de test" [X] (ordre différent)

# Avec proximité (slop):
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "base données",
        "slop": 2              # Max 2 mots entre
      }
    }
  }
}

# Trouve: "base de données" [OK] (1 mot entre)
# Trouve: "base des données" [OK] (1 mot entre)
# Trouve: "base et des données" [X] (3 mots entre, dépasse slop)

# === RECHERCHE EXACTE (TERM) ===

# Pour champs keyword (email, ID, statut, etc.)
{
  "query": {
    "term": {
      "email.keyword": "jean@example.com"
    }
  }
}

# [ATTENTION] IMPORTANT:
# term = recherche EXACTE (sensible à la casse)
# "jean@example.com" ≠ "Jean@example.com"
# "jean@example.com" ≠ "jean@EXAMPLE.com"

# Pour chercher parmi plusieurs valeurs (IN en SQL):
{
  "query": {
    "terms": {
      "statut.keyword": ["actif", "en_attente"]
    }
  }
}

# === RECHERCHE PAR PLAGE (RANGE) ===

# Pour nombres, dates
{
  "query": {
    "range": {
      "age": {
        "gte": 25,     # Greater Than or Equal (>=)
        "lte": 35      # Less Than or Equal (<=)
      }
    }
  }
}

# Opérateurs disponibles:
# - gte: >= (supérieur ou égal)
# - gt:  >  (strictement supérieur)
# - lte: <= (inférieur ou égal)
# - lt:  <  (strictement inférieur)

# Exemple dates:
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "2024-01-01",
        "lt": "2024-02-01"
      }
    }
  }
}

# Dates relatives (pratique!):
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-7d",      # Il y a 7 jours
        "lte": "now"          # Maintenant
      }
    }
  }
}

# Unités temps:
# - y: années
# - M: mois
# - w: semaines
# - d: jours
# - h: heures
# - m: minutes
# - s: secondes

# Exemples:
# "now-1h": il y a 1 heure
# "now-30d": il y a 30 jours
# "now+1d": dans 1 jour

# === RECHERCHE BOOLÉENNE (BOOL) ===

# Combiner plusieurs conditions (comme AND, OR, NOT en SQL)
{
  "query": {
    "bool": {
      "must": [         # AND (doit matcher, affecte score)
        ...
      ],
      "filter": [       # AND (doit matcher, pas de score, plus rapide)
        ...
      ],
      "should": [       # OR (au moins 1, boost score)
        ...
      ],
      "must_not": [     # NOT (ne doit PAS matcher)
        ...
      ]
    }
  }
}

# Explication des clauses:

# MUST: Doit matcher + calcule score
# Utilise pour: recherche principale avec pertinence
{
  "must": [
    {"match": {"message": "error"}}
  ]
}

# FILTER: Doit matcher + pas de score (plus rapide, cachable)
# Utilise pour: filtres exacts (date, statut, etc.)
{
  "filter": [
    {"term": {"status": "published"}},
    {"range": {"date": {"gte": "2024-01-01"}}}
  ]
}

# SHOULD: Au moins 1 doit matcher (optionnel)
# Utilise pour: boost pertinence
{
  "should": [
    {"match": {"tags": "python"}},
    {"match": {"tags": "javascript"}}
  ]
}

# MUST_NOT: Ne doit PAS matcher
# Utilise pour: exclusions
{
  "must_not": [
    {"term": {"status": "deleted"}}
  ]
}

# === EXEMPLE COMPLET BOOL ===

# "Trouve logs d'erreur des 7 derniers jours, 
#  niveau ERROR ou FATAL, mais pas de l'application 'test'"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"message": "error"}}     # Contient "error"
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-7d"               # 7 derniers jours
            }
          }
        }
      ],
      "should": [
        {"term": {"level": "ERROR"}},       # Préfère ERROR
        {"term": {"level": "FATAL"}}        # ou FATAL
      ],
      "must_not": [
        {"term": {"app": "test"}}           # Pas de l'app test
      ],
      "minimum_should_match": 1             # Au moins 1 should requis
    }
  }
}

# === RECHERCHE MULTI-CHAMPS (MULTI_MATCH) ===

# Chercher dans plusieurs champs en même temps
{
  "query": {
    "multi_match": {
      "query": "Jean Paris",
      "fields": ["nom", "ville"]    # Cherche dans nom ET ville
    }
  }
}

# Avec boost (donner plus d'importance à un champ):
{
  "query": {
    "multi_match": {
      "query": "python",
      "fields": ["titre^3", "contenu"]  # ^3 = titre 3x plus important
    }
  }
}

# === RECHERCHE WILDCARD (AVEC JOKER) ===

# * = n'importe quels caractères
# ? = 1 caractère exactement

{
  "query": {
    "wildcard": {
      "nom": "Je*"         # Jean, Jerome, Jessica
    }
  }
}

{
  "query": {
    "wildcard": {
      "code": "ABC-???"    # ABC-123, ABC-xyz, etc.
    }
  }
}

# [ATTENTION] ATTENTION: Wildcard est LENT sur gros volumes
# Évite de commencer par * (ex: "*test")

# === RECHERCHE FUZZY (TOLÉRANTE AUX FAUTES) ===

# Trouve documents même avec fautes de frappe
{
  "query": {
    "fuzzy": {
      "nom": {
        "value": "Jeen",           # Faute: "Jeen" au lieu de "Jean"
        "fuzziness": "AUTO"        # Distance d'édition automatique
      }
    }
  }
}

# Fuzziness expliqué:
# - AUTO: ajuste selon longueur mot (recommandé)
# - 0: pas de tolérance (exact)
# - 1: 1 caractère différent
# - 2: 2 caractères différents

# Exemples avec fuzziness=1:
# "Jean" trouve: "Jaan", "Jern", "Jdan"
# Ne trouve pas: "Jorn" (2 différences)

# === RECHERCHE PREFIX (AUTOCOMPLÉTION) ===

# Trouve documents commençant par...
{
  "query": {
    "prefix": {
      "nom": "Jea"         # Trouve: Jean, Jeanne, Jeanette
    }
  }
}

# Bon pour: autocomplétion, suggestion

# === RECHERCHE EXISTS (CHAMP EXISTE) ===

# Trouve documents ayant un champ (non null)
{
  "query": {
    "exists": {
      "field": "email"     # Uniquement docs avec email
    }
  }
}

# Inverse (n'existe PAS):
{
  "query": {
    "bool": {
      "must_not": [
        {"exists": {"field": "email"}}
      ]
    }
  }
}

# === TRI DES RÉSULTATS (SORT) ===

# Tri simple:
{
  "query": {"match_all": {}},
  "sort": [
    {"age": {"order": "desc"}}    # desc=décroissant, asc=croissant
  ]
}

# Tri multiple (comme ORDER BY en SQL):
{
  "sort": [
    {"age": {"order": "desc"}},          # D'abord par age
    {"nom.keyword": {"order": "asc"}}    # Puis par nom
  ]
}

# [ATTENTION] Pour trier sur texte, utilise .keyword
# "nom.keyword" (pas "nom" seul)

# Tri par pertinence (score):
{
  "sort": [
    {"_score": {"order": "desc"}}    # _score = pertinence
  ]
}

# Tri par date (plus récent d'abord):
{
  "sort": [
    {"@timestamp": {"order": "desc"}}
  ]
}

# === SÉLECTION DE CHAMPS (_source) ===

# Retourner tous les champs (défaut):
{
  "query": {"match_all": {}}
  # _source contient tout
}

# Retourner champs spécifiques (économise bande passante):
{
  "query": {"match_all": {}},
  "_source": ["nom", "email"]    # Seulement nom et email
}

# Exclure certains champs:
{
  "_source": {
    "excludes": ["description_longue", "metadata"]
  }
}

# Inclure/Exclure ensemble:
{
  "_source": {
    "includes": ["user.*"],        # Tous champs user.xxx
    "excludes": ["*.password"]     # Sauf passwords
  }
}

# === HIGHLIGHTING (SURLIGNER RÉSULTATS) ===

# Surligne les termes trouvés (comme Google)
{
  "query": {
    "match": {"message": "error"}
  },
  "highlight": {
    "fields": {
      "message": {}
    }
  }
}

# Réponse inclut:
{
  "hits": {
    "hits": [{
      "_source": {"message": "Database error occurred"},
      "highlight": {
        "message": ["Database <em>error</em> occurred"]  # <em> autour du mot
      }
    }]
  }
}

# Personnaliser tags:
{
  "highlight": {
    "pre_tags": ["<strong>"],
    "post_tags": ["</strong>"],
    "fields": {"message": {}}
  }
}

# === SCROLL API (PAGINER GROS VOLUMES) ===

# Pour récupérer TOUS les documents (> 10,000)
# Utilisé pour exports, backups

# Étape 1: Première requête avec scroll
curl -X GET "localhost:9200/utilisateurs/_search?scroll=1m" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 1000          # 1000 docs par batch
}
'

# Répond avec scroll_id:
{
  "_scroll_id": "abc123xyz...",
  "hits": {
    "hits": [...]      # Premiers 1000 docs
  }
}

# Étape 2: Récupérer batch suivant
curl -X POST "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll": "1m",                    # Keep alive 1 minute
  "scroll_id": "abc123xyz..."        # scroll_id de la réponse précédente
}
'

# Répéter jusqu'à hits vide

# Étape 3: Nettoyer (libérer ressources)
curl -X DELETE "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll_id": "abc123xyz..."
}
'

# === COMPTER DOCUMENTS (COUNT) ===

# Juste compter (sans récupérer docs):
curl -X GET "localhost:9200/utilisateurs/_count?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {"actif": true}
  }
}
'

# Réponse:
{
  "count": 1234
}

# Plus rapide que _search car ne récupère pas les docs

# === EXEMPLES PRATIQUES DE RECHERCHES ===

# 1. RECHERCHE E-COMMERCE
# "Trouve produits 'ordinateur portable', 
#  prix entre 500 et 1500€, en stock, triés par popularité"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"nom": "ordinateur portable"}}
      ],
      "filter": [
        {"range": {"prix": {"gte": 500, "lte": 1500}}},
        {"term": {"en_stock": true}}
      ]
    }
  },
  "sort": [
    {"ventes": {"order": "desc"}}
  ]
}

# 2. RECHERCHE LOGS
# "Logs d'erreur des dernières 24h, serveur web, pas health checks"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"level": "ERROR"}}
      ],
      "filter": [
        {"range": {"@timestamp": {"gte": "now-24h"}}},
        {"term": {"service": "web"}}
      ],
      "must_not": [
        {"match": {"url": "/health"}}
      ]
    }
  },
  "sort": [{"@timestamp": {"order": "desc"}}]
}

# 3. RECHERCHE UTILISATEURS
# "Utilisateurs actifs de Paris ou Lyon, inscrits en 2024"
{
  "query": {
    "bool": {
      "must": [
        {"term": {"actif": true}},
        {"range": {"date_inscription": {"gte": "2024-01-01"}}}
      ],
      "should": [
        {"match": {"ville": "Paris"}},
        {"match": {"ville": "Lyon"}}
      ],
      "minimum_should_match": 1
    }
  }
}

# 4. RECHERCHE ARTICLES BLOG
# "Articles contenant 'python' ou 'javascript', 
#  publiés, par pertinence puis date"
{
  "query": {
    "bool": {
      "should": [
        {"match": {"titre": {"query": "python", "boost": 2}}},  # Titre = 2x important
        {"match": {"contenu": "python"}},
        {"match": {"titre": {"query": "javascript", "boost": 2}}},
        {"match": {"contenu": "javascript"}}
      ],
      "filter": [
        {"term": {"statut": "publié"}}
      ],
      "minimum_should_match": 1
    }
  },
  "sort": [
    {"_score": {"order": "desc"}},
    {"date_publication": {"order": "desc"}}
  ]
}

# === CONSEILS PERFORMANCE RECHERCHE ===

# 1. UTILISER FILTER AU LIEU DE MUST QUAND POSSIBLE
# [OK] Bon (cachable):
{"bool": {"filter": [{"term": {"status": "active"}}]}}

# [X] Moins bon (calcule score inutilement):
{"bool": {"must": [{"term": {"status": "active"}}]}}

# 2. LIMITER SIZE
# Ne demande que ce dont tu as besoin
{"size": 10}  # Pas {"size": 10000}

# 3. UTILISER _source FILTERING
# Seulement les champs nécessaires
{"_source": ["id", "nom"]}  # Pas tous les champs

# 4. ÉVITER WILDCARD STARTING WITH *
# [X] Lent: {"wildcard": {"nom": "*test"}}
# [OK] OK: {"wildcard": {"nom": "test*"}}

# 5. PRÉFÉRER TERM À MATCH POUR KEYWORDS
# [OK] Rapide: {"term": {"status.keyword": "active"}}
# [X] Lent: {"match": {"status": "active"}}

# 6. UTILISER BOOL QUERY EFFICACEMENT
# Ordre optimal:
{
  "bool": {
    "filter": [...],     # D'abord (plus rapide, cachable)
    "must": [...],       # Puis (scoring nécessaire)
    "should": [...],     # Puis (bonus optionnels)
    "must_not": [...]    # Enfin (exclusions)
  }
}

[OK] AGRÉGATIONS ELASTICSEARCH (POUR DÉBUTANTS)

# === QU'EST-CE QU'UNE AGRÉGATION? ===

# Agrégation = Calculs statistiques sur les données
# Comme GROUP BY + fonctions en SQL

# ANALOGIE:
# Tu as un panier de fruits:
# - COUNT: Combien de fruits? (total)
# - TERMS: Combien de pommes, oranges, bananes? (par type)
# - AVG: Poids moyen des fruits?
# - SUM: Poids total?
# - MAX/MIN: Fruit le plus/moins lourd?

# Elasticsearch fait pareil avec tes documents!

# === TYPES D'AGRÉGATIONS ===

# 1. METRICS (Métriques)
# Calculs simples: count, sum, avg, min, max
# Comme calculer une valeur

# 2. BUCKETS (Groupements)
# Regrouper documents par critère
# Comme GROUP BY en SQL

# 3. PIPELINE
# Agrégations sur résultats d'autres agrégations
# Comme calculs dérivés

# === STRUCTURE DE BASE ===

GET /index/_search
{
  "size": 0,              # Ne renvoie pas documents (juste stats)
  "aggs": {               # Section agrégations
    "nom_agregation": {   # Nom que tu choisis
      "type": {           # Type d'agrégation
        ...               # Configuration
      }
    }
  }
}

# === AGRÉGATIONS METRICS (CALCULS) ===

# 1. COUNT (Compter)
# Déjà disponible sans agrégation:
GET /logs/_count

# Dans agrégation:
{
  "aggs": {
    "total_logs": {
      "value_count": {
        "field": "message"
      }
    }
  }
}

# 2. SUM (Somme)
# Exemple: Total des ventes
{
  "size": 0,
  "aggs": {
    "total_ventes": {
      "sum": {
        "field": "montant"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "total_ventes": {
      "value": 125430.50    # Total
    }
  }
}

# 3. AVG (Moyenne)
# Exemple: Âge moyen des utilisateurs
{
  "size": 0,
  "aggs": {
    "age_moyen": {
      "avg": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "age_moyen": {
      "value": 32.5
    }
  }
}

# 4. MIN/MAX (Minimum/Maximum)
# Exemple: Prix min et max produits
{
  "size": 0,
  "aggs": {
    "prix_minimum": {
      "min": {
        "field": "prix"
      }
    },
    "prix_maximum": {
      "max": {
        "field": "prix"
      }
    }
  }
}

# 5. STATS (Statistiques complètes)
# Tout en un: count, min, max, avg, sum
{
  "size": 0,
  "aggs": {
    "stats_age": {
      "stats": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "stats_age": {
      "count": 1000,        # Nombre valeurs
      "min": 18,            # Minimum
      "max": 65,            # Maximum
      "avg": 35.5,          # Moyenne
      "sum": 35500          # Somme
    }
  }
}

# 6. EXTENDED_STATS (Stats étendues)
# Stats + variance, écart-type, etc.
{
  "size": 0,
  "aggs": {
    "stats_detailles": {
      "extended_stats": {
        "field": "response_time"
      }
    }
  }
}

# Ajoute:
# - variance
# - std_deviation (écart-type)
# - std_deviation_bounds (limites)

# 7. PERCENTILES (Percentiles)
# Exemple: Temps réponse p50, p95, p99
{
  "size": 0,
  "aggs": {
    "temps_reponse_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]    # p50, p95, p99
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "temps_reponse_percentiles": {
      "values": {
        "50.0": 120,      # 50% requêtes < 120ms
        "95.0": 450,      # 95% requêtes < 450ms
        "99.0": 890       # 99% requêtes < 890ms
      }
    }
  }
}

# Pourquoi utile?
# p50 = médiane (milieu)
# p95 = expérience 95% utilisateurs
# p99 = expérience worst case (presque tous)

# 8. CARDINALITY (Valeurs uniques)
# Exemple: Nombre visiteurs uniques
{
  "size": 0,
  "aggs": {
    "visiteurs_uniques": {
      "cardinality": {
        "field": "user_id.keyword"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "visiteurs_uniques": {
      "value": 15234      # ~15234 users uniques
    }
  }
}

# Note: Approximatif (algorithme HyperLogLog)
# Précis à ~3% près (suffisant pour gros volumes)

# === AGRÉGATIONS BUCKETS (GROUPEMENTS) ===

# 1. TERMS (Grouper par valeur)
# Comme GROUP BY en SQL
# Exemple: Logs par niveau (ERROR, WARN, INFO)

{
  "size": 0,
  "aggs": {
    "par_niveau": {
      "terms": {
        "field": "level.keyword",    # Champ à grouper
        "size": 10                   # Top 10
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_niveau": {
      "buckets": [
        {
          "key": "INFO",             # Valeur
          "doc_count": 8000          # Nombre documents
        },
        {
          "key": "WARN",
          "doc_count": 1500
        },
        {
          "key": "ERROR",
          "doc_count": 500
        }
      ]
    }
  }
}

# Options utiles:
# - size: Nombre de buckets (défaut: 10)
# - order: Tri
#   {"_count": "desc"}     # Par nombre (défaut)
#   {"_key": "asc"}        # Par valeur alphabétique
# - min_doc_count: Minimum docs pour apparaître

# Exemple avec tri:
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"_count": "desc"}    # Plus visitées d'abord
      }
    }
  }
}

# 2. RANGE (Plages de valeurs)
# Exemple: Répartition par tranches d'âge

{
  "size": 0,
  "aggs": {
    "tranches_age": {
      "range": {
        "field": "age",
        "ranges": [
          {"to": 18},                  # < 18
          {"from": 18, "to": 30},      # 18-29
          {"from": 30, "to": 50},      # 30-49
          {"from": 50}                 # 50+
        ]
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_age": {
      "buckets": [
        {
          "key": "*-18.0",
          "to": 18,
          "doc_count": 234
        },
        {
          "key": "18.0-30.0",
          "from": 18,
          "to": 30,
          "doc_count": 1567
        },
        {
          "key": "30.0-50.0",
          "from": 30,
          "to": 50,
          "doc_count": 2890
        },
        {
          "key": "50.0-*",
          "from": 50,
          "doc_count": 1109
        }
      ]
    }
  }
}

# Labels personnalisés:
{
  "ranges": [
    {"key": "Enfants", "to": 18},
    {"key": "Jeunes", "from": 18, "to": 30},
    {"key": "Adultes", "from": 30, "to": 50},
    {"key": "Seniors", "from": 50}
  ]
}

# 3. DATE_HISTOGRAM (Histogramme temporel)
# Grouper par intervalle temps
# Exemple: Logs par jour

{
  "size": 0,
  "aggs": {
    "logs_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"    # Intervalle
      }
    }
  }
}

# Intervalles disponibles:
# - "minute" / "1m"
# - "hour" / "1h"
# - "day" / "1d"
# - "week" / "1w"
# - "month" / "1M"
# - "quarter" / "1q"
# - "year" / "1y"

# Intervalles fixes:
# - "fixed_interval": "30s"    # 30 secondes
# - "fixed_interval": "12h"    # 12 heures

# Réponse:
{
  "aggregations": {
    "logs_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15T00:00:00.000Z",
          "key": 1705276800000,        # Timestamp epoch
          "doc_count": 45678           # Logs ce jour
        },
        {
          "key_as_string": "2024-01-16T00:00:00.000Z",
          "key": 1705363200000,
          "doc_count": 52341
        }
      ]
    }
  }
}

# Options utiles:
# - format: Format date
#   "format": "yyyy-MM-dd"
# - time_zone: Fuseau horaire
#   "time_zone": "Europe/Paris"
# - min_doc_count: 0 pour buckets vides
#   "min_doc_count": 0    # Affiche jours sans logs

# 4. HISTOGRAM (Histogramme numérique)
# Tranches égales sur nombres
# Exemple: Prix par tranches de 100€

{
  "size": 0,
  "aggs": {
    "tranches_prix": {
      "histogram": {
        "field": "prix",
        "interval": 100        # Tranches de 100
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_prix": {
      "buckets": [
        {"key": 0, "doc_count": 234},      # 0-99€
        {"key": 100, "doc_count": 567},    # 100-199€
        {"key": 200, "doc_count": 890},    # 200-299€
        {"key": 300, "doc_count": 345}     # 300-399€
      ]
    }
  }
}

# 5. FILTER (Filtrer avant agrégation)
# Créer bucket avec filtre
# Exemple: Stats seulement sur erreurs

{
  "size": 0,
  "aggs": {
    "erreurs": {
      "filter": {
        "term": {"level": "ERROR"}
      },
      "aggs": {
        "par_service": {
          "terms": {
            "field": "service.keyword"
          }
        }
      }
    }
  }
}

# 6. FILTERS (Multiples filtres)
# Plusieurs buckets avec filtres différents
# Exemple: Compteurs par niveau

{
  "size": 0,
  "aggs": {
    "messages_par_niveau": {
      "filters": {
        "filters": {
          "errors": {"match": {"level": "ERROR"}},
          "warnings": {"match": {"level": "WARN"}},
          "info": {"match": {"level": "INFO"}}
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "messages_par_niveau": {
      "buckets": {
        "errors": {"doc_count": 500},
        "warnings": {"doc_count": 1500},
        "info": {"doc_count": 8000}
      }
    }
  }
}

# === AGRÉGATIONS IMBRIQUÉES (NESTED) ===

# Combiner agrégations pour analyses multi-niveaux
# Comme GROUP BY avec sous-requêtes

# EXEMPLE 1: Logs par service, puis par niveau
{
  "size": 0,
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword"
      },
      "aggs": {                          # <- Sous-agrégation!
        "par_niveau": {
          "terms": {
            "field": "level.keyword"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_service": {
      "buckets": [
        {
          "key": "web",
          "doc_count": 5000,
          "par_niveau": {                # <- Sous-résultats
            "buckets": [
              {"key": "INFO", "doc_count": 4000},
              {"key": "WARN", "doc_count": 800},
              {"key": "ERROR", "doc_count": 200}
            ]
          }
        },
        {
          "key": "api",
          "doc_count": 3000,
          "par_niveau": {
            "buckets": [
              {"key": "INFO", "doc_count": 2700},
              {"key": "WARN", "doc_count": 250},
              {"key": "ERROR", "doc_count": 50}
            ]
          }
        }
      ]
    }
  }
}

# EXEMPLE 2: Ventes par jour + revenus
{
  "size": 0,
  "aggs": {
    "ventes_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "montant"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "montant"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "montant"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "ventes_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15",
          "doc_count": 234,
          "revenus": {"value": 12450.50},
          "nombre_ventes": {"value": 234},
          "panier_moyen": {"value": 53.21}
        },
        {
          "key_as_string": "2024-01-16",
          "doc_count": 267,
          "revenus": {"value": 15678.90},
          "nombre_ventes": {"value": 267},
          "panier_moyen": {"value": 58.72}
        }
      ]
    }
  }
}

# EXEMPLE 3: URLs par statut + temps réponse
{
  "size": 0,
  "aggs": {
    "par_statut": {
      "range": {
        "field": "response_code",
        "ranges": [
          {"key": "2xx", "from": 200, "to": 300},
          {"key": "4xx", "from": 400, "to": 500},
          {"key": "5xx", "from": 500, "to": 600}
        ]
      },
      "aggs": {
        "top_urls": {
          "terms": {
            "field": "url.keyword",
            "size": 5
          },
          "aggs": {
            "temps_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}

# === TRIER RÉSULTATS AGRÉGATION ===

# Par count (défaut):
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "order": {"_count": "desc"}    # Plus de hits d'abord
      }
    }
  }
}

# Par clé (alphabétique):
{
  "terms": {
    "field": "service.keyword",
    "order": {"_key": "asc"}          # A-Z
  }
}

# Par métrique sous-agrégation:
{
  "aggs": {
    "par_produit": {
      "terms": {
        "field": "produit.keyword",
        "order": {"revenus": "desc"}   # <- Trie par revenus
      },
      "aggs": {
        "revenus": {                   # <- Nom référencé
          "sum": {
            "field": "prix"
          }
        }
      }
    }
  }
}

# === FILTRER BUCKETS ===

# Minimum documents:
{
  "terms": {
    "field": "tag.keyword",
    "min_doc_count": 100      # Seulement tags avec 100+ docs
  }
}

# Inclure/Exclure valeurs:
{
  "terms": {
    "field": "status.keyword",
    "include": ["active", "pending"],    # Seulement ces valeurs
    "exclude": ["deleted", "archived"]   # Exclure ces valeurs
  }
}

# Inclure par regex:
{
  "terms": {
    "field": "url.keyword",
    "include": "/api/.*",       # Seulement URLs commençant par /api/
    "exclude": ".*/test/.*"     # Exclure URLs contenant /test/
  }
}

# === EXEMPLES PRATIQUES COMPLETS ===

# EXEMPLE 1: Dashboard e-commerce
# "Revenus, conversions, panier moyen par jour"

GET /orders/_search
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {"gte": "now-30d"}
    }
  },
  "aggs": {
    "par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {"field": "montant"}
        },
        "nombre_commandes": {
          "value_count": {"field": "montant"}
        },
        "panier_moyen": {
          "avg": {"field": "montant"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "user_id"}
        },
        "taux_conversion": {
          "bucket_script": {
            "buckets_path": {
              "commandes": "nombre_commandes",
              "visiteurs": "visiteurs_uniques"
            },
            "script": "params.commandes / params.visiteurs * 100"
          }
        }
      }
    }
  }
}

# EXEMPLE 2: Analyse logs application
# "Erreurs par service, avec top messages"

GET /logs/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {"term": {"level": "ERROR"}},
        {"range": {"@timestamp": {"gte": "now-24h"}}}
      ]
    }
  },
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword",
        "size": 10
      },
      "aggs": {
        "top_messages": {
          "terms": {
            "field": "message.keyword",
            "size": 5
          }
        },
        "dernier_timestamp": {
          "max": {
            "field": "@timestamp"
          }
        }
      }
    }
  }
}

# EXEMPLE 3: Analyse performance web
# "Temps réponse par endpoint, percentiles"

GET /nginx-logs/_search
{
  "size": 0,
  "aggs": {
    "par_endpoint": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"hits": "desc"}
      },
      "aggs": {
        "hits": {
          "value_count": {"field": "response_time"}
        },
        "temps_moyen": {
          "avg": {"field": "response_time"}
        },
        "percentiles": {
          "percentiles": {
            "field": "response_time",
            "percents": [50, 90, 95, 99]
          }
        },
        "lents": {
          "filter": {
            "range": {"response_time": {"gte": 1000}}
          }
        }
      }
    }
  }
}

# EXEMPLE 4: Analyse géographique
# "Requêtes par pays + revenus"

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "par_pays": {
      "terms": {
        "field": "geoip.country_name.keyword",
        "size": 20
      },
      "aggs": {
        "requetes": {
          "value_count": {"field": "@timestamp"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "client_ip"}
        },
        "par_ville": {
          "terms": {
            "field": "geoip.city_name.keyword",
            "size": 5
          }
        }
      }
    }
  }
}

# === CONSEILS PERFORMANCE AGRÉGATIONS ===

# 1. UTILISER size: 0
# Ne pas retourner documents (seulement stats)
{"size": 0}

# 2. LIMITER size des terms
# Top 10-20 suffisant généralement
{"size": 10}

# 3. FILTRER AVANT d'agréger
# Réduire volume données
{
  "query": {"range": {"@timestamp": {"gte": "now-7d"}}},
  "aggs": {...}
}

# 4. ÉVITER terms sur champs high-cardinality
# Exemple: Ne pas faire terms sur:
# - UUIDs (millions de valeurs uniques)
# - Timestamps précis
# - Texte libre
# Préférer: cardinality pour compter uniques

# 5. UTILISER doc_values=false si pas d'agrégations
# Dans mapping, si champ jamais agrégé

# 6. CACHER résultats si possible
# Même query répétée = résultat caché

# 7. PRÉFÉRER filters à queries dans agrégations
# Plus rapide (cachable)

# === Index Templates ===

# Créer template
curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "message": { "type": "text" },
        "level": { "type": "keyword" }
      }
    }
  }
}
'

# Lister templates
curl -X GET "localhost:9200/_index_template?pretty"

# Voir template spécifique
curl -X GET "localhost:9200/_index_template/logs_template?pretty"

# Supprimer template
curl -X DELETE "localhost:9200/_index_template/logs_template?pretty"

# === Aliases ===

# Créer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Créer alias avec filtre
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-errors",
        "filter": {
          "term": { "level": "ERROR" }
        }
      }
    }
  ]
}
'

# Supprimer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "remove": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Déplacer alias (atomique)
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    { "remove": { "index": "logs-2024-01", "alias": "logs-current" } },
    { "add": { "index": "logs-2024-02", "alias": "logs-current" } }
  ]
}
'

# Lister aliases
curl -X GET "localhost:9200/_alias?pretty"
curl -X GET "localhost:9200/logs-*/_alias?pretty"

# === Snapshots (Backups) ===

# Créer repository (filesystem)
curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/elasticsearch"
  }
}
'

# Créer snapshot
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,users",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Lister snapshots
curl -X GET "localhost:9200/_snapshot/my_backup/_all?pretty"

# Voir détails snapshot
curl -X GET "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"

# Restaurer snapshot
curl -X POST "localhost:9200/_snapshot/my_backup/snapshot_1/_restore?pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-2024-01",
  "ignore_unavailable": true,
  "include_global_state": false,
  "rename_pattern": "(.+)",
  "rename_replacement": "restored_$1"
}
'

# Supprimer snapshot
curl -X DELETE "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"


[OK] KIBANA - INTERFACE & FONCTIONNALITÉS (GUIDE COMPLET)

# === QU'EST-CE QUE KIBANA? ===

# Kibana = Interface graphique pour Elasticsearch
# C'est comme Google Analytics mais pour TES données

# ANALOGIE:
# Elasticsearch = Bibliothèque géante avec millions de livres
# Kibana = Système de recherche et catalogues pour trouver/visualiser les livres

# URL d'accès: http://localhost:5601

# === PREMIÈRE CONNEXION ===

# 1. Ouvrir navigateur: http://localhost:5601
# 2. Si sécurité activée:
#    Username: elastic
#    Password: (celui noté à l'installation)
# 3. Tu arrives sur la page d'accueil Kibana

# === NAVIGATION KIBANA ===

# Menu latéral gauche (principales sections):

# [GRAPHIQUE] ANALYTICS
#   - Discover: Explorer les données brutes
#   - Dashboard: Tableaux de bord
#   - Canvas: Présentations pixel-perfect
#   - Maps: Cartes géographiques
#   - Machine Learning: Détection anomalies (licence payante)

# [HAUSSE] OBSERVABILITY
#   - Logs: Vue centralisée logs
#   - APM: Application Performance Monitoring
#   - Metrics: Métriques infrastructure
#   - Uptime: Monitoring disponibilité

# [VERROUILLE] SECURITY
#   - SIEM: Security Information Event Management
#   - Endpoint: Sécurité endpoints

# [CONFIG] MANAGEMENT
#   - Stack Management: Configuration
#   - Dev Tools: Console pour requêtes
#   - Stack Monitoring: Monitoring ELK

# === DISCOVER (EXPLORATION DE DONNÉES) ===

# C'EST QUOI?
# Discover = Google Search pour tes données
# Cherche, filtre, explore les documents indexés

# ÉTAPES POUR COMMENCER:

# 1. CRÉER INDEX PATTERN
# Index Pattern = Dis à Kibana quels index explorer
# Exemple: "logs-*" pour tous index commençant par "logs-"

# Comment créer:
# a) Menu hamburger ([TRIGRAM_FOR_HEAVEN]) > Stack Management > Index Patterns
# b) "Create index pattern"
# c) Pattern name: logs-* (ou ton pattern)
# d) Time field: @timestamp (champ date pour tri chronologique)
# e) "Create index pattern"

# 2. ALLER DANS DISCOVER
# Menu hamburger > Analytics > Discover

# 3. SÉLECTIONNER INDEX PATTERN
# En haut à gauche: dropdown pour choisir "logs-*"

# 4. CHOISIR PÉRIODE
# En haut à droite: Time picker
# Options:
# - Last 15 minutes (défaut)
# - Last 1 hour
# - Last 24 hours
# - Last 7 days
# - Custom (choix précis)

# INTERFACE DISCOVER:

# BARRE DE RECHERCHE (KQL)
# Au milieu en haut, pour chercher dans les données

# KQL = Kibana Query Language
# Syntaxe simple pour rechercher

# Exemples KQL:
response_code: 200                    # Champ = valeur exacte
response_code >= 400                  # Comparaison
message: "error"                      # Contient "error"
message: "database error"             # Phrase (plusieurs mots)
level: ERROR and service: web         # AND logique
status: active or status: pending     # OR logique
NOT status: deleted                   # NOT logique
response_code: (200 or 201)           # Groupement
client_ip: "192.168.1.*"             # Wildcard
@timestamp >= "2024-01-01"           # Date

# HISTOGRAMME (Graphique en haut)
# Montre distribution temporelle des logs
# - Pic = beaucoup d'événements à ce moment
# - Creux = peu d'événements
# - Clique sur barre = zoom sur cette période

# LISTE DES CHAMPS (Colonne gauche)
# Tous les champs disponibles dans les documents
# 
# Actions sur champs:
# 1. Survoler champ -> Icônes apparaissent:
#    (+) Ajouter comme colonne
#    ([RECHERCHE]) Filtrer pour cette valeur
#    ([EYE]) Voir top values
#
# 2. Cliquer champ -> Voir statistiques:
#    - Top 5 valeurs
#    - Nombre d'occurrences
#    - Distribution

# TABLE DES DOCUMENTS (Centre)
# Liste des documents trouvés
# - Par défaut: 500 derniers
# - Triés par @timestamp (plus récent d'abord)
# 
# Pour chaque document:
# - Flèche ([BLACK_RIGHT-POINTING_TRIANGLE]) = Expand pour voir tous champs
# - Table view = Vue tabulaire
# - JSON view = Vue JSON brut

# FILTRES (Au-dessus recherche)
# Filtres visuels appliqués

# AJOUTER FILTRE:
# Méthode 1: Cliquer sur valeur dans document
# - Loupe (+) = "Filter for value" (inclure)
# - Loupe (-) = "Filter out value" (exclure)
#
# Méthode 2: Bouton "+ Add filter"
# - Choisir champ
# - Choisir opérateur (is, is not, exists, etc.)
# - Entrer valeur
# - "Save"

# Exemple:
# Filtre: level "is" "ERROR"
# -> Montre seulement logs niveau ERROR

# Combiner filtres:
# Filter 1: level is ERROR
# Filter 2: service is web
# -> Montre logs ERROR du service web

# SAUVEGARDER RECHERCHE:
# 1. Bouton "Save" (en haut à droite)
# 2. Nom: "Erreurs service web"
# 3. "Save"
# 
# Pour réutiliser:
# Bouton "Open" > Choisir recherche sauvegardée

# CAS D'USAGE DISCOVER:

# 1. DEBUGGING
# "Utilisateur X a eu une erreur hier à 14h30"
# - Time picker: Hier 14:00 - 15:00
# - Filtre: user_id is X
# - Filtre: level is ERROR
# -> Trouver l'erreur exacte en quelques secondes

# 2. INVESTIGATION INCIDENT
# "Le site était lent ce matin entre 9h et 10h"
# - Time picker: Aujourd'hui 09:00 - 10:00
# - Histogramme: Pic visible?
# - Ajouter colonne: response_time
# - Trier par response_time desc
# -> Voir quelles requêtes étaient lentes

# 3. ANALYSE PATTERN
# "Combien d'erreurs 404 par jour?"
# - Time picker: Last 7 days
# - Filtre: response_code is 404
# - Histogramme: Voir distribution
# - Cliquer champ "url.keyword" -> Top 5 URLs 404

# === VISUALIZATIONS (GRAPHIQUES) ===

# C'EST QUOI?
# Transformer données en graphiques visuels
# Comme Excel Charts mais pour Elasticsearch

# TYPES DE VISUALIZATIONS:

# 1. LINE CHART (Graphique ligne)
# Pour: Évolution temporelle
# Exemple: Nombre de logs par heure

# 2. AREA CHART (Graphique aire)
# Pour: Évolution avec remplissage
# Exemple: CPU usage dans le temps

# 3. BAR CHART (Graphique barres)
# Pour: Comparaisons
# Exemple: Logs par service

# 4. PIE CHART (Camembert)
# Pour: Proportions
# Exemple: Répartition logs par niveau (80% INFO, 15% WARN, 5% ERROR)

# 5. DATA TABLE (Tableau)
# Pour: Listes et rankings
# Exemple: Top 10 URLs les plus visitées

# 6. METRIC (Métrique unique)
# Pour: Chiffre clé
# Exemple: Nombre total de logs aujourd'hui

# 7. GAUGE (Jauge)
# Pour: Indicateur avec seuils
# Exemple: CPU usage avec zones vert/orange/rouge

# 8. TAG CLOUD (Nuage de mots)
# Pour: Fréquence termes
# Exemple: Mots les plus fréquents dans messages

# 9. HEAT MAP (Carte chaleur)
# Pour: Matrice de valeurs
# Exemple: Activité par heure et jour de semaine

# 10. MAPS (Carte géographique)
# Pour: Données géolocalisées
# Exemple: Requêtes par pays

# CRÉER UNE VISUALIZATION:

# MÉTHODE 1: LENS (Moderne, recommandé)

# 1. Menu > Visualize Library
# 2. "Create visualization"
# 3. "Lens" (outil drag-and-drop)
# 4. Choisir index pattern: logs-*
# 
# Interface Lens:
# - Gauche: Champs disponibles (glisser-déposer)
# - Centre: Aperçu graphique
# - Droite: Configuration

# EXEMPLE: Graphique ligne - Logs par heure

# 1. Type: Line
# 2. Axe X (horizontal):
#    - Glisser "@timestamp" depuis gauche
#    - Automatiquement: Date Histogram
#    - Intervalle: Hourly (par heure)
# 3. Axe Y (vertical):
#    - Par défaut: Count (nombre de documents)
# 4. Aperçu s'affiche!
# 5. Personnaliser:
#    - Titre axe Y: "Nombre de logs"
#    - Titre axe X: "Temps"
#    - Couleur ligne: Bleu
# 6. "Save" > Nom: "Logs par heure"

# EXEMPLE: Camembert - Répartition par niveau

# 1. Type: Pie
# 2. Slice by (découper par):
#    - Glisser "level.keyword"
#    - Automatiquement: Top 10 values
# 3. Size by:
#    - Count (nombre de docs par niveau)
# 4. Voir: 80% INFO, 15% WARN, 5% ERROR
# 5. "Save" > Nom: "Logs par niveau"

# EXEMPLE: Tableau - Top 10 URLs

# 1. Type: Table
# 2. Rows (lignes):
#    - Glisser "url.keyword"
#    - Top 10 values
# 3. Metrics:
#    - Count (nombre de fois visitée)
# 4. Tri: Par Count descending
# 5. "Save" > Nom: "Top 10 URLs"

# EXEMPLE: Métrique - Total logs aujourd'hui

# 1. Type: Metric
# 2. Metric value:
#    - Count
# 3. Time range: Today
# 4. Format: Nombre (ex: 1,234,567)
# 5. "Save" > Nom: "Total logs"

# MÉTHODE 2: VISUALIZATION TYPES (Classique)

# Plus de contrôle mais moins intuitif
# Menu > Visualize Library > Create visualization > Choisir type

# EXEMPLE: Vertical Bar - Logs par service

# 1. Choisir: "Vertical Bar"
# 2. Source: logs-*
# 3. Y-axis (Metrics):
#    - Aggregation: Count
#    - Label: "Nombre de logs"
# 4. X-axis (Buckets):
#    - Aggregation: Terms
#    - Field: service.keyword
#    - Size: 10
#    - Order: Metric - Count descending
#    - Label: "Service"
# 5. "Update" ([BLACK_RIGHT-POINTING_TRIANGLE]) pour voir
# 6. "Save" > Nom: "Logs par service"

# PERSONNALISATION VISUALIZATIONS:

# COULEURS:
# - Single color: Une couleur
# - By value: Couleur selon valeur
# - Custom palette: Palette personnalisée

# LÉGENDES:
# - Position: Right, Left, Top, Bottom
# - Afficher/Masquer

# AXES:
# - Titre
# - Échelle: Linear, Log
# - Min/Max

# TOOLTIPS:
# - Infos au survol
# - Format

# === DASHBOARDS (TABLEAUX DE BORD) ===

# C'EST QUOI?
# Dashboard = Collection de visualizations
# Comme un tableau de bord de voiture: tout en un coup d'œil

# CRÉER DASHBOARD:

# 1. Menu > Dashboard
# 2. "Create dashboard"
# 3. "Add from library" ou "Create visualization"
# 
# AJOUTER VISUALIZATIONS:
# 4. "Add from library"
# 5. Cocher: "Logs par heure", "Logs par niveau", "Top 10 URLs"
# 6. "Add"
# 
# ARRANGER:
# 7. Glisser-déposer pour positionner
# 8. Coins pour redimensionner
# 9. Layout automatique ou manuel
#
# SAUVEGARDER:
# 10. "Save" > Nom: "Dashboard Logs Production"
# 11. Description: "Vue d'ensemble logs prod"
# 12. "Save"

# FONCTIONNALITÉS DASHBOARD:

# 1. FILTRES GLOBAUX
# Appliqués à TOUTES les visualizations
# - Ajouter filtre en haut
# - Ex: level is ERROR
# -> Toutes les viz montrent seulement erreurs

# 2. TIME PICKER GLOBAL
# Change période pour tout le dashboard
# - Last 15 minutes
# - Last 24 hours
# - Custom range

# 3. DRILL-DOWN
# Cliquer sur élément -> Filtre ajouté
# Ex: Cliquer "ERROR" dans camembert
# -> Dashboard filtré sur erreurs seulement

# 4. REFRESH AUTO
# Actualisation automatique
# - Cliquer horloge ([HEURE])
# - Choisir intervalle: 10s, 30s, 1m, 5m
# -> Dashboard se rafraîchit automatiquement

# 5. MODE PLEIN ÉCRAN
# Pour affichage grand écran (TV, monitoring room)
# - Bouton "Full screen"
# - Appuyer ESC pour sortir

# 6. PARTAGE
# - Share -> Permalink (lien permanent)
# - Share -> Embed code (iframe HTML)
# - Share -> PDF/PNG (export image)

# EXEMPLE DASHBOARD E-COMMERCE:

# Visualizations:
# 1. Metric: Visiteurs actuels (rafraîchi 10s)
# 2. Line: Visites par heure (24h)
# 3. Pie: Répartition devices (Desktop/Mobile/Tablet)
# 4. Bar: Top 10 produits vus
# 5. Table: Derniers achats
# 6. Map: Visiteurs par pays
# 7. Gauge: Taux conversion (%)
# 8. Line: Revenus par heure

# Layout:
# +------------------+------------------+
# | Visiteurs: 1,234 | Taux conv: 3.2% |
# +------------------+------------------+
# | Visites (line - 24h)                |
# +-------------------------------------+
# | Devices (pie) | Top produits (bar) |
# +---------------+--------------------+
# | Map mondial   | Derniers achats    |
# +---------------+--------------------+
# | Revenus (line)                      |
# +-------------------------------------+

# === CANVAS (PRÉSENTATIONS) ===

# C'EST QUOI?
# Canvas = PowerPoint mais avec données temps réel
# Design pixel-perfect pour présentations

# QUAND UTILISER?
# - Présentation executive
# - Affichage TV monitoring
# - Rapport visuel marketing
# - Infographie dynamique

# CRÉER WORKPAD:

# 1. Menu > Canvas
# 2. "Create workpad"
# 3. Template ou "Start from scratch"
#
# INTERFACE:
# - Toolbar haut: Éléments à ajouter
# - Canvas centre: Zone de design
# - Sidebar droite: Propriétés élément

# ÉLÉMENTS DISPONIBLES:

# 1. TEXT (Texte)
# - Titres, labels, descriptions
# - Font, size, color personnalisables

# 2. SHAPE (Formes)
# - Rectangle, cercle, ligne
# - Pour structure visuelle

# 3. IMAGE
# - Logo, icônes, illustrations
# - Upload ou URL

# 4. ELEMENT (Données)
# - Metric: Chiffre de Elasticsearch
# - Chart: Graphique
# - Table: Tableau
# - Markdown: Texte formaté

# 5. FILTER
# - Time filter
# - Dropdown filter

# EXEMPLE: Rapport mensuel

# Page 1: Couverture
# - Background: Dégradé bleu
# - Logo entreprise
# - Titre: "Rapport Janvier 2024"
# - Sous-titre: "Analyse trafic web"

# Page 2: KPIs
# - 4 grandes métriques:
#   * Visiteurs uniques
#   * Pages vues
#   * Taux rebond
#   * Temps moyen session
# - Design carte avec icône

# Page 3: Graphiques
# - Évolution visites (line)
# - Top pages (bar horizontal)
# - Sources trafic (pie)

# Page 4: Géographie
# - Carte mondiale visiteurs
# - Top 10 pays (table)

# FONCTIONNALITÉS:

# - Multiple pages (slides)
# - Animations transitions
# - Auto-play (diaporama auto)
# - Export PDF/PNG
# - Partage via lien
# - Mode présentation plein écran

# === MAPS (CARTES GÉOGRAPHIQUES) ===

# C'EST QUOI?
# Visualiser données avec coordonnées géographiques
# Comme Google Maps avec tes données

# PRÉREQUIS:
# Données avec champ geo_point dans Elasticsearch
# Exemple:
# {
#   "client_ip": "8.8.8.8",
#   "geoip": {
#     "location": {
#       "lat": 37.386,
#       "lon": -122.0838
#     },
#     "country": "United States"
#   }
# }

# CRÉER MAP:

# 1. Menu > Maps
# 2. "Create map"
# 3. "Add layer"

# TYPES DE LAYERS:

# 1. DOCUMENTS (Points)
# - Chaque document = 1 point sur carte
# - Exemple: IP clientes
# Configuration:
# - Index: logs-*
# - Geospatial field: geoip.location
# - Tooltip: Afficher IP, country

# 2. CLUSTERS
# - Groupe points proches
# - Exemple: 100 requêtes Paris -> 1 cercle "100"
# - Zoom: Cercle se décompose

# 3. HEAT MAP
# - Carte de chaleur (densité)
# - Rouge = beaucoup, Bleu = peu
# - Exemple: Zones activité forte

# 4. CHOROPLETH
# - Régions colorées
# - Exemple: Pays colorés selon revenus
# - USA rouge (1M$), France orange (500K$), etc.

# EXEMPLE: Attaques réseau

# Layer 1: Choropleth - Pays sources attaques
# - Agrégation: Count par pays
# - Couleur: Rouge = beaucoup, Vert = peu

# Layer 2: Lines - Flux attaques
# - Source: IP attaquant
# - Destination: Serveur
# - Lignes rouges entre pays

# Layer 3: Points - Serveurs
# - Nos serveurs (points verts)

# PERSONNALISATION:

# - Basemap: Streets, Satellite, Dark, Light
# - Zoom initial
# - Centre initial
# - Bounds (limiter zone)
# - Tooltips (infos au survol)
# - Symboles (icônes custom)
# - Couleurs (palettes)

# === ALERTING (ALERTES) ===

# C'EST QUOI?
# Surveillance automatique + notifications
# "Préviens-moi si X arrive"

# CRÉER ALERTE:

# 1. Menu hamburger > Stack Management
# 2. "Rules and Connectors"
# 3. "Create rule"

# TYPES DE RÈGLES:

# 1. INDEX THRESHOLD
# "Si nombre de documents dépasse seuil"
# Exemple: Plus de 100 erreurs en 5 minutes

# Configuration:
# - Name: "Trop d'erreurs"
# - Index: logs-*
# - When: count()
# - Over: all documents
# - For the last: 5 minutes
# - Threshold: Is above 100
# - Group by: service (optionnel)
# - Filter: level: "ERROR"

# 2. ELASTICSEARCH QUERY
# Query DSL personnalisée
# Plus flexible mais plus complexe

# 3. ANOMALY DETECTION (ML)
# Détection automatique anomalies
# Nécessite licence Gold+

# ACTIONS (Que faire quand alerte?):

# 1. EMAIL
# - To: ops@example.com
# - Subject: "ALERTE: {{context.rule.name}}"
# - Body: "{{context.hits}} erreurs détectées"

# 2. SLACK
# - Connector: Webhook Slack
# - Channel: #alerts
# - Message: "[ATTENTION] Alerte: {{context.message}}"

# 3. WEBHOOK (HTTP)
# - URL: https://api.example.com/alert
# - Method: POST
# - Body: JSON avec détails

# 4. PAGERDUTY
# - Intégration PagerDuty
# - Severity: Critical
# - Description: Alerte détails

# 5. INDEX (Écrire dans Elasticsearch)
# - Index: alerts-*
# - Document: Détails alerte

# EXEMPLE COMPLET:

# Règle: "Erreurs 5xx serveur web"
# Type: Index threshold
# Check every: 1 minute
# Conditions:
# - Index: nginx-logs-*
# - When: count()
# - Over: all documents
# - For: last 5 minutes
# - Is above: 50
# - Filter: response_code >= 500 AND service: "web"
# Actions:
# - Email ops
# - Slack #incidents
# - PagerDuty si production

# === DEV TOOLS (CONSOLE) ===

# C'EST QUOI?
# Console pour envoyer requêtes Elasticsearch directement
# Comme terminal SQL mais pour Elasticsearch

# OUVRIR:
# Menu > Dev Tools

# INTERFACE:
# - Gauche: Éditeur requêtes
# - Droite: Résultats

# UTILISATION:

# 1. Taper requête:
GET /_cluster/health

# 2. Curseur sur ligne
# 3. Cliquer [BLACK_RIGHT-POINTING_TRIANGLE] ou Ctrl+Enter
# 4. Résultat s'affiche à droite

# FONCTIONNALITÉS:

# - AUTOCOMPLÉTION: Ctrl+Space
# - FORMATER: Ctrl+I
# - HISTORIQUE: ^v pour naviguer
# - MULTI-REQUÊTES: Séparer par ligne vide

# EXEMPLES:

# Santé cluster
GET /_cluster/health

# Lister index
GET /_cat/indices?v

# Recherche
GET /logs-*/_search
{
  "query": {
    "match": {
      "level": "ERROR"
    }
  }
}

# Créer document
POST /users/_doc
{
  "name": "Jean",
  "age": 30
}

# === STACK MANAGEMENT ===

# CONFIGURATION CENTRALE DE ELK

# INDEX PATTERNS:
# - Créer/gérer patterns
# - Définir champ timestamp
# - Refresh fields

# SAVED OBJECTS:
# - Importer/Exporter dashboards
# - Sauvegardes visualizations
# - Format: JSON (ndjson)

# Exporter dashboard:
# 1. Saved Objects
# 2. Cocher dashboard
# 3. "Export"
# 4. Télécharge .ndjson

# Importer:
# 1. "Import"
# 2. Glisser fichier .ndjson
# 3. Résoudre conflits
# 4. "Import"

# INDEX LIFECYCLE MANAGEMENT (ILM):
# - Politiques gestion cycle vie
# - Hot -> Warm -> Cold -> Delete
# - Automatisation retention

# ADVANCED SETTINGS:
# - Thème sombre: discover:enableDarkTheme
# - Langue UI
# - Format dates
# - Timezone

# === SPACES (ESPACES) ===

# C'EST QUOI?
# Espaces isolés pour organiser par équipe/projet
# Comme dossiers séparés

# EXEMPLE:
# - Space "Marketing": Dashboards trafic web
# - Space "DevOps": Dashboards infrastructure
# - Space "Security": Dashboards sécurité

# CRÉER SPACE:

# 1. Stack Management > Spaces
# 2. "Create space"
# 3. Name: "Marketing"
# 4. Initials: "MK" (avatar)
# 5. Color: Bleu
# 6. Description: "Espace équipe marketing"
# 7. "Create"

# CHANGER SPACE:
# Menu en haut à gauche > Choisir space

# === CONSEILS UTILISATION KIBANA ===

# PERFORMANCE:

# 1. LIMITER time range si beaucoup données
# - Last 15 min plutôt que Last 7 days
#
# 2. UTILISER filtres plutôt que queries larges
# - Filter: service is "web" (rapide)
# - Query: * (lent, tout scanner)
#
# 3. SAUVEGARDER recherches fréquentes
# - Évite retaper
#
# 4. REFRESH AUTO seulement si nécessaire
# - Consomme ressources
#
# 5. DASHBOARDS légers
# - 8-12 viz max par dashboard
# - Séparer si plus

# ORGANISATION:

# 1. NOMMER clairement
# - [OK] "Erreurs Production - Dernières 24h"
# - [X] "Dashboard 1"
#
# 2. DESCRIPTIONS
# - Ajouter description dashboards
# - Expliquer à quoi ça sert
#
# 3. TAGS
# - Tagger dashboards: "production", "monitoring"
# - Facilite recherche
#
# 4. DOSSIERS
# - Organiser dans Saved Objects
#
# 5. CONVENTIONS
# - Préfixe: "PROD -", "DEV -"
# - Cohérence nommage

# SÉCURITÉ:

# 1. RÔLES appropriés
# - Lecture seule pour viewers
# - Édition pour analysts
#
# 2. SPACES pour isolation
# - Équipe A ne voit pas équipe B
#
# 3. DASHBOARDS en read-only
# - Évite modifications accidentelles Elasticsearch

# === Machine Learning (Détection d'anomalies) ===

# Nécessite licence (Gold ou supérieure)
# 1. Aller dans "Machine Learning"
# 2. "Create job"
# 3. Choisir type:
#    - Single metric (une métrique)
#    - Multi metric (plusieurs métriques)
#    - Population (comportement groupe)
# 4. Configurer détecteurs
# 5. Lancer job

# === Alerting (Alertes) ===

# 1. Aller dans "Stack Management" > "Rules and Connectors"
# 2. "Create rule"
# 3. Types:
#    - Index threshold: Seuil sur nombre documents
#    - Elasticsearch query: Query personnalisée
#    - Anomaly detection: Basé sur ML
# 4. Configurer conditions
# 5. Configurer actions (email, Slack, webhook, etc.)

# Exemple: Alerte si erreurs > 100 en 5 minutes
# Rule type: Index threshold
# Index: logs-*
# When: count()
# Over: all documents
# For the last: 5 minutes
# Is above: 100
# Filter: level: "ERROR"

# === Dev Tools (Console) ===

# Console pour exécuter requêtes Elasticsearch
# 1. Aller dans "Dev Tools"
# 2. Taper requêtes:

GET /_cluster/health

GET /logs-*/_search
{
  "query": {
    "match_all": {}
  }
}

POST /logs-2024-01/_doc
{
  "message": "Test log",
  "level": "INFO",
  "@timestamp": "2024-01-15T10:00:00"
}

# Autocomplétion: Ctrl+Space
# Exécuter: Ctrl+Enter
# Formater: Ctrl+I

# === Stack Management ===

# 1. Index Patterns:
#    - Créer pattern pour découvrir données
#    - Ex: logs-*, filebeat-*
#    - Définir champ timestamp

# 2. Index Lifecycle Management (ILM):
#    - Gérer cycle de vie des index
#    - Hot > Warm > Cold > Delete

# 3. Saved Objects:
#    - Importer/Exporter dashboards, visualizations
#    - Format JSON

# 4. Advanced Settings:
#    - Personnaliser Kibana
#    - Thème sombre: discover:enableDarkTheme

# === Spaces (Espaces) ===

# Organiser dashboards par équipe/projet
# 1. Stack Management > Spaces
# 2. Create space
# 3. Assigner visualizations, dashboards
# 4. Changer d'espace: menu en haut à gauche


[OK] LOGSTASH - EXEMPLES COMPLETS

# === Pipeline: Logs Apache/Nginx ===

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    sincedb_path => "/var/lib/logstash/sincedb_nginx"
    tags => ["nginx", "access"]
  }
}

filter {
  if "nginx" in [tags] {
    grok {
      match => { 
        "message" => "%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code:int} %{NUMBER:bytes:int} \"%{DATA:referrer}\" \"%{DATA:user_agent}\"" 
      }
    }
    
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
      target => "@timestamp"
    }
    
    geoip {
      source => "client_ip"
      target => "geoip"
    }
    
    useragent {
      source => "user_agent"
      target => "user_agent_parsed"
    }
    
    mutate {
      remove_field => ["message", "timestamp"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs JSON ===

input {
  tcp {
    port => 5000
    codec => json
  }
}

filter {
  # Les données sont déjà en JSON, parser automatique
  
  if [level] == "ERROR" or [level] == "FATAL" {
    mutate {
      add_tag => ["error"]
    }
  }
  
  # Extraire info de stack trace
  if [stack_trace] {
    mutate {
      add_field => { "has_stack_trace" => true }
    }
  }
}

output {
  if "error" in [tags] {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-errors-%{+YYYY.MM.dd}"
    }
  } else {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-logs-%{+YYYY.MM.dd}"
    }
  }
}

# === Pipeline: Logs Syslog ===

input {
  syslog {
    port => 514
    type => "syslog"
  }
}

filter {
  if [type] == "syslog" {
    grok {
      match => { 
        "message" => "%{SYSLOGBASE} %{GREEDYDATA:syslog_message}" 
      }
    }
    
    date {
      match => [ "timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
      target => "@timestamp"
    }
    
    mutate {
      remove_field => ["message"]
      rename => { "syslog_message" => "message" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "syslog-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs Docker ===

input {
  file {
    path => "/var/lib/docker/containers/*/*.log"
    codec => json
    type => "docker"
  }
}

filter {
  if [type] == "docker" {
    json {
      source => "log"
    }
    
    mutate {
      rename => { "log" => "message" }
    }
    
    # Extraire container ID du path
    grok {
      match => { 
        "path" => "/var/lib/docker/containers/%{DATA:container_id}/%{GREEDYDATA}" 
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs application Java ===

input {
  file {
    path => "/var/log/app/*.log"
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601}"
      negate => true
      what => "previous"
    }
  }
}

filter {
  grok {
    match => { 
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:log_message}" 
    }
  }
  
  date {
    match => [ "timestamp", "yyyy-MM-dd HH:mm:ss,SSS" ]
    target => "@timestamp"
  }
  
  # Détecter stack traces
  if [log_message] =~ /^(\s+at\s|Caused by:)/ {
    mutate {
      add_tag => ["stacktrace"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "java-app-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Métriques système (depuis Metricbeat) ===

input {
  beats {
    port => 5044
    type => "metrics"
  }
}

filter {
  if [type] == "metrics" {
    # Calculer pourcentage CPU
    if [system][cpu] {
      ruby {
        code => "
          total = event.get('[system][cpu][total][pct]')
          if total
            event.set('[system][cpu][total][percent]', (total * 100).round(2))
          end
        "
      }
    }
    
    # Ajouter alertes si seuils dépassés
    if [system][cpu][total][pct] and [system][cpu][total][pct] > 0.9 {
      mutate {
        add_tag => ["high_cpu"]
      }
    }
    
    if [system][memory][used][pct] and [system][memory][used][pct] > 0.9 {
      mutate {
        add_tag => ["high_memory"]
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "metricbeat-%{+YYYY.MM.dd}"
  }
  
  # Alerte si ressources critiques
  if "high_cpu" in [tags] or "high_memory" in [tags] {
    email {
      to => "ops@example.com"
      subject => "Alert: High resource usage on %{host.name}"
      body => "CPU: %{[system][cpu][total][percent]}%\nMemory: %{[system][memory][used][pct]}%"
    }
  }
}


[OK] FILEBEAT - EXEMPLES COMPLETS

# === Configuration: Logs multiples applications ===

filebeat.inputs:

# Application web
- type: log
  enabled: true
  paths:
    - /var/log/webapp/*.log
  fields:
    app: webapp
    environment: production
  fields_under_root: true
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

# API logs
- type: log
  enabled: true
  paths:
    - /var/log/api/*.log
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    app: api
    environment: production
  fields_under_root: true

# Base de données logs
- type: log
  enabled: true
  paths:
    - /var/log/postgresql/*.log
  exclude_lines: ['^DEBUG']
  fields:
    app: database
    type: postgresql
  fields_under_root: true

processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~

output.logstash:
  hosts: ["localhost:5044"]
  loadbalance: true

# === Configuration: Docker containers ===

filebeat.inputs:
- type: container
  enabled: true
  paths:
    - '/var/lib/docker/containers/*/*.log'
  
  processors:
    - add_docker_metadata:
        host: "unix:///var/run/docker.sock"
    
    - decode_json_fields:
        fields: ["message"]
        target: ""
        overwrite_keys: true
    
    # Enrichir avec labels Docker
    - add_fields:
        target: docker
        fields:
          container.labels: ~

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "docker-%{[agent.version]}-%{+yyyy.MM.dd}"

setup.template.name: "docker"
setup.template.pattern: "docker-*"

# === Configuration: Module Nginx avec personnalisation ===

filebeat.modules:
- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log*"]
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log*"]

processors:
  - drop_event:
      when:
        or:
          - equals:
              http.response.status_code: 200
          - equals:
              http.response.status_code: 301
  
  - if:
      equals:
        http.response.status_code: 404
    then:
      - add_tags:
          tags: [not_found]
  
  - if:
        range:
          http.response.status_code:
            gte: 500
    then:
      - add_tags:
          tags: [server_error]

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "nginx-%{[agent.version]}-%{+yyyy.MM.dd}"

# === Configuration: Monitoring Kubernetes ===

filebeat.autodiscover:
  providers:
    - type: kubernetes
      node: ${NODE_NAME}
      hints.enabled: true
      hints.default_config:
        type: container
        paths:
          - /var/log/containers/*${data.kubernetes.container.id}.log

processors:
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      - logs_path:
          logs_path: "/var/log/containers/"
  
  - drop_event:
      when:
        equals:
          kubernetes.namespace: "kube-system"

output.elasticsearch:
  hosts: ["${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}"]
  username: ${ELASTICSEARCH_USERNAME}
  password: ${ELASTICSEARCH_PASSWORD}
  index: "k8s-logs-%{[agent.version]}-%{+yyyy.MM.dd}"


[OK] PATTERNS GROK PERSONNALISÉS

# Créer fichier: /etc/logstash/patterns/custom_patterns

# === Format ===
PATTERN_NAME regex

# === Exemples ===

# Log application custom
MYAPP_LOG %{TIMESTAMP_ISO8601:timestamp} \| %{LOGLEVEL:level} \| %{DATA:module} \| %{GREEDYDATA:message}

# Log avec user ID
MYAPP_USER_LOG \[%{DATA:user_id}\] %{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA:message}

# Format de transaction
TRANSACTION_ID TXN-%{INT:transaction_id}
TRANSACTION_LOG %{TRANSACTION_ID} - %{WORD:status} - %{NUMBER:amount:float} %{WORD:currency}

# Email pattern
EMAIL_ADDR [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}

# IP avec port
IPPORT %{IP:ip}:%{INT:port}

# === Utiliser dans Logstash ===

filter {
  grok {
    patterns_dir => ["/etc/logstash/patterns"]
    match => { 
      "message" => "%{MYAPP_LOG}" 
    }
  }
}


[OK] INDEX LIFECYCLE MANAGEMENT (ILM)

# ILM = Gérer automatiquement le cycle de vie des index
# Phases: Hot > Warm > Cold > Frozen > Delete

# === Créer politique ILM ===

curl -X PUT "localhost:9200/_ilm/policy/logs_policy?pretty" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d",
            "max_docs": 10000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "searchable_snapshot": {
            "snapshot_repository": "my_backup"
          },
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
'

# === Appliquer politique à index template ===

curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs_policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}
'

# === Créer index initial avec alias ===

curl -X PUT "localhost:9200/logs-000001?pretty" -H 'Content-Type: application/json' -d'
{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}
'

# === Voir statut ILM ===

# Lister politiques
curl -X GET "localhost:9200/_ilm/policy?pretty"

# Voir politique spécifique
curl -X GET "localhost:9200/_ilm/policy/logs_policy?pretty"

# Expliquer état ILM d'un index
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# === Gestion ILM ===

# Arrêter ILM
curl -X POST "localhost:9200/_ilm/stop?pretty"

# Démarrer ILM
curl -X POST "localhost:9200/_ilm/start?pretty"

# Statut ILM
curl -X GET "localhost:9200/_ilm/status?pretty"

# Forcer rollover manuel
curl -X POST "localhost:9200/logs/_rollover?pretty"

# Réessayer action échouée
curl -X POST "localhost:9200/logs-000001/_ilm/retry?pretty"

# Supprimer index de ILM
curl -X POST "localhost:9200/logs-000001/_ilm/remove?pretty"


[OK] SÉCURITÉ - CONFIGURATION

# === Activer X-Pack Security ===

# Dans elasticsearch.yml
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

# Générer certificats
cd /usr/share/elasticsearch
bin/elasticsearch-certutil ca
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12

# Copier certificats
cp elastic-certificates.p12 /etc/elasticsearch/
chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-certificates.p12

# Configuration SSL dans elasticsearch.yml
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12

# Redémarrer Elasticsearch
sudo systemctl restart elasticsearch

# === Configurer mots de passe ===

# Mode interactif
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

# Mode automatique (génère mots de passe aléatoires)
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto

# Utilisateurs créés:
# - elastic (superuser)
# - kibana_system (pour Kibana)
# - logstash_system (pour Logstash)
# - beats_system (pour Beats)
# - apm_system (pour APM)
# - remote_monitoring_user

# === Changer mot de passe utilisateur ===

curl -X POST "localhost:9200/_security/user/elastic/_password?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "password" : "nouveau_mot_de_passe"
}
'

# === Créer utilisateur personnalisé ===

curl -X POST "localhost:9200/_security/user/mon_utilisateur?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "password" : "password123",
  "roles" : [ "kibana_admin", "monitoring_user" ],
  "full_name" : "Jean Dupont",
  "email" : "jean@example.com"
}
'

# === Rôles prédéfinis ===

# superuser - Accès complet
# kibana_admin - Admin Kibana
# kibana_user - Utilisateur Kibana
# monitoring_user - Voir monitoring
# ingest_admin - Gérer pipelines
# logstash_admin - Admin Logstash
# beats_admin - Admin Beats
# reporting_user - Générer rapports
# viewer - Lecture seule

# === Créer rôle personnalisé ===

curl -X POST "localhost:9200/_security/role/logs_reader?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "cluster": ["monitor"],
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read", "view_index_metadata"]
    }
  ]
}
'

# === Créer rôle avec Field Level Security ===

curl -X POST "localhost:9200/_security/role/limited_user?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read"],
      "field_security": {
        "grant": ["@timestamp", "message", "level"],
        "except": ["password", "credit_card"]
      },
      "query": "{\"match\": {\"department\": \"sales\"}}"
    }
  ]
}
'

# === Configuration Kibana avec sécurité ===

# Dans kibana.yml
elasticsearch.username: "kibana_system"
elasticsearch.password: "mot_de_passe"

# SSL
elasticsearch.ssl.verificationMode: certificate
elasticsearch.ssl.certificateAuthorities: [ "/path/to/ca.crt" ]

# Redémarrer Kibana
sudo systemctl restart kibana

# === Configuration Logstash avec sécurité ===

# Dans pipeline
output {
  elasticsearch {
    hosts => ["https://localhost:9200"]
    user => "logstash_system"
    password => "mot_de_passe"
    ssl => true
    cacert => "/path/to/ca.crt"
    index => "logs-%{+YYYY.MM.dd}"
  }
}

# === Configuration Filebeat avec sécurité ===

# Dans filebeat.yml
output.elasticsearch:
  hosts: ["https://localhost:9200"]
  username: "beats_system"
  password: "mot_de_passe"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

setup.kibana:
  host: "https://localhost:5601"
  username: "elastic"
  password: "mot_de_passe"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

# === API Keys (Alternative aux mots de passe) ===

# Créer API key
curl -X POST "localhost:9200/_security/api_key?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "name": "my-api-key",
  "role_descriptors": {
    "logs_writer": {
      "cluster": ["monitor"],
      "index": [
        {
          "names": ["logs-*"],
          "privileges": ["create_index", "write"]
        }
      ]
    }
  }
}
'

# Réponse contient: id et api_key
# Utiliser: base64(id:api_key)

# Utiliser dans Filebeat
output.elasticsearch:
  hosts: ["localhost:9200"]
  api_key: "id:api_key"

# Lister API keys
curl -X GET "localhost:9200/_security/api_key?pretty" -u elastic

# Révoquer API key
curl -X DELETE "localhost:9200/_security/api_key?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "id": "key_id"
}
'


[OK] MONITORING & PERFORMANCE

# === Monitoring du cluster ===

# Santé cluster
curl -X GET "localhost:9200/_cluster/health?pretty"

# Stats cluster
curl -X GET "localhost:9200/_cluster/stats?pretty"

# État des nœuds
curl -X GET "localhost:9200/_nodes/stats?pretty"

# Tâches en cours
curl -X GET "localhost:9200/_tasks?pretty"

# Tâches détaillées
curl -X GET "localhost:9200/_tasks?detailed=true&actions=*search&pretty"

# Annuler tâche
curl -X POST "localhost:9200/_tasks/task_id/_cancel?pretty"

# === Hot Threads (Debug performance) ===

curl -X GET "localhost:9200/_nodes/hot_threads?pretty"

# === Monitoring des index ===

# Stats index
curl -X GET "localhost:9200/_stats?pretty"
curl -X GET "localhost:9200/logs-*/_stats?pretty"

# Segments info
curl -X GET "localhost:9200/_cat/segments?v"

# Recovery info
curl -X GET "localhost:9200/_cat/recovery?v"

# Shards allocation
curl -X GET "localhost:9200/_cat/shards?v"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,node&s=index"

# === Cache & Memory ===

# Clear cache
curl -X POST "localhost:9200/_cache/clear?pretty"
curl -X POST "localhost:9200/logs-*/_cache/clear?pretty"

# Field data cache
curl -X POST "localhost:9200/_cache/clear?fielddata=true&pretty"

# Query cache
curl -X POST "localhost:9200/_cache/clear?query=true&pretty"

# Request cache
curl -X POST "localhost:9200/_cache/clear?request=true&pretty"

# === Optimisation ===

# Forcemerge (optimiser segments)
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1&pretty"

# Refresh (rendre documents cherchables)
curl -X POST "localhost:9200/_refresh?pretty"
curl -X POST "localhost:9200/logs-*/_refresh?pretty"

# Flush (écrire sur disque)
curl -X POST "localhost:9200/_flush?pretty"

# === Allocation des shards ===

# Voir allocation
curl -X GET "localhost:9200/_cat/allocation?v"

# Explication allocation
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Réallouer shard manuellement
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "move": {
        "index": "logs-2024-01",
        "shard": 0,
        "from_node": "node1",
        "to_node": "node2"
      }
    }
  ]
}
'

# Réessayer shards échoués
curl -X POST "localhost:9200/_cluster/reroute?retry_failed=true&pretty"

# === Paramètres cluster ===

# Voir settings
curl -X GET "localhost:9200/_cluster/settings?pretty&include_defaults=true"

# Désactiver allocation (maintenance)
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "none"
  }
}
'

# Réactiver allocation
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all"
  }
}
'

# Limiter recovery concurrent
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.node_concurrent_recoveries": 2
  }
}
'

# === Monitoring avec Stack Monitoring ===

# Activer dans Kibana: Stack Monitoring
# Automatiquement collecte métriques Elasticsearch, Logstash, Kibana

# Ou configurer manuellement dans elasticsearch.yml
xpack.monitoring.collection.enabled: true
xpack.monitoring.elasticsearch.collection.enabled: true

# Voir données monitoring
curl -X GET "localhost:9200/.monitoring-es-*/_search?pretty"

# === Métriques JVM ===

curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# Heap usage
curl -X GET "localhost:9200/_nodes/stats?filter_path=nodes.*.jvm.mem.heap_*&pretty"

# GC stats
curl -X GET "localhost:9200/_nodes/stats?filter_path=nodes.*.jvm.gc&pretty"

# === Slow logs ===

# Configuration dans elasticsearch.yml ou dynamique:

# Slow search logs
curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.query.debug": "2s",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.level": "info"
}
'

# Slow index logs
curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.threshold.index.info": "5s",
  "index.indexing.slowlog.level": "info"
}
'

# Logs dans: /var/log/elasticsearch/cluster-name_index_search_slowlog.log


[OK] DÉPANNAGE & PROBLÈMES COURANTS

# === Problème: Elasticsearch ne démarre pas ===

# Vérifier logs
sudo journalctl -u elasticsearch.service -f
tail -f /var/log/elasticsearch/elasticsearch.log

# Vérifier configuration
/usr/share/elasticsearch/bin/elasticsearch -V

# Vérifier ports
sudo netstat -tulpn | grep 9200
sudo lsof -i :9200

# Vérifier permissions
ls -la /var/lib/elasticsearch
ls -la /var/log/elasticsearch

# Réparer permissions
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/log/elasticsearch

# === Problème: Mémoire insuffisante ===

# Erreur: "OutOfMemoryError"
# Solution: Augmenter heap JVM

# Éditer /etc/elasticsearch/jvm.options
-Xms4g
-Xmx4g

# Règle: 50% RAM max, ne pas dépasser 32GB

# Vérifier utilisation mémoire
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# === Problème: Cluster status YELLOW ===

# Cause: Replicas non assignés
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Solution 1: Réduire nombre de replicas
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# Solution 2: Ajouter nœuds au cluster

# === Problème: Cluster status RED ===

# Cause: Shards primaires manquants (GRAVE!)
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Expliquer pourquoi shard non assigné
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Solution: Restaurer depuis snapshot si possible
# Ou réallouer manuellement (risque perte données)
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "mon-index",
        "shard": 0,
        "node": "node-1",
        "accept_data_loss": true
      }
    }
  ]
}
'

# === Problème: Disque plein ===

# Elasticsearch bloque écriture si disque > 95% plein

# Vérifier espace disque
df -h
curl -X GET "localhost:9200/_cat/allocation?v"

# Supprimer vieux index
curl -X DELETE "localhost:9200/logs-2023-*?pretty"

# Ou utiliser Curator (outil de gestion)
pip install elasticsearch-curator

# curator.yml
curator --config curator.yml actions.yml

# === Problème: Recherches lentes ===

# Vérifier slow logs
tail -f /var/log/elasticsearch/*_search_slowlog.log

# Profiler query
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "profile": true,
  "query": {
    "match": {
      "message": "error"
    }
  }
}
'

# Optimisations:
# - Utiliser filters au lieu de queries (cachés)
# - Réduire number_of_shards
# - Forcemerge index anciens
# - Augmenter refresh_interval

curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "30s"
  }
}
'

# === Problème: Indexation lente ===

# Désactiver refresh temporairement
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "-1"
  }
}
'

# Bulk insert
# Réactiver après
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "1s"
  }
}
'

# Réduire replicas pendant indexation
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# === Problème: Trop de segments ===

# Vérifier
curl -X GET "localhost:9200/_cat/segments?v"

# Forcemerge
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1&pretty"

# === Problème: Circuit breaker ===

# Erreur: "Data too large, circuit breaker"
# Cause: Query trop gourmande en mémoire

# Vérifier breakers
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

# Augmenter limite (temporaire)
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "80%"
  }
}
'

# Meilleures solutions:
# - Optimiser query
# - Augmenter RAM
# - Réduire taille résultats

# === Problème: Version conflict ===

# Erreur: "version_conflict_engine_exception"
# Cause: Document modifié entre lecture et écriture

# Solutions:
# - Utiliser retry_on_conflict
curl -X POST "localhost:9200/users/_update/1?retry_on_conflict=3&pretty" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26
  }
}
'

# - Utiliser version externe
# - Utiliser scripts pour updates

# === Problème: Connexion refusée ===

# Vérifier Elasticsearch écoute
curl -X GET "localhost:9200"

# Vérifier network.host dans elasticsearch.yml
network.host: 0.0.0.0

# Vérifier firewall
sudo ufw status
sudo ufw allow 9200/tcp

# === Problème: Kibana ne se connecte pas à Elasticsearch ===

# Vérifier kibana.yml
elasticsearch.hosts: ["http://localhost:9200"]

# Tester connexion
curl -X GET "http://localhost:9200"

# Vérifier logs Kibana
tail -f /var/log/kibana/kibana.log

# Avec sécurité: vérifier username/password
elasticsearch.username: "kibana_system"
elasticsearch.password: "correct_password"

# === Problème: Logstash ne démarre pas ===

# Tester config
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --config.test_and_exit

# Vérifier logs
tail -f /var/log/logstash/logstash-plain.log

# Mode debug
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --log.level=debug

# === Problème: Filebeat ne envoie pas de données ===

# Test config
filebeat test config
filebeat test output

# Mode debug
filebeat -e -d "*"

# Vérifier registry (position lecture fichiers)
cat /var/lib/filebeat/registry/filebeat/data.json

# Reset registry (relit depuis début)
sudo systemctl stop filebeat
sudo rm /var/lib/filebeat/registry/filebeat/data.json
sudo systemctl start filebeat


[OK] COMMANDES UTILES - ELASTICSEARCH

# === Cat API (Format lisible) ===

# Tous les cat endpoints
curl -X GET "localhost:9200/_cat?pretty"

# Indices
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET "localhost:9200/_cat/indices?v&s=store.size:desc"
curl -X GET "localhost:9200/_cat/indices?v&h=index,docs.count,store.size"

# Shards
curl -X GET "localhost:9200/_cat/shards?v"
curl -X GET "localhost:9200/_cat/shards/logs-*?v"

# Nœuds
curl -X GET "localhost:9200/_cat/nodes?v"
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m"

# Master
curl -X GET "localhost:9200/_cat/master?v"

# Allocation
curl -X GET "localhost:9200/_cat/allocation?v"

# Count
curl -X GET "localhost:9200/_cat/count?v"
curl -X GET "localhost:9200/_cat/count/logs-*?v"

# Health
curl -X GET "localhost:9200/_cat/health?v"

# Segments
curl -X GET "localhost:9200/_cat/segments?v"

# Templates
curl -X GET "localhost:9200/_cat/templates?v"

# Aliases
curl -X GET "localhost:9200/_cat/aliases?v"

# Plugins
curl -X GET "localhost:9200/_cat/plugins?v"

# Tasks
curl -X GET "localhost:9200/_cat/tasks?v"

# === Scripts utiles ===

# Compter documents dans tous les index
for index in $(curl -s 'localhost:9200/_cat/indices?h=index'); do
  count=$(curl -s "localhost:9200/${index}/_count" | jq -r '.count')
  echo "${index}: ${count}"
done

# Supprimer tous les index vieux de +30 jours
curl -s 'localhost:9200/_cat/indices?h=index' | grep 'logs-2023' | xargs -I {} curl -X DELETE "localhost:9200/{}"

# Backup tous les index
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_$(date +%Y%m%d)?wait_for_completion=false&pretty"

# === Requêtes complexes ===

# Aggregation multi-niveaux
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_status": {
      "terms": {
        "field": "response_code",
        "size": 10
      },
      "aggs": {
        "par_heure": {
          "date_histogram": {
            "field": "@timestamp",
            "calendar_interval": "hour"
          },
          "aggs": {
            "temps_reponse_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}
'

# Percentiles
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "response_time_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]
      }
    }
  }
}
'

# Top hits (exemples dans chaque bucket)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_ip": {
      "terms": {
        "field": "client_ip.keyword",
        "size": 10
      },
      "aggs": {
        "exemples": {
          "top_hits": {
            "size": 3,
            "_source": ["@timestamp", "request", "response_code"]
          }
        }
      }
    }
  }
}
'


[OK] BONNES PRATIQUES

# === Naming conventions ===

# Index: lowercase, pattern avec date
# [OK] logs-nginx-2024-01-15
# [OK] metrics-system-2024-01
# [X] Logs_Nginx_20240115

# Aliases: utiliser pour applications
# [OK] logs-current -> logs-2024-01-15
# [OK] logs-errors -> logs-* (avec filtre)

# === Structure des données ===

# Utiliser types appropriés
# - keyword: ID, email, username (exact match)
# - text: Message, description (full-text search)
# - date: Timestamps
# - integer/long: Compteurs, IDs numériques
# - float/double: Valeurs décimales
# - boolean: Flags
# - ip: Adresses IP
# - geo_point: Coordonnées GPS

# Éviter nested/object si possible (plus lent)

# === Sharding ===

# Règle: 1 shard = 10-50 GB max
# Trop de shards = overhead
# Trop peu = distribution inégale

# Petit cluster (< 50 GB data): 1 shard
# Cluster moyen: 3-5 shards
# Grand cluster: calculer selon volume

# Replicas:
# - Production: minimum 1 replica
# - Dev: 0 replica OK
# - HA critique: 2+ replicas

# === Refresh interval ===

# Défaut: 1s (bon pour recherche temps réel)
# Indexation bulk: augmenter à 30s ou -1 (désactiver)
# Logs anciens: 30s ou plus

# === Index lifecycle ===

# Utiliser ILM pour:
# - Rollover automatique
# - Compression (warm phase)
# - Suppression automatique
# - Économiser espace/ressources

# === Monitoring ===

# Surveiller:
# - Heap usage (< 75%)
# - Disk usage (< 85%)
# - Cluster health
# - Search/indexing latency
# - Node count

# Alertes sur:
# - Cluster RED/YELLOW
# - Heap > 80%
# - Disk > 90%
# - Slow queries
# - Failed shards

# === Sécurité ===

# [OK] Activer X-Pack Security
# [OK] Utiliser HTTPS
# [OK] Authentification forte
# [OK] Principe least privilege (rôles)
# [OK] API keys pour applications
# [OK] Firewall (limiter accès 9200/9300)
# [OK] Monitoring accès
# [OK] Backups réguliers

# === Performance ===

# Indexation:
# - Bulk API (batch 5-15 MB)
# - Désactiver refresh si bulk important
# - Réduire replicas temporairement
# - Utiliser pipelines Ingest pour transformations

# Recherche:
# - Utiliser filters (cachés)
# - Limiter size des résultats
# - Utiliser scroll API pour grandes données
# - Index appropriate fields as keyword
# - Utiliser routing pour cibler shards

# Optimisation index:
# - Forcemerge index read-only
# - Désactiver _source si non nécessaire
# - Utiliser _source includes/excludes
# - Doc values pour aggregations

# === Backups ===

# Stratégie 3-2-1:
# - 3 copies
# - 2 médias différents
# - 1 offsite

# Automatiser snapshots:
# - Quotidien pour données critiques
# - Hebdomadaire pour archives
# - Tester restauration régulièrement


[OK] CAS D'USAGE PRATIQUES

# === Use Case 1: Centralisation logs applications ===

# Architecture:
# Applications -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat sur chaque serveur:
filebeat.inputs:
- type: log
  paths:
    - /var/log/app/*.log
  fields:
    app: mon-app
    env: production
  fields_under_root: true

output.logstash:
  hosts: ["logstash:5044"]

# Logstash pipeline:
input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:log_message}" }
  }
  
  if [level] == "ERROR" {
    mutate {
      add_tag => ["alert"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "%{[fields][app]}-logs-%{+YYYY.MM.dd}"
  }
}

# Kibana: Dashboard avec visualizations
# - Logs par niveau (pie chart)
# - Timeline des erreurs (line chart)
# - Top erreurs (data table)
# - Alertes sur erreurs critiques

# === Use Case 2: Monitoring infrastructure ===

# Architecture:
# Serveurs -> Metricbeat -> Elasticsearch -> Kibana

# Metricbeat configuration:
metricbeat.modules:
- module: system
  metricsets:
    - cpu
    - memory
    - network
    - diskio
    - filesystem
  period: 10s

- module: docker
  metricsets:
    - container
    - cpu
    - diskio
    - memory
    - network
  period: 10s

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  index: "metricbeat-%{+yyyy.MM.dd}"

setup.kibana:
  host: "kibana:5601"

# Setup dashboards:
metricbeat setup --dashboards

# Kibana: Dashboards automatiques
# - System Overview
# - CPU usage
# - Memory usage
# - Network traffic
# - Docker containers

# Alertes:
# - CPU > 80% pendant 5 min
# - Memory > 90%
# - Disk > 85%

# === Use Case 3: Analyse e-commerce ===

# Architecture:
# Application -> HTTP input -> Logstash -> Elasticsearch -> Kibana

# Application envoie events JSON:
POST http://logstash:8080
{
  "event_type": "purchase",
  "user_id": "12345",
  "product_id": "ABC123",
  "amount": 49.99,
  "currency": "EUR",
  "timestamp": "2024-01-15T10:30:00Z"
}

# Logstash:
input {
  http {
    port => 8080
    codec => json
  }
}

filter {
  date {
    match => [ "timestamp", "ISO8601" ]
  }
  
  mutate {
    convert => {
      "amount" => "float"
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "ecommerce-events-%{+YYYY.MM}"
  }
}

# Kibana visualizations:
# - Revenus par jour (line chart)
# - Top produits (bar chart)
# - Conversion funnel
# - Heatmap achats par heure
# - Geo map des ventes

# Aggregations utiles:
curl -X GET "localhost:9200/ecommerce-events-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "revenus_quotidiens": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "total_revenus": {
          "sum": {
            "field": "amount"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "amount"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "amount"
          }
        }
      }
    },
    "top_produits": {
      "terms": {
        "field": "product_id.keyword",
        "size": 10
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "amount"
          }
        }
      }
    }
  }
}
'

# === Use Case 4: Security monitoring (SIEM) ===

# Architecture:
# Firewalls/IDS -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat modules:
filebeat.modules:
- module: iptables
- module: suricata
- module: zeek

# Logstash enrichissement:
filter {
  # GeoIP
  geoip {
    source => "source_ip"
    target => "source_geo"
  }
  
  # Threat intelligence
  translate {
    field => "source_ip"
    destination => "threat_level"
    dictionary_path => "/etc/logstash/threat_ips.yml"
    fallback => "unknown"
  }
  
  # Détection patterns suspects
  if [destination_port] in [22, 3389] and [failed_login] {
    mutate {
      add_tag => ["brute_force_attempt"]
    }
  }
}

# Kibana SIEM:
# - Timeline événements
# - Carte attaques géographiques
# - Top IPs suspectes
# - Anomalies détectées

# Alertes:
# - Multiple failed logins
# - Traffic suspect
# - Port scans
# - Malware detected

# === Use Case 5: IoT data collection ===

# Architecture:
# IoT devices -> MQTT -> Logstash -> Elasticsearch -> Kibana

# Logstash MQTT input:
input {
  mqtt {
    host => "mqtt-broker"
    port => 1883
    topic => "sensors/#"
    codec => json
  }
}

filter {
  # Ajouter metadata
  mutate {
    add_field => {
      "device_type" => "sensor"
    }
  }
  
  # Convertir types
  mutate {
    convert => {
      "temperature" => "float"
      "humidity" => "float"
    }
  }
  
  # Alertes sur seuils
  if [temperature] > 30 {
    mutate {
      add_tag => ["high_temperature"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "iot-sensors-%{+YYYY.MM.dd}"
  }
}

# Kibana:
# - Time series température/humidité
# - Heatmap par location
# - Alertes sur anomalies
# - Prédictions ML


[OK] OUTILS COMPLÉMENTAIRES

# === Curator (Gestion automatique index) ===

# Installer
pip install elasticsearch-curator

# Configuration: curator.yml
client:
  hosts:
    - localhost
  port: 9200
  timeout: 30

# Actions: actions.yml
actions:
  1:
    action: delete_indices
    description: Supprimer index > 30 jours
    options:
      ignore_empty_list: True
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 30
  
  2:
    action: forcemerge
    description: Forcemerge index > 2 jours
    options:
      max_num_segments: 1
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 2

# Exécuter
curator --config curator.yml actions.yml

# Cron quotidien
# crontab -e
0 2 * * * /usr/local/bin/curator --config /etc/curator/curator.yml /etc/curator/actions.yml

# === ElastAlert (Alerting avancé) ===

# Installer
pip install elastalert

# Configuration: config.yaml
rules_folder: rules
run_every:
  minutes: 1
buffer_time:
  minutes: 15
es_host: localhost
es_port: 9200
writeback_index: elastalert_status

# Règle: spike_rule.yaml
name: Spike in errors
type: spike
index: logs-*
timeframe:
  minutes: 10
threshold_cur: 5
threshold_ref: 5
spike_height: 2
spike_type: up
filter:
- term:
    level: "ERROR"
alert:
- email
email:
- ops@example.com

# Lancer
elastalert --config config.yaml --rule spike_rule.yaml

# === Elasticsearch SQL ===

# Requêtes SQL sur Elasticsearch (v7+)
curl -X POST "localhost:9200/_sql?format=txt&pretty" -H 'Content-Type: application/json' -d'
{
  "query": "SELECT @timestamp, level, message FROM \"logs-*\" WHERE level = '\''ERROR'\'' LIMIT 10"
}
'

# Avec Kibana Console:
POST _sql?format=txt
{
  "query": "SELECT COUNT(*) FROM \"logs-*\" GROUP BY level"
}

# Translate to Query DSL:
POST _sql/translate
{
  "query": "SELECT * FROM \"logs-*\" WHERE response_code >= 400"
}

# === Elastic APM (Application Performance Monitoring) ===

# Installer APM Server
apt-get install apm-server

# Configuration: apm-server.yml
apm-server:
  host: "0.0.0.0:8200"

output.elasticsearch:
  hosts: ["localhost:9200"]

setup.kibana:
  host: "localhost:5601"

# Instrumenter application (Python exemple)
pip install elastic-apm

# app.py
from elasticapm import Client
from elasticapm.contrib.flask import ElasticAPM

app = Flask(__name__)
app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': 'my-app',
    'SERVER_URL': 'http://localhost:8200',
    'ENVIRONMENT': 'production',
}
apm = ElasticAPM(app)

# Voir traces dans Kibana APM

# === Elasticsearch Watcher (Alerting natif) ===

# Créer watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate?pretty" -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": {
      "interval": "5m"
    }
  },
  "input": {
    "search": {
      "request": {
        "indices": ["logs-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "level": "ERROR"
                  }
                },
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-5m"
                    }
                  }
                }
              ]
            }
          },
          "aggs": {
            "error_count": {
              "value_count": {
                "field": "level"
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.aggregations.error_count.value": {
        "gt": 100
      }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "ops@example.com",
        "subject": "High error rate detected",
        "body": "Detected {{ctx.payload.aggregations.error_count.value}} errors in last 5 minutes"
      }
    }
  }
}
'

# Lister watches
curl -X GET "localhost:9200/_watcher/_query/watches?pretty"

# Activer/Désactiver watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_activate?pretty"
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_deactivate?pretty"

# === Elasticsearch Hadoop ===

# Connecter Elasticsearch avec Hadoop/Spark

# Spark exemple (Scala):
import org.elasticsearch.spark.sql._

val df = spark.read
  .format("es")
  .load("logs-*/doc")

df.filter(df("level") === "ERROR")
  .groupBy("source")
  .count()
  .show()


[OK] RESSOURCES & DOCUMENTATION

# === Documentation officielle ===

# Elasticsearch
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

# Logstash
https://www.elastic.co/guide/en/logstash/current/index.html

# Kibana
https://www.elastic.co/guide/en/kibana/current/index.html

# Beats
https://www.elastic.co/guide/en/beats/libbeat/current/index.html

# === Guides pratiques ===

# Getting Started
https://www.elastic.co/guide/en/elastic-stack-get-started/current/index.html

# Elasticsearch: The Definitive Guide (livre)
https://www.elastic.co/guide/en/elasticsearch/guide/current/index.html

# Blog Elastic
https://www.elastic.co/blog/

# === Forums & Support ===

# Discuss Elastic (forum communauté)
https://discuss.elastic.co/

# GitHub Issues
https://github.com/elastic/elasticsearch/issues

# Stack Overflow
https://stackoverflow.com/questions/tagged/elasticsearch

# === Formations ===

# Elastic Training (officiel)
https://www.elastic.co/training/

# Free fundamentals courses
https://www.elastic.co/training/free

# === Outils en ligne ===

# Grok Debugger (tester patterns)
http://grokdebug.herokuapp.com/

# JSON formatter
https://jsonformatter.org/

# Elasticsearch Head (plugin navigateur)
https://github.com/mobz/elasticsearch-head

# === Communauté ===

# Meetups Elastic
https://www.elastic.co/community/

# ElasticON (conférence annuelle)
https://www.elastic.co/elasticon/

# === Versions & Compatibilité ===

# Support matrix
https://www.elastic.co/support/matrix

# Release notes
https://www.elastic.co/downloads/past-releases

# Breaking changes
https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html


[OK] EXEMPLES DE SCRIPTS MAINTENANCE

# === Backup automatique quotidien (Bash) ===

#!/bin/bash
# backup_elasticsearch.sh

REPOSITORY="my_backup"
SNAPSHOT_NAME="snapshot_$(date +%Y%m%d_%H%M%S)"
ES_HOST="localhost:9200"

# Créer snapshot
curl -X PUT "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}?wait_for_completion=false" \
  -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,metrics-*",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Vérifier statut
sleep 10
STATUS=$(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}" | jq -r '.snapshots[0].state')

if [ "$STATUS" == "SUCCESS" ]; then
  echo "Backup réussi: ${SNAPSHOT_NAME}"
  
  # Supprimer snapshots > 7 jours
  CUTOFF_DATE=$(date -d "7 days ago" +%Y%m%d)
  for snapshot in $(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/_all" | jq -r '.snapshots[].snapshot'); do
    SNAPSHOT_DATE=$(echo $snapshot | grep -oP '\d{8}')
    if [ "$SNAPSHOT_DATE" -lt "$CUTOFF_DATE" ]; then
      echo "Suppression ancien snapshot: $snapshot"
      curl -X DELETE "${ES_HOST}/_snapshot/${REPOSITORY}/${snapshot}"
    fi
  done
else
  echo "Erreur backup: ${STATUS}"
  exit 1
fi

# Cron: 0 2 * * * /usr/local/bin/backup_elasticsearch.sh

# === Monitoring santé cluster (Python) ===

#!/usr/bin/env python3
# monitor_cluster.py

import requests
import json
import sys
from datetime import datetime

ES_HOST = "http://localhost:9200"
WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

def check_cluster_health():
    try:
        response = requests.get(f"{ES_HOST}/_cluster/health")
        health = response.json()
        
        status = health['status']
        cluster_name = health['cluster_name']
        
        if status in ['yellow', 'red']:
            message = {
                "text": f"[ATTENTION] Cluster {cluster_name} status: {status}",
                "attachments": [{
                    "color": "warning" if status == "yellow" else "danger",
                    "fields": [
                        {"title": "Status", "value": status, "short": True},
                        {"title": "Nodes", "value": str(health['number_of_nodes']), "short": True},
                        {"title": "Active Shards", "value": str(health['active_shards']), "short": True},
                        {"title": "Unassigned Shards", "value": str(health['unassigned_shards']), "short": True},
                    ],
                    "footer": f"Checked at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
                }]
            }
            
            # Envoyer alerte Slack
            requests.post(WEBHOOK_URL, json=message)
            return False
        
        return True
        
    except Exception as e:
        print(f"Erreur: {e}")
        sys.exit(1)

def check_disk_usage():
    try:
        response = requests.get(f"{ES_HOST}/_cat/allocation?format=json")
        allocations = response.json()
        
        for alloc in allocations:
            disk_percent = float(alloc['disk.percent'])
            if disk_percent > 85:
                message = {
                    "text": f"[ROUGE] Disk usage high on node {alloc['node']}: {disk_percent}%"
                }
                requests.post(WEBHOOK_URL, json=message)
                
    except Exception as e:
        print(f"Erreur: {e}")

if __name__ == "__main__":
    check_cluster_health()
    check_disk_usage()

# Cron: */5 * * * * /usr/local/bin/monitor_cluster.py

# === Nettoyage index anciens (Python) ===

#!/usr/bin/env python3
# cleanup_old_indices.py

import requests
from datetime import datetime, timedelta

ES_HOST = "http://localhost:9200"
RETENTION_DAYS = 30
INDEX_PATTERN = "logs-"

def get_indices():
    response = requests.get(f"{ES_HOST}/_cat/indices?h=index&format=json")
    return [idx['index'] for idx in response.json()]

def delete_old_indices():
    indices = get_indices()
    cutoff_date = datetime.now() - timedelta(days=RETENTION_DAYS)
    
    for index in indices:
        if not index.startswith(INDEX_PATTERN):
            continue
            
        try:
            # Extraire date du nom (format: logs-YYYY.MM.DD)
            date_str = index.replace(INDEX_PATTERN, "")
            index_date = datetime.strptime(date_str, "%Y.%m.%d")
            
            if index_date < cutoff_date:
                print(f"Suppression index: {index}")
                response = requests.delete(f"{ES_HOST}/{index}")
                if response.status_code == 200:
                    print(f"[OK] {index} supprimé")
                else:
                    print(f"[X] Erreur suppression {index}: {response.text}")
                    
        except ValueError:
            print(f"Format date invalide pour: {index}")
            continue

if __name__ == "__main__":
    delete_old_indices()

# Cron: 0 3 * * * /usr/local/bin/cleanup_old_indices.py


# === FIN DE LA CHEATSHEET ===

# Cette cheatsheet couvre:
# [OK] Installation complète ELK Stack
# [OK] Configuration Elasticsearch, Logstash, Kibana, Filebeat
# [OK] API REST Elasticsearch (CRUD, recherche, agrégations)
# [OK] Pipelines Logstash avec exemples réels
# [OK] Visualisations et dashboards Kibana
# [OK] Gestion de sécurité (X-Pack)
# [OK] Monitoring et performance
# [OK] ILM (Index Lifecycle Management)
# [OK] Dépannage et problèmes courants
# [OK] Cas d'usage pratiques
# [OK] Outils complémentaires
# [OK] Scripts de maintenance
# [OK] Bonnes pratiques

# Pour aller plus loin:
# - Elastic Certified Engineer
# - Architecture clusters multi-nœuds
# - Machine Learning avancé
# - Cross-cluster search
# - Elasticsearch SQL
# - Canvas pour présentations
# - APM (Application Performance Monitoring)]    # Pas les archives

# EXCLUDE_LINES: Ignorer lignes (regex)
  exclude_lines: ['^DEBUG', '^TRACE']   # Pas DEBUG/TRACE

# INCLUDE_LINES: Garder seulement certaines lignes
  include_lines: ['^ERROR', '^FATAL']   # Seulement ERROR/FATAL

# MULTILINE: Regrouper lignes (stack traces Java)
  multiline.pattern: '^[[:space:]]'     # Ligne commence par espace
  multiline.negate: false               # Pattern match = continuer
  multiline.match: after                # Ajouter après ligne précédente

# Exemple multiline Java:
# Log normal:
# 2024-01-15 ERROR Database error
#   at com.example.Main.connect()
#   at com.example.Main.main()
#
# Sans multiline: 3 événements séparés
# Avec multiline: 1 événement avec stack trace complète

  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'  # Commence par date
  multiline.negate: true                             # Si PAS date
  multiline.match: after                             # Ajouter à précédent

# FIELDS: Ajouter champs personnalisés
  fields:
    app: nginx                    # Ajouter champ app=nginx
    environment: production       # Ajouter champ environment=production
    server: web-01
  fields_under_root: true         # Mettre champs à la racine (pas dans fields.xxx)

# TAGS: Étiquettes pour filtrage
  tags: ["nginx", "web", "production"]

# ENCODING: Encodage fichier
  encoding: utf-8                 # Défaut: utf-8 (plain-ascii, utf-16be, etc.)

# SCAN_FREQUENCY: Fréquence vérification nouveaux fichiers
  scan_frequency: 10s             # Défaut: 10s

# HARVESTER_BUFFER_SIZE: Taille buffer lecture
  harvester_buffer_size: 16384    # 16 KB (défaut)

# CLOSE_INACTIVE: Fermer fichier si inactif
  close_inactive: 5m              # Ferme après 5 min sans nouvelles lignes

# CLEAN_INACTIVE: Oublier fichier si inactif longtemps
  clean_inactive: 72h             # Oublie après 72h (garde registry propre)

# 2. TYPE: CONTAINER (Logs Docker)
# Surveille logs containers Docker

- type: container
  enabled: true
  paths:
    - '/var/lib/docker/containers/*/*.log'   # Logs Docker standard
  
  # Parser automatiquement JSON Docker
  json.keys_under_root: true
  json.add_error_key: true

# 3. TYPE: JOURNALD (Logs systemd Linux)
# Lit journald (logs système moderne Linux)

- type: journald
  enabled: true
  id: everything                  # ID unique
  
  # Filtres journald
  include_matches:
    - "systemd.unit=nginx.service"
    - "systemd.unit=mysql.service"

# 4. TYPE: STDIN (Test)
# Lit depuis stdin (pour tests)

- type: stdin
  enabled: true

# Utilisation:
echo "test log" | filebeat -e -c filebeat.yml

# === MODULES (CONFIGURATIONS PRÉ-FAITES) ===

# Modules = Configurations toutes prêtes pour apps connues
# Pas besoin configurer manuellement parsing, dashboards inclus

# LISTER MODULES DISPONIBLES:
filebeat modules list

# Sortie:
# Enabled:
#   nginx
#   mysql
# Disabled:
#   apache
#   redis
#   mongodb
#   ...

# ACTIVER MODULE:
filebeat modules enable nginx
filebeat modules enable mysql
filebeat modules enable system

# DÉSACTIVER MODULE:
filebeat modules disable apache

# CONFIGURER MODULE:
# Fichier: /etc/filebeat/modules.d/nginx.yml

- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log*"]   # Où sont les logs
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log*"]

# Modules disponibles (principaux):
# - nginx: Serveur web
# - apache: Serveur web Apache
# - mysql: Base de données
# - postgresql: Base de données
# - redis: Cache
# - mongodb: Base de données NoSQL
# - elasticsearch: Logs Elasticsearch
# - kibana: Logs Kibana
# - logstash: Logs Logstash
# - system: Logs système (auth, syslog)
# - auditd: Logs audit Linux
# - iptables: Firewall Linux
# - kubernetes: Logs Kubernetes

# EXEMPLE MODULE SYSTEM:

- module: system
  syslog:
    enabled: true
    var.paths: ["/var/log/syslog*"]
  auth:
    enabled: true
    var.paths: ["/var/log/auth.log*"]

# Avantages modules:
# - Parsing automatique (grok patterns inclus)
# - Dashboards Kibana pré-configurés
# - Index templates optimisés
# - Meilleures pratiques appliquées

# === PROCESSORS (TRANSFORMATIONS) ===

# Processors = Transformations légères avant envoi
# Comme Logstash filters mais plus simples

processors:
  # 1. ADD_HOST_METADATA: Infos hôte
  - add_host_metadata:
      when.not.contains.tags: forwarded
  # Ajoute: hostname, OS, architecture, IP
  
  # 2. ADD_CLOUD_METADATA: Infos cloud (AWS, Azure, GCP)
  - add_cloud_metadata: ~
  # Ajoute: provider, instance_id, région, etc.
  
  # 3. ADD_DOCKER_METADATA: Infos containers
  - add_docker_metadata:
      host: "unix:///var/run/docker.sock"
  # Ajoute: container.id, container.name, container.image
  
  # 4. ADD_KUBERNETES_METADATA: Infos Kubernetes
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      - logs_path:
          logs_path: "/var/log/containers/"
  # Ajoute: pod, namespace, labels
  
  # 5. DROP_EVENT: Ignorer événement
  - drop_event:
      when:
        or:
          - equals:
              http.response.status_code: 200
          - contains:
              message: "healthcheck"
  
  # 6. DROP_FIELDS: Supprimer champs
  - drop_fields:
      fields: ["log.file.path", "agent.ephemeral_id"]
      ignore_missing: true
  
  # 7. RENAME: Renommer champ
  - rename:
      fields:
        - from: "source"
          to: "log_source"
      ignore_missing: true
  
  # 8. ADD_TAGS: Ajouter tags
  - add_tags:
      tags: [production, web-tier]
      when:
        equals:
          environment: prod
  
  # 9. ADD_FIELDS: Ajouter champs
  - add_fields:
      target: ''
      fields:
        datacenter: us-east-1
        team: platform
  
  # 10. DECODE_JSON_FIELDS: Parser JSON
  - decode_json_fields:
      fields: ["message"]
      target: ""
      overwrite_keys: true
  
  # 11. DISSECT: Parser format simple (plus rapide que grok)
  - dissect:
      tokenizer: "%{timestamp} %{level} %{message}"
      field: "message"
      target_prefix: ""
  
  # 12. EXTRACT_ARRAY: Extraire depuis array
  - extract_array:
      field: tags
      mappings:
        first_tag: 0
  
  # 13. SCRIPT: JavaScript custom
  - script:
      lang: javascript
      source: >
        function process(event) {
          event.Put("custom_field", event.Get("field1") + event.Get("field2"));
        }

# CONDITIONS (when):
# Appliquer processor seulement si condition

  - add_tags:
      tags: ["error"]
      when:
        or:
          - equals:
              log.level: ERROR
          - equals:
              log.level: FATAL
          - range:
              http.response.status_code:
                gte: 500

# Opérateurs disponibles:
# - equals: Égalité
# - contains: Contient
# - regexp: Expression régulière
# - range: Plage (gte, lte, gt, lt)
# - has_fields: Champs existent
# - or/and/not: Logique booléenne

# === OUTPUT ELASTICSEARCH ===

output.elasticsearch:
  # Hosts (un ou plusieurs)
  hosts: ["localhost:9200"]
  
  # Index
  index: "filebeat-%{[agent.version]}-%{+yyyy.MM.dd}"
  
  # Authentification
  username: "elastic"
  password: "changeme"
  
  # SSL/TLS
  ssl.enabled: true
  ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]
  ssl.certificate: "/etc/pki/client/cert.pem"
  ssl.key: "/etc/pki/client/cert.key"
  
  # Load balancing
  loadbalance: true                # Distribuer entre hosts
  
  # Bulk settings (performance)
  bulk_max_size: 50                # Documents par batch (défaut: 50)
  
  # Workers (parallélisme)
  worker: 1                        # Nombre workers (défaut: 1)
  
  # Compression
  compression_level: 0             # 0-9 (0=pas de compression)
  
  # Pipeline Ingest
  pipeline: "my-pipeline"          # Pipeline Elasticsearch à utiliser
  
  # ILM (Index Lifecycle Management)
  ilm.enabled: true
  ilm.rollover_alias: "filebeat"

# === OUTPUT LOGSTASH ===

output.logstash:
  # Hosts
  hosts: ["localhost:5044"]
  
  # Load balancing
  loadbalance: true
  
  # SSL/TLS
  ssl.enabled: true
  ssl.certificate_authorities: ["/path/to/ca.pem"]
  
  # Compression
  compression_level: 3
  
  # Worker
  worker: 1
  
  # TTL (keep-alive)
  ttl: 30s
  
  # Pipelining (envois parallèles)
  pipelining: 2

# === OUTPUT CONSOLE (Debug) ===

output.console:
  pretty: true                     # Format lisible
  codec.json:                      # Format JSON
    pretty: true

# === OUTPUT FILE (Backup local) ===

output.file:
  path: "/tmp/filebeat"
  filename: filebeat
  rotate_every_kb: 10000           # Rotation tous les 10 MB
  number_of_files: 7               # Garder 7 fichiers

# === SETUP KIBANA ===

# Configuration connexion Kibana (pour dashboards)

setup.kibana:
  host: "localhost:5601"
  username: "elastic"
  password: "changeme"
  ssl.enabled: true

# Charger dashboards automatiquement:
filebeat setup --dashboards

# Cela installe:
# - Dashboards pré-configurés
# - Visualisations
# - Index patterns

# === SETUP TEMPLATES ===

# Configuration templates Elasticsearch

setup.template.settings:
  index.number_of_shards: 1        # Nombre shards
  index.number_of_replicas: 0      # Nombre replicas
  index.codec: best_compression    # Compression

# Charger template:
filebeat setup --template

# === LOGGING ===

# Configuration logs Filebeat lui-même

logging.level: info                # debug, info, warning, error
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7                     # Garder 7 jours
  permissions: 0644

# === EXEMPLE COMPLET: APPLICATION WEB ===

# Serveur web avec Nginx + application custom

filebeat.inputs:

# Logs Nginx access
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    log_type: nginx_access
    tier: frontend
  fields_under_root: true
  tags: ["nginx", "access"]

# Logs Nginx error
- type: log
  enabled: true
  paths:
    - /var/log/nginx/error.log
  fields:
    log_type: nginx_error
    tier: frontend
  fields_under_root: true
  tags: ["nginx", "error"]

# Logs application (JSON)
- type: log
  enabled: true
  paths:
    - /var/log/app/app.log
  json.keys_under_root: true       # JSON dans log
  json.add_error_key: true
  fields:
    log_type: application
    tier: backend
    app: myapp
  fields_under_root: true
  tags: ["application"]

# Logs application (multiline pour stack traces)
- type: log
  enabled: true
  paths:
    - /var/log/app/exceptions.log
  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
  multiline.negate: true
  multiline.match: after
  fields:
    log_type: application_error
    tier: backend
  fields_under_root: true
  tags: ["application", "error"]

# Processors
processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - drop_fields:
      fields: ["agent.ephemeral_id", "ecs.version"]

# Output vers Logstash (pour parsing complexe)
output.logstash:
  hosts: ["logstash:5044"]
  loadbalance: true

# Setup Kibana
setup.kibana:
  host: "kibana:5601"

# Logging
logging.level: info
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7

# === EXEMPLE: DOCKER CONTAINERS ===

filebeat.inputs:
- type: container
  enabled: true
  paths:
    - '/var/lib/docker/containers/*/*.log'
  
  # Parser JSON Docker
  json.keys_under_root: true
  json.add_error_key: true
  json.message_key: log

processors:
  # Enrichir avec metadata Docker
  - add_docker_metadata:
      host: "unix:///var/run/docker.sock"
  
  # Nettoyer champs inutiles
  - drop_fields:
      fields: ["docker.container.labels"]
      ignore_missing: true
  
  # Ajouter infos selon container
  - add_fields:
      target: ''
      fields:
        environment: production
      when:
        contains:
          docker.container.name: "prod-"

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  index: "docker-logs-%{+yyyy.MM.dd}"

# === EXEMPLE: KUBERNETES ===

filebeat.autodiscover:
  providers:
    - type: kubernetes
      node: ${NODE_NAME}
      hints.enabled: true           # Utiliser annotations pods
      hints.default_config:
        type: container
        paths:
          - /var/log/containers/*${data.kubernetes.container.id}.log

processors:
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      - logs_path:
          logs_path: "/var/log/containers/"
  
  # Ignorer namespaces système
  - drop_event:
      when:
        equals:
          kubernetes.namespace: "kube-system"

output.elasticsearch:
  hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
  username: ${ELASTICSEARCH_USERNAME}
  password: ${ELASTICSEARCH_PASSWORD}
  index: "k8s-logs-%{[agent.version]}-%{+yyyy.MM.dd}"

# === TESTER CONFIGURATION ===

# 1. TEST SYNTAXE
filebeat test config

# Sortie si OK:
# Config OK

# 2. TEST OUTPUT ELASTICSEARCH
filebeat test output

# Sortie si connexion OK:
# elasticsearch: http://localhost:9200...
#   parse url... OK
#   connection...
#     parse host... OK
#     dns lookup... OK
#     addresses: 127.0.0.1
#     dial up... OK
#   TLS... WARN secure connection disabled
#   talk to server... OK

# 3. LANCER EN MODE TEST (Foreground)
filebeat -e -c /etc/filebeat/filebeat.yml

# -e: logs vers stderr (console)
# -c: fichier config
# Ctrl+C pour arrêter

# 4. MODE DEBUG
filebeat -e -d "*"

# Affiche tous les logs debug

# === DÉMARRER FILEBEAT ===

# LINUX (systemd):
sudo systemctl start filebeat
sudo systemctl enable filebeat      # Démarrage auto
sudo systemctl status filebeat

# LINUX (service):
sudo service filebeat start
sudo service filebeat status

# DOCKER:
docker run -d \
  --name filebeat \
  --user=root \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro \
  docker.elastic.co/beats/filebeat:8.11.0

# === MONITORING FILEBEAT ===

# VOIR LOGS FILEBEAT:
sudo tail -f /var/log/filebeat/filebeat

# MÉTRIQUES FILEBEAT:
# HTTP endpoint (activer dans config):
http.enabled: true
http.host: "0.0.0.0"
http.port: 5066

# Accéder: http://localhost:5066/stats
# JSON avec:
# - Nombre events envoyés
# - Erreurs
# - Files monitorés
# - État harvesters

# REGISTRY FILEBEAT:
# Fichier mémorisant position lecture
# Linux: /var/lib/filebeat/registry/filebeat/data.json
cat /var/lib/filebeat/registry/filebeat/data.json

# Contient pour chaque fichier:
# - Chemin
# - Offset (position lecture)
# - Timestamp dernière lecture

# === DÉPANNAGE FILEBEAT ===

# PROBLÈME: Filebeat ne démarre pas
# 1. Vérifier config
filebeat test config

# 2. Vérifier logs
sudo journalctl -u filebeat -f

# 3. Vérifier permissions
ls -la /var/log/filebeat
# Doit être accessible par filebeat user

# PROBLÈME: Logs ne sont pas envoyés
# 1. Tester output
filebeat test output

# 2. Vérifier registry (position lecture)
cat /var/lib/filebeat/registry/filebeat/data.json
# Si offset = fin fichier, normal (en attente nouvelles lignes)

# 3. Forcer relecture depuis début (TEST SEULEMENT!)
sudo systemctl stop filebeat
sudo rm /var/lib/filebeat/registry/filebeat/data.json
sudo systemctl start filebeat

# PROBLÈME: Trop de logs envoyés
# Utiliser exclude_lines ou drop_event
exclude_lines: ['^DEBUG']

# PROBLÈME: Performance (CPU élevé)
# 1. Réduire scan_frequency
scan_frequency: 30s

# 2. Réduire harvester_buffer_size
harvester_buffer_size: 8192

# 3. Augmenter close_inactive
close_inactive: 10m

# === BONNES PRATIQUES ===

# 1. UN FILEBEAT PAR SERVEUR
# Léger, peut tourner partout

# 2. AJOUTER TAGS/FIELDS
# Pour identifier source logs
fields:
  environment: production
  datacenter: us-east-1
  server: web-01

# 3. UTILISER MODULES SI POSSIBLE
# Pré-configurés, optimisés, dashboards inclus

# 4. FILTRER LOGS INUTILES
# Debug, health checks, etc.
exclude_lines: ['^DEBUG', 'healthcheck']

# 5. MULTILINE POUR STACK TRACES
# Regroupe lignes liées

# 6. PROCESSORS LÉGERS
# Transformations lourdes dans Logstash

# 7. OUTPUT LOGSTASH EN PRODUCTION
# Logstash = buffer + transformations complexes

# 8. MONITORING
# Activer HTTP endpoint pour métriques

# 9. ROTATION LOGS FILEBEAT
# Éviter remplir disque
logging.files.keepfiles: 7

# 10. BACKUP REGISTRY
# Sauvegarder /var/lib/filebeat/registry/
# Pour éviter relire tout en cas crash


[OK] ELASTICSEARCH - API REST (COMPRENDRE LES BASES)

# === QU'EST-CE QU'UNE API REST? ===

# REST = Representational State Transfer
# C'est comme envoyer des lettres à Elasticsearch:
# - Tu envoies une requête HTTP (GET, POST, PUT, DELETE)
# - Elasticsearch répond avec du JSON
# - Pas besoin d'interface graphique, juste curl ou un outil HTTP

# STRUCTURE D'UNE REQUÊTE:
curl -X <MÉTHODE> "<URL>" -H 'Content-Type: application/json' -d '<DONNÉES JSON>'

# Exemple concret:
curl -X GET "http://localhost:9200/_cluster/health"
#     ^    ^                ^                  ^
#     |    |                |                  |
#  Méthode URL de base    Port              Endpoint (ce qu'on veut)

# MÉTHODES HTTP:
# GET    = Lire/Récupérer (comme "affiche-moi")
# POST   = Créer (comme "ajoute ça")
# PUT    = Créer/Remplacer (comme "mets ça à cet endroit")
# DELETE = Supprimer (comme "efface ça")

# === CONCEPTS ELASTICSEARCH ===

# 1. INDEX (Pluriel: INDICES)
# C'est comme une base de données ou une table
# Exemples: logs-2024-01-15, utilisateurs, produits
# Un index contient des documents similaires

# 2. DOCUMENT
# C'est un enregistrement, une ligne de données
# Format: JSON (comme un dictionnaire Python)
# Exemple de document:
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "ville": "Paris"
}

# 3. MAPPING
# C'est le schéma/structure des données
# Définit les types de champs (text, integer, date, etc.)
# Comme définir les colonnes d'une table SQL

# 4. SHARD
# Fragment d'un index pour distribuer les données
# Comme découper un gros livre en plusieurs tomes
# Plus de shards = meilleure distribution

# 5. REPLICA
# Copie de backup d'un shard
# Pour sécurité et performances (load balancing)

# === SANTÉ DU CLUSTER (Première commande à connaître!) ===

curl -X GET "localhost:9200/_cluster/health?pretty"

# Explication:
# _cluster/health = endpoint pour santé cluster
# ?pretty = affiche JSON formaté (plus lisible)

# Réponse:
{
  "cluster_name" : "elasticsearch",
  "status" : "green",              # <- IMPORTANT!
  "timed_out" : false,
  "number_of_nodes" : 1,           # Nombre de nœuds actifs
  "number_of_data_nodes" : 1,      # Nœuds qui stockent données
  "active_primary_shards" : 5,     # Shards primaires actifs
  "active_shards" : 5,             # Total shards actifs
  "relocating_shards" : 0,         # Shards en déplacement
  "initializing_shards" : 0,       # Shards en initialisation
  "unassigned_shards" : 0          # Shards non assignés (problème si > 0)
}

# STATUS EXPLIQUÉ:
# GREEN  = Tout va bien, toutes données disponibles et répliquées
# YELLOW = Données dispo mais replicas manquants (OK pour dev 1 nœud)
# RED    = Certaines données primaires manquantes (PROBLÈME GRAVE!)

# === GESTION DES INDEX ===

# 1. LISTER TOUS LES INDEX
curl -X GET "localhost:9200/_cat/indices?v"

# Sortie exemple:
# health status index           pri rep docs.count docs.deleted store.size
# yellow open   logs-2024-01-15  1   1       1234            0      1.2mb
# green  open   utilisateurs     1   0        456            0      500kb

# Colonnes expliquées:
# - health: santé (green/yellow/red)
# - status: open (accessible) ou close (fermé)
# - index: nom de l'index
# - pri: nombre de shards primaires
# - rep: nombre de replicas
# - docs.count: nombre de documents
# - store.size: taille sur disque

# 2. CRÉER UN INDEX (Simple)
curl -X PUT "localhost:9200/mon-index"

# Explication:
# PUT = créer ou remplacer
# /mon-index = nom du nouvel index
# Répond: {"acknowledged":true}

# 3. CRÉER UN INDEX (Avec configuration)
curl -X PUT "localhost:9200/mon-index" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1,      # Nombre de fragments
    "number_of_replicas": 1     # Nombre de copies
  }
}
'

# Pourquoi configurer shards/replicas?
# - 1 shard + 0 replica = Dev (rapide, pas de backup)
# - 1 shard + 1 replica = Prod petit (backup)
# - 5 shards + 2 replicas = Prod large (distribué + haute dispo)

# 4. CRÉER INDEX AVEC MAPPING (Structure)
curl -X PUT "localhost:9200/utilisateurs" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "nom": { 
        "type": "text"           # Texte recherchable (full-text)
      },
      "age": { 
        "type": "integer"        # Nombre entier
      },
      "email": { 
        "type": "keyword"        # Texte exact (pas de full-text)
      },
      "date_inscription": { 
        "type": "date"           # Date
      },
      "actif": { 
        "type": "boolean"        # Vrai/Faux
      },
      "localisation": { 
        "type": "geo_point"      # Coordonnées GPS
      }
    }
  }
}
'

# TYPES DE CHAMPS EXPLIQUÉS:

# TEXT vs KEYWORD:
# - text: "Jean Dupont" -> recherche "jean", "dupont", "Jean Dupont" (trouvé!)
#         Analyse le texte, supporte recherche partielle
#         Bon pour: messages, descriptions, articles
#
# - keyword: "Jean Dupont" -> recherche exacte "Jean Dupont" seulement
#           Pas d'analyse, recherche exacte
#           Bon pour: emails, IDs, statuts, tags, URLs

# INTEGER / LONG:
# - Nombres entiers (-2, 0, 42, 1000)
# - integer: -2^31 à 2^31-1
# - long: plus grand range

# FLOAT / DOUBLE:
# - Nombres décimaux (3.14, -0.5, 1000.99)
# - float: précision simple
# - double: précision double (plus précis)

# DATE:
# - Dates et timestamps
# - Format: "2024-01-15", "2024-01-15T10:30:00Z"
# - Stocké en millisecondes depuis 1970 (epoch)

# BOOLEAN:
# - true ou false
# - Pour flags, état actif/inactif

# GEO_POINT:
# - Coordonnées latitude/longitude
# - Pour recherches géographiques
# - Format: {"lat": 48.8566, "lon": 2.3522}

# 5. VOIR MAPPING D'UN INDEX
curl -X GET "localhost:9200/utilisateurs/_mapping?pretty"

# Répond avec la structure complète de l'index

# 6. AJOUTER UN CHAMP AU MAPPING (Update)
curl -X PUT "localhost:9200/utilisateurs/_mapping" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "telephone": { "type": "keyword" }
  }
}
'

# [ATTENTION] IMPORTANT: On peut AJOUTER des champs mais pas MODIFIER les existants!
# Pour modifier: il faut réindexer (copier dans nouvel index)

# 7. SUPPRIMER UN INDEX
curl -X DELETE "localhost:9200/mon-index"

# [ATTENTION] ATTENTION: Supprime TOUTES les données de l'index!
# Pas de corbeille, pas d'undo!

# 8. SUPPRIMER PLUSIEURS INDEX (Pattern)
curl -X DELETE "localhost:9200/logs-2023-*"

# Supprime tous les index commençant par "logs-2023-"
# Exemple: logs-2023-01-01, logs-2023-01-02, etc.

# 9. FERMER UN INDEX (Économiser mémoire)
curl -X POST "localhost:9200/mon-index/_close"

# Quand fermer?
# - Index rarement utilisé mais à garder
# - Libère la mémoire
# - Données toujours sur disque
# - Pas cherchable tant que fermé

# 10. OUVRIR INDEX FERMÉ
curl -X POST "localhost:9200/mon-index/_open"

# 11. VOIR INFO DÉTAILLÉE D'UN INDEX
curl -X GET "localhost:9200/mon-index?pretty"

# Affiche: settings, mappings, aliases

# === GESTION DES DOCUMENTS ===

# 1. AJOUTER UN DOCUMENT (ID automatique)
curl -X POST "localhost:9200/utilisateurs/_doc" -H 'Content-Type: application/json' -d'
{
  "nom": "Jean Dupont",
  "age": 30,
  "email": "jean@example.com",
  "date_inscription": "2024-01-15",
  "actif": true
}
'

# Réponse:
{
  "_index": "utilisateurs",        # Dans quel index
  "_id": "abc123xyz",              # ID généré automatiquement
  "_version": 1,                   # Version (pour détection conflits)
  "result": "created",             # Action effectuée
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

# 2. AJOUTER DOCUMENT (ID spécifique)
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 25,
  "email": "marie@example.com",
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT avec /1 à la fin = ID sera "1"
# Pratique si tu as déjà un ID (ex: ID de ta base SQL)

# 3. RÉCUPÉRER UN DOCUMENT PAR ID
curl -X GET "localhost:9200/utilisateurs/_doc/1?pretty"

# Réponse:
{
  "_index": "utilisateurs",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,                   # <- Document trouvé!
  "_source": {                     # <- Les données!
    "nom": "Marie Martin",
    "age": 25,
    "email": "marie@example.com",
    "date_inscription": "2024-02-01",
    "actif": true
  }
}

# Si document n'existe pas: "found": false

# 4. RÉCUPÉRER SEULEMENT CERTAINS CHAMPS
curl -X GET "localhost:9200/utilisateurs/_doc/1?_source=nom,email&pretty"

# Renvoie seulement nom et email (économise bande passante)

# 5. VÉRIFIER SI DOCUMENT EXISTE (Rapide)
curl -I "localhost:9200/utilisateurs/_doc/1"

# -I = HEAD request (juste les headers, pas le body)
# Répond 200 si existe, 404 si n'existe pas
# Plus rapide que GET car ne récupère pas les données

# 6. METTRE À JOUR DOCUMENT COMPLET
curl -X PUT "localhost:9200/utilisateurs/_doc/1" -H 'Content-Type: application/json' -d'
{
  "nom": "Marie Martin",
  "age": 26,                       # <- Changé de 25 à 26
  "email": "marie.new@example.com", # <- Email mis à jour
  "date_inscription": "2024-02-01",
  "actif": true
}
'

# PUT remplace TOUT le document!
# [ATTENTION] Si tu oublies un champ, il sera supprimé!

# 7. METTRE À JOUR PARTIELLEMENT (Recommandé)
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26                      # <- Change seulement age
  }
}
'

# _update avec "doc" = met à jour seulement les champs spécifiés
# Les autres champs restent intacts
# Plus sûr que PUT complet!

# 8. METTRE À JOUR AVEC SCRIPT
curl -X POST "localhost:9200/utilisateurs/_update/1" -H 'Content-Type: application/json' -d'
{
  "script": {
    "source": "ctx._source.age += params.increment",
    "params": {
      "increment": 1
    }
  }
}
'

# Explication:
# ctx._source = le document actuel
# ctx._source.age += 1 = incrémente age de 1
# Pratique pour compteurs, accumulateurs

# 9. UPSERT (Update ou Insert)
curl -X POST "localhost:9200/utilisateurs/_update/999" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "nom": "Nouveau",
    "age": 30
  },
  "doc_as_upsert": true
}
'

# Comportement:
# - Si document ID=999 existe -> met à jour
# - Si n'existe pas -> crée avec ces données
# Pratique pour éviter erreur "document not found"

# 10. SUPPRIMER UN DOCUMENT
curl -X DELETE "localhost:9200/utilisateurs/_doc/1"

# Supprime le document ID=1
# Pas de confirmation, c'est immédiat!

# 11. SUPPRIMER PAR REQUÊTE (Plusieurs documents)
curl -X POST "localhost:9200/utilisateurs/_delete_by_query" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "actif": false             # Supprime tous les inactifs
    }
  }
}
'

# Pratique pour nettoyage en masse
# Exemple: supprimer tous users inactifs depuis 1 an

# === OPÉRATIONS EN MASSE (BULK API) ===

# Pourquoi Bulk?
# - Insérer 1 document à la fois = lent (1000 documents = 1000 requêtes)
# - Bulk = grouper plusieurs opérations en 1 requête (1000 docs = 1 requête!)
# - Beaucoup plus rapide pour gros volumes

curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "index": { "_index": "utilisateurs", "_id": "1" } }
{ "nom": "User 1", "age": 30 }
{ "index": { "_index": "utilisateurs", "_id": "2" } }
{ "nom": "User 2", "age": 25 }
{ "delete": { "_index": "utilisateurs", "_id": "3" } }
{ "update": { "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# Format Bulk:
# Ligne 1: Action (index, create, update, delete)
# Ligne 2: Données (sauf pour delete)
# Répéter...

# [ATTENTION] IMPORTANT: 
# - Chaque ligne doit être un JSON valide
# - Dernière ligne doit se terminer par \n (retour ligne)
# - Pas de virgule entre les lignes

# Meilleures pratiques Bulk:
# - Batches de 5-15 MB (pas trop gros)
# - 1000-5000 documents par batch
# - Ne pas envoyer tout d'un coup (risque timeout) "_index": "utilisateurs", "_id": "4" } }
{ "doc": { "age": 35 } }
'

# === RECHERCHE DE DOCUMENTS (QUERIES) ===

# COMPRENDRE LA RECHERCHE ELASTICSEARCH

# Elasticsearch = Moteur de recherche comme Google
# 2 types de recherches:
# 1. QUERY (Score de pertinence)
#    - Répond: "À quel point ce document correspond?"
#    - Score: 0.0 à X (plus haut = plus pertinent)
#    - Bon pour: recherche full-text, "trouve articles sur python"
#
# 2. FILTER (Oui/Non)
#    - Répond: "Ce document correspond ou pas?"
#    - Pas de score
#    - Plus rapide (mis en cache)
#    - Bon pour: filtres exacts, "articles de 2024", "statut=publié"

# ENDPOINT DE RECHERCHE
curl -X GET "localhost:9200/<index>/_search"

# Structure de base:
{
  "query": {           # Ce qu'on cherche
    ...
  },
  "size": 10,          # Nombre résultats (défaut: 10)
  "from": 0,           # Pagination (0 = première page)
  "sort": [...],       # Tri des résultats
  "_source": [...]     # Quels champs retourner
}

# === RECHERCHE SIMPLE (MATCH_ALL) ===

# Récupérer TOUS les documents
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}    # Match tout (comme SELECT * en SQL)
  }
}
'

# Réponse:
{
  "took": 5,                    # Temps en millisecondes
  "timed_out": false,           # Timeout dépassé?
  "hits": {
    "total": {
      "value": 1234,            # Nombre total de résultats
      "relation": "eq"          # eq=exact, gte=au moins
    },
    "max_score": 1.0,           # Score max trouvé
    "hits": [                   # Les documents (défaut: 10 premiers)
      {
        "_index": "utilisateurs",
        "_id": "1",
        "_score": 1.0,          # Score de pertinence
        "_source": {            # Le document
          "nom": "Jean",
          "age": 30
        }
      }
    ]
  }
}

# === PAGINATION ===

# Page 1 (premiers 10 résultats)
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 10,          # Nombre par page
  "from": 0            # Offset (commence à 0)
}
'

# Page 2 (résultats 11-20)
{
  "size": 10,
  "from": 10          # Saute les 10 premiers
}

# Page 3 (résultats 21-30)
{
  "size": 10,
  "from": 20          # Saute les 20 premiers
}

# [ATTENTION] LIMITE: from + size ne peut pas dépasser 10,000
# Pour plus: utiliser Scroll API ou Search After

# === RECHERCHE FULL-TEXT (MATCH) ===

# Chercher dans un champ texte
curl -X GET "localhost:9200/utilisateurs/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "nom": "Jean"
    }
  }
}
'

# Comment ça marche?
# 1. "Jean" est analysé (lowercase, etc.)
# 2. Cherche documents contenant "jean"
# 3. Documents avec "Jean Dupont", "jean martin" sont trouvés
# 4. Score calculé selon pertinence

# Recherche plusieurs mots:
{
  "query": {
    "match": {
      "message": "erreur connexion base"
    }
  }
}

# Comportement par défaut (OR):
# Trouve: "erreur" OU "connexion" OU "base"
# Document avec juste "erreur" sera trouvé (score plus bas)

# Forcer AND (tous les mots):
{
  "query": {
    "match": {
      "message": {
        "query": "erreur connexion base",
        "operator": "and"      # Doit contenir TOUS les mots
      }
    }
  }
}

# === RECHERCHE PHRASE EXACTE (MATCH_PHRASE) ===

# Chercher phrase dans l'ordre exact
{
  "query": {
    "match_phrase": {
      "message": "base de données"
    }
  }
}

# Trouve: "erreur base de données" [OK]
# Ne trouve PAS: "base données de test" [X] (ordre différent)

# Avec proximité (slop):
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "base données",
        "slop": 2              # Max 2 mots entre
      }
    }
  }
}

# Trouve: "base de données" [OK] (1 mot entre)
# Trouve: "base des données" [OK] (1 mot entre)
# Trouve: "base et des données" [X] (3 mots entre, dépasse slop)

# === RECHERCHE EXACTE (TERM) ===

# Pour champs keyword (email, ID, statut, etc.)
{
  "query": {
    "term": {
      "email.keyword": "jean@example.com"
    }
  }
}

# [ATTENTION] IMPORTANT:
# term = recherche EXACTE (sensible à la casse)
# "jean@example.com" ≠ "Jean@example.com"
# "jean@example.com" ≠ "jean@EXAMPLE.com"

# Pour chercher parmi plusieurs valeurs (IN en SQL):
{
  "query": {
    "terms": {
      "statut.keyword": ["actif", "en_attente"]
    }
  }
}

# === RECHERCHE PAR PLAGE (RANGE) ===

# Pour nombres, dates
{
  "query": {
    "range": {
      "age": {
        "gte": 25,     # Greater Than or Equal (>=)
        "lte": 35      # Less Than or Equal (<=)
      }
    }
  }
}

# Opérateurs disponibles:
# - gte: >= (supérieur ou égal)
# - gt:  >  (strictement supérieur)
# - lte: <= (inférieur ou égal)
# - lt:  <  (strictement inférieur)

# Exemple dates:
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "2024-01-01",
        "lt": "2024-02-01"
      }
    }
  }
}

# Dates relatives (pratique!):
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-7d",      # Il y a 7 jours
        "lte": "now"          # Maintenant
      }
    }
  }
}

# Unités temps:
# - y: années
# - M: mois
# - w: semaines
# - d: jours
# - h: heures
# - m: minutes
# - s: secondes

# Exemples:
# "now-1h": il y a 1 heure
# "now-30d": il y a 30 jours
# "now+1d": dans 1 jour

# === RECHERCHE BOOLÉENNE (BOOL) ===

# Combiner plusieurs conditions (comme AND, OR, NOT en SQL)
{
  "query": {
    "bool": {
      "must": [         # AND (doit matcher, affecte score)
        ...
      ],
      "filter": [       # AND (doit matcher, pas de score, plus rapide)
        ...
      ],
      "should": [       # OR (au moins 1, boost score)
        ...
      ],
      "must_not": [     # NOT (ne doit PAS matcher)
        ...
      ]
    }
  }
}

# Explication des clauses:

# MUST: Doit matcher + calcule score
# Utilise pour: recherche principale avec pertinence
{
  "must": [
    {"match": {"message": "error"}}
  ]
}

# FILTER: Doit matcher + pas de score (plus rapide, cachable)
# Utilise pour: filtres exacts (date, statut, etc.)
{
  "filter": [
    {"term": {"status": "published"}},
    {"range": {"date": {"gte": "2024-01-01"}}}
  ]
}

# SHOULD: Au moins 1 doit matcher (optionnel)
# Utilise pour: boost pertinence
{
  "should": [
    {"match": {"tags": "python"}},
    {"match": {"tags": "javascript"}}
  ]
}

# MUST_NOT: Ne doit PAS matcher
# Utilise pour: exclusions
{
  "must_not": [
    {"term": {"status": "deleted"}}
  ]
}

# === EXEMPLE COMPLET BOOL ===

# "Trouve logs d'erreur des 7 derniers jours, 
#  niveau ERROR ou FATAL, mais pas de l'application 'test'"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"message": "error"}}     # Contient "error"
      ],
      "filter": [
        {
          "range": {
            "@timestamp": {
              "gte": "now-7d"               # 7 derniers jours
            }
          }
        }
      ],
      "should": [
        {"term": {"level": "ERROR"}},       # Préfère ERROR
        {"term": {"level": "FATAL"}}        # ou FATAL
      ],
      "must_not": [
        {"term": {"app": "test"}}           # Pas de l'app test
      ],
      "minimum_should_match": 1             # Au moins 1 should requis
    }
  }
}

# === RECHERCHE MULTI-CHAMPS (MULTI_MATCH) ===

# Chercher dans plusieurs champs en même temps
{
  "query": {
    "multi_match": {
      "query": "Jean Paris",
      "fields": ["nom", "ville"]    # Cherche dans nom ET ville
    }
  }
}

# Avec boost (donner plus d'importance à un champ):
{
  "query": {
    "multi_match": {
      "query": "python",
      "fields": ["titre^3", "contenu"]  # ^3 = titre 3x plus important
    }
  }
}

# === RECHERCHE WILDCARD (AVEC JOKER) ===

# * = n'importe quels caractères
# ? = 1 caractère exactement

{
  "query": {
    "wildcard": {
      "nom": "Je*"         # Jean, Jerome, Jessica
    }
  }
}

{
  "query": {
    "wildcard": {
      "code": "ABC-???"    # ABC-123, ABC-xyz, etc.
    }
  }
}

# [ATTENTION] ATTENTION: Wildcard est LENT sur gros volumes
# Évite de commencer par * (ex: "*test")

# === RECHERCHE FUZZY (TOLÉRANTE AUX FAUTES) ===

# Trouve documents même avec fautes de frappe
{
  "query": {
    "fuzzy": {
      "nom": {
        "value": "Jeen",           # Faute: "Jeen" au lieu de "Jean"
        "fuzziness": "AUTO"        # Distance d'édition automatique
      }
    }
  }
}

# Fuzziness expliqué:
# - AUTO: ajuste selon longueur mot (recommandé)
# - 0: pas de tolérance (exact)
# - 1: 1 caractère différent
# - 2: 2 caractères différents

# Exemples avec fuzziness=1:
# "Jean" trouve: "Jaan", "Jern", "Jdan"
# Ne trouve pas: "Jorn" (2 différences)

# === RECHERCHE PREFIX (AUTOCOMPLÉTION) ===

# Trouve documents commençant par...
{
  "query": {
    "prefix": {
      "nom": "Jea"         # Trouve: Jean, Jeanne, Jeanette
    }
  }
}

# Bon pour: autocomplétion, suggestion

# === RECHERCHE EXISTS (CHAMP EXISTE) ===

# Trouve documents ayant un champ (non null)
{
  "query": {
    "exists": {
      "field": "email"     # Uniquement docs avec email
    }
  }
}

# Inverse (n'existe PAS):
{
  "query": {
    "bool": {
      "must_not": [
        {"exists": {"field": "email"}}
      ]
    }
  }
}

# === TRI DES RÉSULTATS (SORT) ===

# Tri simple:
{
  "query": {"match_all": {}},
  "sort": [
    {"age": {"order": "desc"}}    # desc=décroissant, asc=croissant
  ]
}

# Tri multiple (comme ORDER BY en SQL):
{
  "sort": [
    {"age": {"order": "desc"}},          # D'abord par age
    {"nom.keyword": {"order": "asc"}}    # Puis par nom
  ]
}

# [ATTENTION] Pour trier sur texte, utilise .keyword
# "nom.keyword" (pas "nom" seul)

# Tri par pertinence (score):
{
  "sort": [
    {"_score": {"order": "desc"}}    # _score = pertinence
  ]
}

# Tri par date (plus récent d'abord):
{
  "sort": [
    {"@timestamp": {"order": "desc"}}
  ]
}

# === SÉLECTION DE CHAMPS (_source) ===

# Retourner tous les champs (défaut):
{
  "query": {"match_all": {}}
  # _source contient tout
}

# Retourner champs spécifiques (économise bande passante):
{
  "query": {"match_all": {}},
  "_source": ["nom", "email"]    # Seulement nom et email
}

# Exclure certains champs:
{
  "_source": {
    "excludes": ["description_longue", "metadata"]
  }
}

# Inclure/Exclure ensemble:
{
  "_source": {
    "includes": ["user.*"],        # Tous champs user.xxx
    "excludes": ["*.password"]     # Sauf passwords
  }
}

# === HIGHLIGHTING (SURLIGNER RÉSULTATS) ===

# Surligne les termes trouvés (comme Google)
{
  "query": {
    "match": {"message": "error"}
  },
  "highlight": {
    "fields": {
      "message": {}
    }
  }
}

# Réponse inclut:
{
  "hits": {
    "hits": [{
      "_source": {"message": "Database error occurred"},
      "highlight": {
        "message": ["Database <em>error</em> occurred"]  # <em> autour du mot
      }
    }]
  }
}

# Personnaliser tags:
{
  "highlight": {
    "pre_tags": ["<strong>"],
    "post_tags": ["</strong>"],
    "fields": {"message": {}}
  }
}

# === SCROLL API (PAGINER GROS VOLUMES) ===

# Pour récupérer TOUS les documents (> 10,000)
# Utilisé pour exports, backups

# Étape 1: Première requête avec scroll
curl -X GET "localhost:9200/utilisateurs/_search?scroll=1m" -H 'Content-Type: application/json' -d'
{
  "query": {"match_all": {}},
  "size": 1000          # 1000 docs par batch
}
'

# Répond avec scroll_id:
{
  "_scroll_id": "abc123xyz...",
  "hits": {
    "hits": [...]      # Premiers 1000 docs
  }
}

# Étape 2: Récupérer batch suivant
curl -X POST "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll": "1m",                    # Keep alive 1 minute
  "scroll_id": "abc123xyz..."        # scroll_id de la réponse précédente
}
'

# Répéter jusqu'à hits vide

# Étape 3: Nettoyer (libérer ressources)
curl -X DELETE "localhost:9200/_search/scroll" -H 'Content-Type: application/json' -d'
{
  "scroll_id": "abc123xyz..."
}
'

# === COMPTER DOCUMENTS (COUNT) ===

# Juste compter (sans récupérer docs):
curl -X GET "localhost:9200/utilisateurs/_count?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {"actif": true}
  }
}
'

# Réponse:
{
  "count": 1234
}

# Plus rapide que _search car ne récupère pas les docs

# === EXEMPLES PRATIQUES DE RECHERCHES ===

# 1. RECHERCHE E-COMMERCE
# "Trouve produits 'ordinateur portable', 
#  prix entre 500 et 1500€, en stock, triés par popularité"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"nom": "ordinateur portable"}}
      ],
      "filter": [
        {"range": {"prix": {"gte": 500, "lte": 1500}}},
        {"term": {"en_stock": true}}
      ]
    }
  },
  "sort": [
    {"ventes": {"order": "desc"}}
  ]
}

# 2. RECHERCHE LOGS
# "Logs d'erreur des dernières 24h, serveur web, pas health checks"
{
  "query": {
    "bool": {
      "must": [
        {"match": {"level": "ERROR"}}
      ],
      "filter": [
        {"range": {"@timestamp": {"gte": "now-24h"}}},
        {"term": {"service": "web"}}
      ],
      "must_not": [
        {"match": {"url": "/health"}}
      ]
    }
  },
  "sort": [{"@timestamp": {"order": "desc"}}]
}

# 3. RECHERCHE UTILISATEURS
# "Utilisateurs actifs de Paris ou Lyon, inscrits en 2024"
{
  "query": {
    "bool": {
      "must": [
        {"term": {"actif": true}},
        {"range": {"date_inscription": {"gte": "2024-01-01"}}}
      ],
      "should": [
        {"match": {"ville": "Paris"}},
        {"match": {"ville": "Lyon"}}
      ],
      "minimum_should_match": 1
    }
  }
}

# 4. RECHERCHE ARTICLES BLOG
# "Articles contenant 'python' ou 'javascript', 
#  publiés, par pertinence puis date"
{
  "query": {
    "bool": {
      "should": [
        {"match": {"titre": {"query": "python", "boost": 2}}},  # Titre = 2x important
        {"match": {"contenu": "python"}},
        {"match": {"titre": {"query": "javascript", "boost": 2}}},
        {"match": {"contenu": "javascript"}}
      ],
      "filter": [
        {"term": {"statut": "publié"}}
      ],
      "minimum_should_match": 1
    }
  },
  "sort": [
    {"_score": {"order": "desc"}},
    {"date_publication": {"order": "desc"}}
  ]
}

# === CONSEILS PERFORMANCE RECHERCHE ===

# 1. UTILISER FILTER AU LIEU DE MUST QUAND POSSIBLE
# [OK] Bon (cachable):
{"bool": {"filter": [{"term": {"status": "active"}}]}}

# [X] Moins bon (calcule score inutilement):
{"bool": {"must": [{"term": {"status": "active"}}]}}

# 2. LIMITER SIZE
# Ne demande que ce dont tu as besoin
{"size": 10}  # Pas {"size": 10000}

# 3. UTILISER _source FILTERING
# Seulement les champs nécessaires
{"_source": ["id", "nom"]}  # Pas tous les champs

# 4. ÉVITER WILDCARD STARTING WITH *
# [X] Lent: {"wildcard": {"nom": "*test"}}
# [OK] OK: {"wildcard": {"nom": "test*"}}

# 5. PRÉFÉRER TERM À MATCH POUR KEYWORDS
# [OK] Rapide: {"term": {"status.keyword": "active"}}
# [X] Lent: {"match": {"status": "active"}}

# 6. UTILISER BOOL QUERY EFFICACEMENT
# Ordre optimal:
{
  "bool": {
    "filter": [...],     # D'abord (plus rapide, cachable)
    "must": [...],       # Puis (scoring nécessaire)
    "should": [...],     # Puis (bonus optionnels)
    "must_not": [...]    # Enfin (exclusions)
  }
}

[OK] AGRÉGATIONS ELASTICSEARCH (POUR DÉBUTANTS)

# === QU'EST-CE QU'UNE AGRÉGATION? ===

# Agrégation = Calculs statistiques sur les données
# Comme GROUP BY + fonctions en SQL

# ANALOGIE:
# Tu as un panier de fruits:
# - COUNT: Combien de fruits? (total)
# - TERMS: Combien de pommes, oranges, bananes? (par type)
# - AVG: Poids moyen des fruits?
# - SUM: Poids total?
# - MAX/MIN: Fruit le plus/moins lourd?

# Elasticsearch fait pareil avec tes documents!

# === TYPES D'AGRÉGATIONS ===

# 1. METRICS (Métriques)
# Calculs simples: count, sum, avg, min, max
# Comme calculer une valeur

# 2. BUCKETS (Groupements)
# Regrouper documents par critère
# Comme GROUP BY en SQL

# 3. PIPELINE
# Agrégations sur résultats d'autres agrégations
# Comme calculs dérivés

# === STRUCTURE DE BASE ===

GET /index/_search
{
  "size": 0,              # Ne renvoie pas documents (juste stats)
  "aggs": {               # Section agrégations
    "nom_agregation": {   # Nom que tu choisis
      "type": {           # Type d'agrégation
        ...               # Configuration
      }
    }
  }
}

# === AGRÉGATIONS METRICS (CALCULS) ===

# 1. COUNT (Compter)
# Déjà disponible sans agrégation:
GET /logs/_count

# Dans agrégation:
{
  "aggs": {
    "total_logs": {
      "value_count": {
        "field": "message"
      }
    }
  }
}

# 2. SUM (Somme)
# Exemple: Total des ventes
{
  "size": 0,
  "aggs": {
    "total_ventes": {
      "sum": {
        "field": "montant"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "total_ventes": {
      "value": 125430.50    # Total
    }
  }
}

# 3. AVG (Moyenne)
# Exemple: Âge moyen des utilisateurs
{
  "size": 0,
  "aggs": {
    "age_moyen": {
      "avg": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "age_moyen": {
      "value": 32.5
    }
  }
}

# 4. MIN/MAX (Minimum/Maximum)
# Exemple: Prix min et max produits
{
  "size": 0,
  "aggs": {
    "prix_minimum": {
      "min": {
        "field": "prix"
      }
    },
    "prix_maximum": {
      "max": {
        "field": "prix"
      }
    }
  }
}

# 5. STATS (Statistiques complètes)
# Tout en un: count, min, max, avg, sum
{
  "size": 0,
  "aggs": {
    "stats_age": {
      "stats": {
        "field": "age"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "stats_age": {
      "count": 1000,        # Nombre valeurs
      "min": 18,            # Minimum
      "max": 65,            # Maximum
      "avg": 35.5,          # Moyenne
      "sum": 35500          # Somme
    }
  }
}

# 6. EXTENDED_STATS (Stats étendues)
# Stats + variance, écart-type, etc.
{
  "size": 0,
  "aggs": {
    "stats_detailles": {
      "extended_stats": {
        "field": "response_time"
      }
    }
  }
}

# Ajoute:
# - variance
# - std_deviation (écart-type)
# - std_deviation_bounds (limites)

# 7. PERCENTILES (Percentiles)
# Exemple: Temps réponse p50, p95, p99
{
  "size": 0,
  "aggs": {
    "temps_reponse_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]    # p50, p95, p99
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "temps_reponse_percentiles": {
      "values": {
        "50.0": 120,      # 50% requêtes < 120ms
        "95.0": 450,      # 95% requêtes < 450ms
        "99.0": 890       # 99% requêtes < 890ms
      }
    }
  }
}

# Pourquoi utile?
# p50 = médiane (milieu)
# p95 = expérience 95% utilisateurs
# p99 = expérience worst case (presque tous)

# 8. CARDINALITY (Valeurs uniques)
# Exemple: Nombre visiteurs uniques
{
  "size": 0,
  "aggs": {
    "visiteurs_uniques": {
      "cardinality": {
        "field": "user_id.keyword"
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "visiteurs_uniques": {
      "value": 15234      # ~15234 users uniques
    }
  }
}

# Note: Approximatif (algorithme HyperLogLog)
# Précis à ~3% près (suffisant pour gros volumes)

# === AGRÉGATIONS BUCKETS (GROUPEMENTS) ===

# 1. TERMS (Grouper par valeur)
# Comme GROUP BY en SQL
# Exemple: Logs par niveau (ERROR, WARN, INFO)

{
  "size": 0,
  "aggs": {
    "par_niveau": {
      "terms": {
        "field": "level.keyword",    # Champ à grouper
        "size": 10                   # Top 10
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_niveau": {
      "buckets": [
        {
          "key": "INFO",             # Valeur
          "doc_count": 8000          # Nombre documents
        },
        {
          "key": "WARN",
          "doc_count": 1500
        },
        {
          "key": "ERROR",
          "doc_count": 500
        }
      ]
    }
  }
}

# Options utiles:
# - size: Nombre de buckets (défaut: 10)
# - order: Tri
#   {"_count": "desc"}     # Par nombre (défaut)
#   {"_key": "asc"}        # Par valeur alphabétique
# - min_doc_count: Minimum docs pour apparaître

# Exemple avec tri:
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"_count": "desc"}    # Plus visitées d'abord
      }
    }
  }
}

# 2. RANGE (Plages de valeurs)
# Exemple: Répartition par tranches d'âge

{
  "size": 0,
  "aggs": {
    "tranches_age": {
      "range": {
        "field": "age",
        "ranges": [
          {"to": 18},                  # < 18
          {"from": 18, "to": 30},      # 18-29
          {"from": 30, "to": 50},      # 30-49
          {"from": 50}                 # 50+
        ]
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_age": {
      "buckets": [
        {
          "key": "*-18.0",
          "to": 18,
          "doc_count": 234
        },
        {
          "key": "18.0-30.0",
          "from": 18,
          "to": 30,
          "doc_count": 1567
        },
        {
          "key": "30.0-50.0",
          "from": 30,
          "to": 50,
          "doc_count": 2890
        },
        {
          "key": "50.0-*",
          "from": 50,
          "doc_count": 1109
        }
      ]
    }
  }
}

# Labels personnalisés:
{
  "ranges": [
    {"key": "Enfants", "to": 18},
    {"key": "Jeunes", "from": 18, "to": 30},
    {"key": "Adultes", "from": 30, "to": 50},
    {"key": "Seniors", "from": 50}
  ]
}

# 3. DATE_HISTOGRAM (Histogramme temporel)
# Grouper par intervalle temps
# Exemple: Logs par jour

{
  "size": 0,
  "aggs": {
    "logs_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"    # Intervalle
      }
    }
  }
}

# Intervalles disponibles:
# - "minute" / "1m"
# - "hour" / "1h"
# - "day" / "1d"
# - "week" / "1w"
# - "month" / "1M"
# - "quarter" / "1q"
# - "year" / "1y"

# Intervalles fixes:
# - "fixed_interval": "30s"    # 30 secondes
# - "fixed_interval": "12h"    # 12 heures

# Réponse:
{
  "aggregations": {
    "logs_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15T00:00:00.000Z",
          "key": 1705276800000,        # Timestamp epoch
          "doc_count": 45678           # Logs ce jour
        },
        {
          "key_as_string": "2024-01-16T00:00:00.000Z",
          "key": 1705363200000,
          "doc_count": 52341
        }
      ]
    }
  }
}

# Options utiles:
# - format: Format date
#   "format": "yyyy-MM-dd"
# - time_zone: Fuseau horaire
#   "time_zone": "Europe/Paris"
# - min_doc_count: 0 pour buckets vides
#   "min_doc_count": 0    # Affiche jours sans logs

# 4. HISTOGRAM (Histogramme numérique)
# Tranches égales sur nombres
# Exemple: Prix par tranches de 100€

{
  "size": 0,
  "aggs": {
    "tranches_prix": {
      "histogram": {
        "field": "prix",
        "interval": 100        # Tranches de 100
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "tranches_prix": {
      "buckets": [
        {"key": 0, "doc_count": 234},      # 0-99€
        {"key": 100, "doc_count": 567},    # 100-199€
        {"key": 200, "doc_count": 890},    # 200-299€
        {"key": 300, "doc_count": 345}     # 300-399€
      ]
    }
  }
}

# 5. FILTER (Filtrer avant agrégation)
# Créer bucket avec filtre
# Exemple: Stats seulement sur erreurs

{
  "size": 0,
  "aggs": {
    "erreurs": {
      "filter": {
        "term": {"level": "ERROR"}
      },
      "aggs": {
        "par_service": {
          "terms": {
            "field": "service.keyword"
          }
        }
      }
    }
  }
}

# 6. FILTERS (Multiples filtres)
# Plusieurs buckets avec filtres différents
# Exemple: Compteurs par niveau

{
  "size": 0,
  "aggs": {
    "messages_par_niveau": {
      "filters": {
        "filters": {
          "errors": {"match": {"level": "ERROR"}},
          "warnings": {"match": {"level": "WARN"}},
          "info": {"match": {"level": "INFO"}}
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "messages_par_niveau": {
      "buckets": {
        "errors": {"doc_count": 500},
        "warnings": {"doc_count": 1500},
        "info": {"doc_count": 8000}
      }
    }
  }
}

# === AGRÉGATIONS IMBRIQUÉES (NESTED) ===

# Combiner agrégations pour analyses multi-niveaux
# Comme GROUP BY avec sous-requêtes

# EXEMPLE 1: Logs par service, puis par niveau
{
  "size": 0,
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword"
      },
      "aggs": {                          # <- Sous-agrégation!
        "par_niveau": {
          "terms": {
            "field": "level.keyword"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "par_service": {
      "buckets": [
        {
          "key": "web",
          "doc_count": 5000,
          "par_niveau": {                # <- Sous-résultats
            "buckets": [
              {"key": "INFO", "doc_count": 4000},
              {"key": "WARN", "doc_count": 800},
              {"key": "ERROR", "doc_count": 200}
            ]
          }
        },
        {
          "key": "api",
          "doc_count": 3000,
          "par_niveau": {
            "buckets": [
              {"key": "INFO", "doc_count": 2700},
              {"key": "WARN", "doc_count": 250},
              {"key": "ERROR", "doc_count": 50}
            ]
          }
        }
      ]
    }
  }
}

# EXEMPLE 2: Ventes par jour + revenus
{
  "size": 0,
  "aggs": {
    "ventes_par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "montant"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "montant"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "montant"
          }
        }
      }
    }
  }
}

# Réponse:
{
  "aggregations": {
    "ventes_par_jour": {
      "buckets": [
        {
          "key_as_string": "2024-01-15",
          "doc_count": 234,
          "revenus": {"value": 12450.50},
          "nombre_ventes": {"value": 234},
          "panier_moyen": {"value": 53.21}
        },
        {
          "key_as_string": "2024-01-16",
          "doc_count": 267,
          "revenus": {"value": 15678.90},
          "nombre_ventes": {"value": 267},
          "panier_moyen": {"value": 58.72}
        }
      ]
    }
  }
}

# EXEMPLE 3: URLs par statut + temps réponse
{
  "size": 0,
  "aggs": {
    "par_statut": {
      "range": {
        "field": "response_code",
        "ranges": [
          {"key": "2xx", "from": 200, "to": 300},
          {"key": "4xx", "from": 400, "to": 500},
          {"key": "5xx", "from": 500, "to": 600}
        ]
      },
      "aggs": {
        "top_urls": {
          "terms": {
            "field": "url.keyword",
            "size": 5
          },
          "aggs": {
            "temps_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}

# === TRIER RÉSULTATS AGRÉGATION ===

# Par count (défaut):
{
  "aggs": {
    "top_urls": {
      "terms": {
        "field": "url.keyword",
        "order": {"_count": "desc"}    # Plus de hits d'abord
      }
    }
  }
}

# Par clé (alphabétique):
{
  "terms": {
    "field": "service.keyword",
    "order": {"_key": "asc"}          # A-Z
  }
}

# Par métrique sous-agrégation:
{
  "aggs": {
    "par_produit": {
      "terms": {
        "field": "produit.keyword",
        "order": {"revenus": "desc"}   # <- Trie par revenus
      },
      "aggs": {
        "revenus": {                   # <- Nom référencé
          "sum": {
            "field": "prix"
          }
        }
      }
    }
  }
}

# === FILTRER BUCKETS ===

# Minimum documents:
{
  "terms": {
    "field": "tag.keyword",
    "min_doc_count": 100      # Seulement tags avec 100+ docs
  }
}

# Inclure/Exclure valeurs:
{
  "terms": {
    "field": "status.keyword",
    "include": ["active", "pending"],    # Seulement ces valeurs
    "exclude": ["deleted", "archived"]   # Exclure ces valeurs
  }
}

# Inclure par regex:
{
  "terms": {
    "field": "url.keyword",
    "include": "/api/.*",       # Seulement URLs commençant par /api/
    "exclude": ".*/test/.*"     # Exclure URLs contenant /test/
  }
}

# === EXEMPLES PRATIQUES COMPLETS ===

# EXEMPLE 1: Dashboard e-commerce
# "Revenus, conversions, panier moyen par jour"

GET /orders/_search
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {"gte": "now-30d"}
    }
  },
  "aggs": {
    "par_jour": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenus": {
          "sum": {"field": "montant"}
        },
        "nombre_commandes": {
          "value_count": {"field": "montant"}
        },
        "panier_moyen": {
          "avg": {"field": "montant"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "user_id"}
        },
        "taux_conversion": {
          "bucket_script": {
            "buckets_path": {
              "commandes": "nombre_commandes",
              "visiteurs": "visiteurs_uniques"
            },
            "script": "params.commandes / params.visiteurs * 100"
          }
        }
      }
    }
  }
}

# EXEMPLE 2: Analyse logs application
# "Erreurs par service, avec top messages"

GET /logs/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {"term": {"level": "ERROR"}},
        {"range": {"@timestamp": {"gte": "now-24h"}}}
      ]
    }
  },
  "aggs": {
    "par_service": {
      "terms": {
        "field": "service.keyword",
        "size": 10
      },
      "aggs": {
        "top_messages": {
          "terms": {
            "field": "message.keyword",
            "size": 5
          }
        },
        "dernier_timestamp": {
          "max": {
            "field": "@timestamp"
          }
        }
      }
    }
  }
}

# EXEMPLE 3: Analyse performance web
# "Temps réponse par endpoint, percentiles"

GET /nginx-logs/_search
{
  "size": 0,
  "aggs": {
    "par_endpoint": {
      "terms": {
        "field": "url.keyword",
        "size": 20,
        "order": {"hits": "desc"}
      },
      "aggs": {
        "hits": {
          "value_count": {"field": "response_time"}
        },
        "temps_moyen": {
          "avg": {"field": "response_time"}
        },
        "percentiles": {
          "percentiles": {
            "field": "response_time",
            "percents": [50, 90, 95, 99]
          }
        },
        "lents": {
          "filter": {
            "range": {"response_time": {"gte": 1000}}
          }
        }
      }
    }
  }
}

# EXEMPLE 4: Analyse géographique
# "Requêtes par pays + revenus"

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "par_pays": {
      "terms": {
        "field": "geoip.country_name.keyword",
        "size": 20
      },
      "aggs": {
        "requetes": {
          "value_count": {"field": "@timestamp"}
        },
        "visiteurs_uniques": {
          "cardinality": {"field": "client_ip"}
        },
        "par_ville": {
          "terms": {
            "field": "geoip.city_name.keyword",
            "size": 5
          }
        }
      }
    }
  }
}

# === CONSEILS PERFORMANCE AGRÉGATIONS ===

# 1. UTILISER size: 0
# Ne pas retourner documents (seulement stats)
{"size": 0}

# 2. LIMITER size des terms
# Top 10-20 suffisant généralement
{"size": 10}

# 3. FILTRER AVANT d'agréger
# Réduire volume données
{
  "query": {"range": {"@timestamp": {"gte": "now-7d"}}},
  "aggs": {...}
}

# 4. ÉVITER terms sur champs high-cardinality
# Exemple: Ne pas faire terms sur:
# - UUIDs (millions de valeurs uniques)
# - Timestamps précis
# - Texte libre
# Préférer: cardinality pour compter uniques

# 5. UTILISER doc_values=false si pas d'agrégations
# Dans mapping, si champ jamais agrégé

# 6. CACHER résultats si possible
# Même query répétée = résultat caché

# 7. PRÉFÉRER filters à queries dans agrégations
# Plus rapide (cachable)

# === Index Templates ===

# Créer template
curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "message": { "type": "text" },
        "level": { "type": "keyword" }
      }
    }
  }
}
'

# Lister templates
curl -X GET "localhost:9200/_index_template?pretty"

# Voir template spécifique
curl -X GET "localhost:9200/_index_template/logs_template?pretty"

# Supprimer template
curl -X DELETE "localhost:9200/_index_template/logs_template?pretty"

# === Aliases ===

# Créer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Créer alias avec filtre
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "add": {
        "index": "logs-2024-01",
        "alias": "logs-errors",
        "filter": {
          "term": { "level": "ERROR" }
        }
      }
    }
  ]
}
'

# Supprimer alias
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    {
      "remove": {
        "index": "logs-2024-01",
        "alias": "logs-current"
      }
    }
  ]
}
'

# Déplacer alias (atomique)
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
  "actions": [
    { "remove": { "index": "logs-2024-01", "alias": "logs-current" } },
    { "add": { "index": "logs-2024-02", "alias": "logs-current" } }
  ]
}
'

# Lister aliases
curl -X GET "localhost:9200/_alias?pretty"
curl -X GET "localhost:9200/logs-*/_alias?pretty"

# === Snapshots (Backups) ===

# Créer repository (filesystem)
curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mount/backups/elasticsearch"
  }
}
'

# Créer snapshot
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,users",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Lister snapshots
curl -X GET "localhost:9200/_snapshot/my_backup/_all?pretty"

# Voir détails snapshot
curl -X GET "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"

# Restaurer snapshot
curl -X POST "localhost:9200/_snapshot/my_backup/snapshot_1/_restore?pretty" -H 'Content-Type: application/json' -d'
{
  "indices": "logs-2024-01",
  "ignore_unavailable": true,
  "include_global_state": false,
  "rename_pattern": "(.+)",
  "rename_replacement": "restored_$1"
}
'

# Supprimer snapshot
curl -X DELETE "localhost:9200/_snapshot/my_backup/snapshot_1?pretty"


[OK] KIBANA - INTERFACE & FONCTIONNALITÉS (GUIDE COMPLET)

# === QU'EST-CE QUE KIBANA? ===

# Kibana = Interface graphique pour Elasticsearch
# C'est comme Google Analytics mais pour TES données

# ANALOGIE:
# Elasticsearch = Bibliothèque géante avec millions de livres
# Kibana = Système de recherche et catalogues pour trouver/visualiser les livres

# URL d'accès: http://localhost:5601

# === PREMIÈRE CONNEXION ===

# 1. Ouvrir navigateur: http://localhost:5601
# 2. Si sécurité activée:
#    Username: elastic
#    Password: (celui noté à l'installation)
# 3. Tu arrives sur la page d'accueil Kibana

# === NAVIGATION KIBANA ===

# Menu latéral gauche (principales sections):

# [GRAPHIQUE] ANALYTICS
#   - Discover: Explorer les données brutes
#   - Dashboard: Tableaux de bord
#   - Canvas: Présentations pixel-perfect
#   - Maps: Cartes géographiques
#   - Machine Learning: Détection anomalies (licence payante)

# [HAUSSE] OBSERVABILITY
#   - Logs: Vue centralisée logs
#   - APM: Application Performance Monitoring
#   - Metrics: Métriques infrastructure
#   - Uptime: Monitoring disponibilité

# [VERROUILLE] SECURITY
#   - SIEM: Security Information Event Management
#   - Endpoint: Sécurité endpoints

# [CONFIG] MANAGEMENT
#   - Stack Management: Configuration
#   - Dev Tools: Console pour requêtes
#   - Stack Monitoring: Monitoring ELK

# === DISCOVER (EXPLORATION DE DONNÉES) ===

# C'EST QUOI?
# Discover = Google Search pour tes données
# Cherche, filtre, explore les documents indexés

# ÉTAPES POUR COMMENCER:

# 1. CRÉER INDEX PATTERN
# Index Pattern = Dis à Kibana quels index explorer
# Exemple: "logs-*" pour tous index commençant par "logs-"

# Comment créer:
# a) Menu hamburger ([TRIGRAM_FOR_HEAVEN]) > Stack Management > Index Patterns
# b) "Create index pattern"
# c) Pattern name: logs-* (ou ton pattern)
# d) Time field: @timestamp (champ date pour tri chronologique)
# e) "Create index pattern"

# 2. ALLER DANS DISCOVER
# Menu hamburger > Analytics > Discover

# 3. SÉLECTIONNER INDEX PATTERN
# En haut à gauche: dropdown pour choisir "logs-*"

# 4. CHOISIR PÉRIODE
# En haut à droite: Time picker
# Options:
# - Last 15 minutes (défaut)
# - Last 1 hour
# - Last 24 hours
# - Last 7 days
# - Custom (choix précis)

# INTERFACE DISCOVER:

# BARRE DE RECHERCHE (KQL)
# Au milieu en haut, pour chercher dans les données

# KQL = Kibana Query Language
# Syntaxe simple pour rechercher

# Exemples KQL:
response_code: 200                    # Champ = valeur exacte
response_code >= 400                  # Comparaison
message: "error"                      # Contient "error"
message: "database error"             # Phrase (plusieurs mots)
level: ERROR and service: web         # AND logique
status: active or status: pending     # OR logique
NOT status: deleted                   # NOT logique
response_code: (200 or 201)           # Groupement
client_ip: "192.168.1.*"             # Wildcard
@timestamp >= "2024-01-01"           # Date

# HISTOGRAMME (Graphique en haut)
# Montre distribution temporelle des logs
# - Pic = beaucoup d'événements à ce moment
# - Creux = peu d'événements
# - Clique sur barre = zoom sur cette période

# LISTE DES CHAMPS (Colonne gauche)
# Tous les champs disponibles dans les documents
# 
# Actions sur champs:
# 1. Survoler champ -> Icônes apparaissent:
#    (+) Ajouter comme colonne
#    ([RECHERCHE]) Filtrer pour cette valeur
#    ([EYE]) Voir top values
#
# 2. Cliquer champ -> Voir statistiques:
#    - Top 5 valeurs
#    - Nombre d'occurrences
#    - Distribution

# TABLE DES DOCUMENTS (Centre)
# Liste des documents trouvés
# - Par défaut: 500 derniers
# - Triés par @timestamp (plus récent d'abord)
# 
# Pour chaque document:
# - Flèche ([BLACK_RIGHT-POINTING_TRIANGLE]) = Expand pour voir tous champs
# - Table view = Vue tabulaire
# - JSON view = Vue JSON brut

# FILTRES (Au-dessus recherche)
# Filtres visuels appliqués

# AJOUTER FILTRE:
# Méthode 1: Cliquer sur valeur dans document
# - Loupe (+) = "Filter for value" (inclure)
# - Loupe (-) = "Filter out value" (exclure)
#
# Méthode 2: Bouton "+ Add filter"
# - Choisir champ
# - Choisir opérateur (is, is not, exists, etc.)
# - Entrer valeur
# - "Save"

# Exemple:
# Filtre: level "is" "ERROR"
# -> Montre seulement logs niveau ERROR

# Combiner filtres:
# Filter 1: level is ERROR
# Filter 2: service is web
# -> Montre logs ERROR du service web

# SAUVEGARDER RECHERCHE:
# 1. Bouton "Save" (en haut à droite)
# 2. Nom: "Erreurs service web"
# 3. "Save"
# 
# Pour réutiliser:
# Bouton "Open" > Choisir recherche sauvegardée

# CAS D'USAGE DISCOVER:

# 1. DEBUGGING
# "Utilisateur X a eu une erreur hier à 14h30"
# - Time picker: Hier 14:00 - 15:00
# - Filtre: user_id is X
# - Filtre: level is ERROR
# -> Trouver l'erreur exacte en quelques secondes

# 2. INVESTIGATION INCIDENT
# "Le site était lent ce matin entre 9h et 10h"
# - Time picker: Aujourd'hui 09:00 - 10:00
# - Histogramme: Pic visible?
# - Ajouter colonne: response_time
# - Trier par response_time desc
# -> Voir quelles requêtes étaient lentes

# 3. ANALYSE PATTERN
# "Combien d'erreurs 404 par jour?"
# - Time picker: Last 7 days
# - Filtre: response_code is 404
# - Histogramme: Voir distribution
# - Cliquer champ "url.keyword" -> Top 5 URLs 404

# === VISUALIZATIONS (GRAPHIQUES) ===

# C'EST QUOI?
# Transformer données en graphiques visuels
# Comme Excel Charts mais pour Elasticsearch

# TYPES DE VISUALIZATIONS:

# 1. LINE CHART (Graphique ligne)
# Pour: Évolution temporelle
# Exemple: Nombre de logs par heure

# 2. AREA CHART (Graphique aire)
# Pour: Évolution avec remplissage
# Exemple: CPU usage dans le temps

# 3. BAR CHART (Graphique barres)
# Pour: Comparaisons
# Exemple: Logs par service

# 4. PIE CHART (Camembert)
# Pour: Proportions
# Exemple: Répartition logs par niveau (80% INFO, 15% WARN, 5% ERROR)

# 5. DATA TABLE (Tableau)
# Pour: Listes et rankings
# Exemple: Top 10 URLs les plus visitées

# 6. METRIC (Métrique unique)
# Pour: Chiffre clé
# Exemple: Nombre total de logs aujourd'hui

# 7. GAUGE (Jauge)
# Pour: Indicateur avec seuils
# Exemple: CPU usage avec zones vert/orange/rouge

# 8. TAG CLOUD (Nuage de mots)
# Pour: Fréquence termes
# Exemple: Mots les plus fréquents dans messages

# 9. HEAT MAP (Carte chaleur)
# Pour: Matrice de valeurs
# Exemple: Activité par heure et jour de semaine

# 10. MAPS (Carte géographique)
# Pour: Données géolocalisées
# Exemple: Requêtes par pays

# CRÉER UNE VISUALIZATION:

# MÉTHODE 1: LENS (Moderne, recommandé)

# 1. Menu > Visualize Library
# 2. "Create visualization"
# 3. "Lens" (outil drag-and-drop)
# 4. Choisir index pattern: logs-*
# 
# Interface Lens:
# - Gauche: Champs disponibles (glisser-déposer)
# - Centre: Aperçu graphique
# - Droite: Configuration

# EXEMPLE: Graphique ligne - Logs par heure

# 1. Type: Line
# 2. Axe X (horizontal):
#    - Glisser "@timestamp" depuis gauche
#    - Automatiquement: Date Histogram
#    - Intervalle: Hourly (par heure)
# 3. Axe Y (vertical):
#    - Par défaut: Count (nombre de documents)
# 4. Aperçu s'affiche!
# 5. Personnaliser:
#    - Titre axe Y: "Nombre de logs"
#    - Titre axe X: "Temps"
#    - Couleur ligne: Bleu
# 6. "Save" > Nom: "Logs par heure"

# EXEMPLE: Camembert - Répartition par niveau

# 1. Type: Pie
# 2. Slice by (découper par):
#    - Glisser "level.keyword"
#    - Automatiquement: Top 10 values
# 3. Size by:
#    - Count (nombre de docs par niveau)
# 4. Voir: 80% INFO, 15% WARN, 5% ERROR
# 5. "Save" > Nom: "Logs par niveau"

# EXEMPLE: Tableau - Top 10 URLs

# 1. Type: Table
# 2. Rows (lignes):
#    - Glisser "url.keyword"
#    - Top 10 values
# 3. Metrics:
#    - Count (nombre de fois visitée)
# 4. Tri: Par Count descending
# 5. "Save" > Nom: "Top 10 URLs"

# EXEMPLE: Métrique - Total logs aujourd'hui

# 1. Type: Metric
# 2. Metric value:
#    - Count
# 3. Time range: Today
# 4. Format: Nombre (ex: 1,234,567)
# 5. "Save" > Nom: "Total logs"

# MÉTHODE 2: VISUALIZATION TYPES (Classique)

# Plus de contrôle mais moins intuitif
# Menu > Visualize Library > Create visualization > Choisir type

# EXEMPLE: Vertical Bar - Logs par service

# 1. Choisir: "Vertical Bar"
# 2. Source: logs-*
# 3. Y-axis (Metrics):
#    - Aggregation: Count
#    - Label: "Nombre de logs"
# 4. X-axis (Buckets):
#    - Aggregation: Terms
#    - Field: service.keyword
#    - Size: 10
#    - Order: Metric - Count descending
#    - Label: "Service"
# 5. "Update" ([BLACK_RIGHT-POINTING_TRIANGLE]) pour voir
# 6. "Save" > Nom: "Logs par service"

# PERSONNALISATION VISUALIZATIONS:

# COULEURS:
# - Single color: Une couleur
# - By value: Couleur selon valeur
# - Custom palette: Palette personnalisée

# LÉGENDES:
# - Position: Right, Left, Top, Bottom
# - Afficher/Masquer

# AXES:
# - Titre
# - Échelle: Linear, Log
# - Min/Max

# TOOLTIPS:
# - Infos au survol
# - Format

# === DASHBOARDS (TABLEAUX DE BORD) ===

# C'EST QUOI?
# Dashboard = Collection de visualizations
# Comme un tableau de bord de voiture: tout en un coup d'œil

# CRÉER DASHBOARD:

# 1. Menu > Dashboard
# 2. "Create dashboard"
# 3. "Add from library" ou "Create visualization"
# 
# AJOUTER VISUALIZATIONS:
# 4. "Add from library"
# 5. Cocher: "Logs par heure", "Logs par niveau", "Top 10 URLs"
# 6. "Add"
# 
# ARRANGER:
# 7. Glisser-déposer pour positionner
# 8. Coins pour redimensionner
# 9. Layout automatique ou manuel
#
# SAUVEGARDER:
# 10. "Save" > Nom: "Dashboard Logs Production"
# 11. Description: "Vue d'ensemble logs prod"
# 12. "Save"

# FONCTIONNALITÉS DASHBOARD:

# 1. FILTRES GLOBAUX
# Appliqués à TOUTES les visualizations
# - Ajouter filtre en haut
# - Ex: level is ERROR
# -> Toutes les viz montrent seulement erreurs

# 2. TIME PICKER GLOBAL
# Change période pour tout le dashboard
# - Last 15 minutes
# - Last 24 hours
# - Custom range

# 3. DRILL-DOWN
# Cliquer sur élément -> Filtre ajouté
# Ex: Cliquer "ERROR" dans camembert
# -> Dashboard filtré sur erreurs seulement

# 4. REFRESH AUTO
# Actualisation automatique
# - Cliquer horloge ([HEURE])
# - Choisir intervalle: 10s, 30s, 1m, 5m
# -> Dashboard se rafraîchit automatiquement

# 5. MODE PLEIN ÉCRAN
# Pour affichage grand écran (TV, monitoring room)
# - Bouton "Full screen"
# - Appuyer ESC pour sortir

# 6. PARTAGE
# - Share -> Permalink (lien permanent)
# - Share -> Embed code (iframe HTML)
# - Share -> PDF/PNG (export image)

# EXEMPLE DASHBOARD E-COMMERCE:

# Visualizations:
# 1. Metric: Visiteurs actuels (rafraîchi 10s)
# 2. Line: Visites par heure (24h)
# 3. Pie: Répartition devices (Desktop/Mobile/Tablet)
# 4. Bar: Top 10 produits vus
# 5. Table: Derniers achats
# 6. Map: Visiteurs par pays
# 7. Gauge: Taux conversion (%)
# 8. Line: Revenus par heure

# Layout:
# +------------------+------------------+
# | Visiteurs: 1,234 | Taux conv: 3.2% |
# +------------------+------------------+
# | Visites (line - 24h)                |
# +-------------------------------------+
# | Devices (pie) | Top produits (bar) |
# +---------------+--------------------+
# | Map mondial   | Derniers achats    |
# +---------------+--------------------+
# | Revenus (line)                      |
# +-------------------------------------+

# === CANVAS (PRÉSENTATIONS) ===

# C'EST QUOI?
# Canvas = PowerPoint mais avec données temps réel
# Design pixel-perfect pour présentations

# QUAND UTILISER?
# - Présentation executive
# - Affichage TV monitoring
# - Rapport visuel marketing
# - Infographie dynamique

# CRÉER WORKPAD:

# 1. Menu > Canvas
# 2. "Create workpad"
# 3. Template ou "Start from scratch"
#
# INTERFACE:
# - Toolbar haut: Éléments à ajouter
# - Canvas centre: Zone de design
# - Sidebar droite: Propriétés élément

# ÉLÉMENTS DISPONIBLES:

# 1. TEXT (Texte)
# - Titres, labels, descriptions
# - Font, size, color personnalisables

# 2. SHAPE (Formes)
# - Rectangle, cercle, ligne
# - Pour structure visuelle

# 3. IMAGE
# - Logo, icônes, illustrations
# - Upload ou URL

# 4. ELEMENT (Données)
# - Metric: Chiffre de Elasticsearch
# - Chart: Graphique
# - Table: Tableau
# - Markdown: Texte formaté

# 5. FILTER
# - Time filter
# - Dropdown filter

# EXEMPLE: Rapport mensuel

# Page 1: Couverture
# - Background: Dégradé bleu
# - Logo entreprise
# - Titre: "Rapport Janvier 2024"
# - Sous-titre: "Analyse trafic web"

# Page 2: KPIs
# - 4 grandes métriques:
#   * Visiteurs uniques
#   * Pages vues
#   * Taux rebond
#   * Temps moyen session
# - Design carte avec icône

# Page 3: Graphiques
# - Évolution visites (line)
# - Top pages (bar horizontal)
# - Sources trafic (pie)

# Page 4: Géographie
# - Carte mondiale visiteurs
# - Top 10 pays (table)

# FONCTIONNALITÉS:

# - Multiple pages (slides)
# - Animations transitions
# - Auto-play (diaporama auto)
# - Export PDF/PNG
# - Partage via lien
# - Mode présentation plein écran

# === MAPS (CARTES GÉOGRAPHIQUES) ===

# C'EST QUOI?
# Visualiser données avec coordonnées géographiques
# Comme Google Maps avec tes données

# PRÉREQUIS:
# Données avec champ geo_point dans Elasticsearch
# Exemple:
# {
#   "client_ip": "8.8.8.8",
#   "geoip": {
#     "location": {
#       "lat": 37.386,
#       "lon": -122.0838
#     },
#     "country": "United States"
#   }
# }

# CRÉER MAP:

# 1. Menu > Maps
# 2. "Create map"
# 3. "Add layer"

# TYPES DE LAYERS:

# 1. DOCUMENTS (Points)
# - Chaque document = 1 point sur carte
# - Exemple: IP clientes
# Configuration:
# - Index: logs-*
# - Geospatial field: geoip.location
# - Tooltip: Afficher IP, country

# 2. CLUSTERS
# - Groupe points proches
# - Exemple: 100 requêtes Paris -> 1 cercle "100"
# - Zoom: Cercle se décompose

# 3. HEAT MAP
# - Carte de chaleur (densité)
# - Rouge = beaucoup, Bleu = peu
# - Exemple: Zones activité forte

# 4. CHOROPLETH
# - Régions colorées
# - Exemple: Pays colorés selon revenus
# - USA rouge (1M$), France orange (500K$), etc.

# EXEMPLE: Attaques réseau

# Layer 1: Choropleth - Pays sources attaques
# - Agrégation: Count par pays
# - Couleur: Rouge = beaucoup, Vert = peu

# Layer 2: Lines - Flux attaques
# - Source: IP attaquant
# - Destination: Serveur
# - Lignes rouges entre pays

# Layer 3: Points - Serveurs
# - Nos serveurs (points verts)

# PERSONNALISATION:

# - Basemap: Streets, Satellite, Dark, Light
# - Zoom initial
# - Centre initial
# - Bounds (limiter zone)
# - Tooltips (infos au survol)
# - Symboles (icônes custom)
# - Couleurs (palettes)

# === ALERTING (ALERTES) ===

# C'EST QUOI?
# Surveillance automatique + notifications
# "Préviens-moi si X arrive"

# CRÉER ALERTE:

# 1. Menu hamburger > Stack Management
# 2. "Rules and Connectors"
# 3. "Create rule"

# TYPES DE RÈGLES:

# 1. INDEX THRESHOLD
# "Si nombre de documents dépasse seuil"
# Exemple: Plus de 100 erreurs en 5 minutes

# Configuration:
# - Name: "Trop d'erreurs"
# - Index: logs-*
# - When: count()
# - Over: all documents
# - For the last: 5 minutes
# - Threshold: Is above 100
# - Group by: service (optionnel)
# - Filter: level: "ERROR"

# 2. ELASTICSEARCH QUERY
# Query DSL personnalisée
# Plus flexible mais plus complexe

# 3. ANOMALY DETECTION (ML)
# Détection automatique anomalies
# Nécessite licence Gold+

# ACTIONS (Que faire quand alerte?):

# 1. EMAIL
# - To: ops@example.com
# - Subject: "ALERTE: {{context.rule.name}}"
# - Body: "{{context.hits}} erreurs détectées"

# 2. SLACK
# - Connector: Webhook Slack
# - Channel: #alerts
# - Message: "[ATTENTION] Alerte: {{context.message}}"

# 3. WEBHOOK (HTTP)
# - URL: https://api.example.com/alert
# - Method: POST
# - Body: JSON avec détails

# 4. PAGERDUTY
# - Intégration PagerDuty
# - Severity: Critical
# - Description: Alerte détails

# 5. INDEX (Écrire dans Elasticsearch)
# - Index: alerts-*
# - Document: Détails alerte

# EXEMPLE COMPLET:

# Règle: "Erreurs 5xx serveur web"
# Type: Index threshold
# Check every: 1 minute
# Conditions:
# - Index: nginx-logs-*
# - When: count()
# - Over: all documents
# - For: last 5 minutes
# - Is above: 50
# - Filter: response_code >= 500 AND service: "web"
# Actions:
# - Email ops
# - Slack #incidents
# - PagerDuty si production

# === DEV TOOLS (CONSOLE) ===

# C'EST QUOI?
# Console pour envoyer requêtes Elasticsearch directement
# Comme terminal SQL mais pour Elasticsearch

# OUVRIR:
# Menu > Dev Tools

# INTERFACE:
# - Gauche: Éditeur requêtes
# - Droite: Résultats

# UTILISATION:

# 1. Taper requête:
GET /_cluster/health

# 2. Curseur sur ligne
# 3. Cliquer [BLACK_RIGHT-POINTING_TRIANGLE] ou Ctrl+Enter
# 4. Résultat s'affiche à droite

# FONCTIONNALITÉS:

# - AUTOCOMPLÉTION: Ctrl+Space
# - FORMATER: Ctrl+I
# - HISTORIQUE: ^v pour naviguer
# - MULTI-REQUÊTES: Séparer par ligne vide

# EXEMPLES:

# Santé cluster
GET /_cluster/health

# Lister index
GET /_cat/indices?v

# Recherche
GET /logs-*/_search
{
  "query": {
    "match": {
      "level": "ERROR"
    }
  }
}

# Créer document
POST /users/_doc
{
  "name": "Jean",
  "age": 30
}

# === STACK MANAGEMENT ===

# CONFIGURATION CENTRALE DE ELK

# INDEX PATTERNS:
# - Créer/gérer patterns
# - Définir champ timestamp
# - Refresh fields

# SAVED OBJECTS:
# - Importer/Exporter dashboards
# - Sauvegardes visualizations
# - Format: JSON (ndjson)

# Exporter dashboard:
# 1. Saved Objects
# 2. Cocher dashboard
# 3. "Export"
# 4. Télécharge .ndjson

# Importer:
# 1. "Import"
# 2. Glisser fichier .ndjson
# 3. Résoudre conflits
# 4. "Import"

# INDEX LIFECYCLE MANAGEMENT (ILM):
# - Politiques gestion cycle vie
# - Hot -> Warm -> Cold -> Delete
# - Automatisation retention

# ADVANCED SETTINGS:
# - Thème sombre: discover:enableDarkTheme
# - Langue UI
# - Format dates
# - Timezone

# === SPACES (ESPACES) ===

# C'EST QUOI?
# Espaces isolés pour organiser par équipe/projet
# Comme dossiers séparés

# EXEMPLE:
# - Space "Marketing": Dashboards trafic web
# - Space "DevOps": Dashboards infrastructure
# - Space "Security": Dashboards sécurité

# CRÉER SPACE:

# 1. Stack Management > Spaces
# 2. "Create space"
# 3. Name: "Marketing"
# 4. Initials: "MK" (avatar)
# 5. Color: Bleu
# 6. Description: "Espace équipe marketing"
# 7. "Create"

# CHANGER SPACE:
# Menu en haut à gauche > Choisir space

# === CONSEILS UTILISATION KIBANA ===

# PERFORMANCE:

# 1. LIMITER time range si beaucoup données
# - Last 15 min plutôt que Last 7 days
#
# 2. UTILISER filtres plutôt que queries larges
# - Filter: service is "web" (rapide)
# - Query: * (lent, tout scanner)
#
# 3. SAUVEGARDER recherches fréquentes
# - Évite retaper
#
# 4. REFRESH AUTO seulement si nécessaire
# - Consomme ressources
#
# 5. DASHBOARDS légers
# - 8-12 viz max par dashboard
# - Séparer si plus

# ORGANISATION:

# 1. NOMMER clairement
# - [OK] "Erreurs Production - Dernières 24h"
# - [X] "Dashboard 1"
#
# 2. DESCRIPTIONS
# - Ajouter description dashboards
# - Expliquer à quoi ça sert
#
# 3. TAGS
# - Tagger dashboards: "production", "monitoring"
# - Facilite recherche
#
# 4. DOSSIERS
# - Organiser dans Saved Objects
#
# 5. CONVENTIONS
# - Préfixe: "PROD -", "DEV -"
# - Cohérence nommage

# SÉCURITÉ:

# 1. RÔLES appropriés
# - Lecture seule pour viewers
# - Édition pour analysts
#
# 2. SPACES pour isolation
# - Équipe A ne voit pas équipe B
#
# 3. DASHBOARDS en read-only
# - Évite modifications accidentelles Elasticsearch

# === Machine Learning (Détection d'anomalies) ===

# Nécessite licence (Gold ou supérieure)
# 1. Aller dans "Machine Learning"
# 2. "Create job"
# 3. Choisir type:
#    - Single metric (une métrique)
#    - Multi metric (plusieurs métriques)
#    - Population (comportement groupe)
# 4. Configurer détecteurs
# 5. Lancer job

# === Alerting (Alertes) ===

# 1. Aller dans "Stack Management" > "Rules and Connectors"
# 2. "Create rule"
# 3. Types:
#    - Index threshold: Seuil sur nombre documents
#    - Elasticsearch query: Query personnalisée
#    - Anomaly detection: Basé sur ML
# 4. Configurer conditions
# 5. Configurer actions (email, Slack, webhook, etc.)

# Exemple: Alerte si erreurs > 100 en 5 minutes
# Rule type: Index threshold
# Index: logs-*
# When: count()
# Over: all documents
# For the last: 5 minutes
# Is above: 100
# Filter: level: "ERROR"

# === Dev Tools (Console) ===

# Console pour exécuter requêtes Elasticsearch
# 1. Aller dans "Dev Tools"
# 2. Taper requêtes:

GET /_cluster/health

GET /logs-*/_search
{
  "query": {
    "match_all": {}
  }
}

POST /logs-2024-01/_doc
{
  "message": "Test log",
  "level": "INFO",
  "@timestamp": "2024-01-15T10:00:00"
}

# Autocomplétion: Ctrl+Space
# Exécuter: Ctrl+Enter
# Formater: Ctrl+I

# === Stack Management ===

# 1. Index Patterns:
#    - Créer pattern pour découvrir données
#    - Ex: logs-*, filebeat-*
#    - Définir champ timestamp

# 2. Index Lifecycle Management (ILM):
#    - Gérer cycle de vie des index
#    - Hot > Warm > Cold > Delete

# 3. Saved Objects:
#    - Importer/Exporter dashboards, visualizations
#    - Format JSON

# 4. Advanced Settings:
#    - Personnaliser Kibana
#    - Thème sombre: discover:enableDarkTheme

# === Spaces (Espaces) ===

# Organiser dashboards par équipe/projet
# 1. Stack Management > Spaces
# 2. Create space
# 3. Assigner visualizations, dashboards
# 4. Changer d'espace: menu en haut à gauche


[OK] LOGSTASH - EXEMPLES COMPLETS

# === Pipeline: Logs Apache/Nginx ===

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    sincedb_path => "/var/lib/logstash/sincedb_nginx"
    tags => ["nginx", "access"]
  }
}

filter {
  if "nginx" in [tags] {
    grok {
      match => { 
        "message" => "%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code:int} %{NUMBER:bytes:int} \"%{DATA:referrer}\" \"%{DATA:user_agent}\"" 
      }
    }
    
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
      target => "@timestamp"
    }
    
    geoip {
      source => "client_ip"
      target => "geoip"
    }
    
    useragent {
      source => "user_agent"
      target => "user_agent_parsed"
    }
    
    mutate {
      remove_field => ["message", "timestamp"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs JSON ===

input {
  tcp {
    port => 5000
    codec => json
  }
}

filter {
  # Les données sont déjà en JSON, parser automatique
  
  if [level] == "ERROR" or [level] == "FATAL" {
    mutate {
      add_tag => ["error"]
    }
  }
  
  # Extraire info de stack trace
  if [stack_trace] {
    mutate {
      add_field => { "has_stack_trace" => true }
    }
  }
}

output {
  if "error" in [tags] {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-errors-%{+YYYY.MM.dd}"
    }
  } else {
    elasticsearch {
      hosts => ["localhost:9200"]
      index => "app-logs-%{+YYYY.MM.dd}"
    }
  }
}

# === Pipeline: Logs Syslog ===

input {
  syslog {
    port => 514
    type => "syslog"
  }
}

filter {
  if [type] == "syslog" {
    grok {
      match => { 
        "message" => "%{SYSLOGBASE} %{GREEDYDATA:syslog_message}" 
      }
    }
    
    date {
      match => [ "timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
      target => "@timestamp"
    }
    
    mutate {
      remove_field => ["message"]
      rename => { "syslog_message" => "message" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "syslog-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs Docker ===

input {
  file {
    path => "/var/lib/docker/containers/*/*.log"
    codec => json
    type => "docker"
  }
}

filter {
  if [type] == "docker" {
    json {
      source => "log"
    }
    
    mutate {
      rename => { "log" => "message" }
    }
    
    # Extraire container ID du path
    grok {
      match => { 
        "path" => "/var/lib/docker/containers/%{DATA:container_id}/%{GREEDYDATA}" 
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Logs application Java ===

input {
  file {
    path => "/var/log/app/*.log"
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601}"
      negate => true
      what => "previous"
    }
  }
}

filter {
  grok {
    match => { 
      "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:log_message}" 
    }
  }
  
  date {
    match => [ "timestamp", "yyyy-MM-dd HH:mm:ss,SSS" ]
    target => "@timestamp"
  }
  
  # Détecter stack traces
  if [log_message] =~ /^(\s+at\s|Caused by:)/ {
    mutate {
      add_tag => ["stacktrace"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "java-app-%{+YYYY.MM.dd}"
  }
}

# === Pipeline: Métriques système (depuis Metricbeat) ===

input {
  beats {
    port => 5044
    type => "metrics"
  }
}

filter {
  if [type] == "metrics" {
    # Calculer pourcentage CPU
    if [system][cpu] {
      ruby {
        code => "
          total = event.get('[system][cpu][total][pct]')
          if total
            event.set('[system][cpu][total][percent]', (total * 100).round(2))
          end
        "
      }
    }
    
    # Ajouter alertes si seuils dépassés
    if [system][cpu][total][pct] and [system][cpu][total][pct] > 0.9 {
      mutate {
        add_tag => ["high_cpu"]
      }
    }
    
    if [system][memory][used][pct] and [system][memory][used][pct] > 0.9 {
      mutate {
        add_tag => ["high_memory"]
      }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "metricbeat-%{+YYYY.MM.dd}"
  }
  
  # Alerte si ressources critiques
  if "high_cpu" in [tags] or "high_memory" in [tags] {
    email {
      to => "ops@example.com"
      subject => "Alert: High resource usage on %{host.name}"
      body => "CPU: %{[system][cpu][total][percent]}%\nMemory: %{[system][memory][used][pct]}%"
    }
  }
}


[OK] FILEBEAT - EXEMPLES COMPLETS

# === Configuration: Logs multiples applications ===

filebeat.inputs:

# Application web
- type: log
  enabled: true
  paths:
    - /var/log/webapp/*.log
  fields:
    app: webapp
    environment: production
  fields_under_root: true
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

# API logs
- type: log
  enabled: true
  paths:
    - /var/log/api/*.log
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    app: api
    environment: production
  fields_under_root: true

# Base de données logs
- type: log
  enabled: true
  paths:
    - /var/log/postgresql/*.log
  exclude_lines: ['^DEBUG']
  fields:
    app: database
    type: postgresql
  fields_under_root: true

processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~

output.logstash:
  hosts: ["localhost:5044"]
  loadbalance: true

# === Configuration: Docker containers ===

filebeat.inputs:
- type: container
  enabled: true
  paths:
    - '/var/lib/docker/containers/*/*.log'
  
  processors:
    - add_docker_metadata:
        host: "unix:///var/run/docker.sock"
    
    - decode_json_fields:
        fields: ["message"]
        target: ""
        overwrite_keys: true
    
    # Enrichir avec labels Docker
    - add_fields:
        target: docker
        fields:
          container.labels: ~

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "docker-%{[agent.version]}-%{+yyyy.MM.dd}"

setup.template.name: "docker"
setup.template.pattern: "docker-*"

# === Configuration: Module Nginx avec personnalisation ===

filebeat.modules:
- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log*"]
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log*"]

processors:
  - drop_event:
      when:
        or:
          - equals:
              http.response.status_code: 200
          - equals:
              http.response.status_code: 301
  
  - if:
      equals:
        http.response.status_code: 404
    then:
      - add_tags:
          tags: [not_found]
  
  - if:
        range:
          http.response.status_code:
            gte: 500
    then:
      - add_tags:
          tags: [server_error]

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "nginx-%{[agent.version]}-%{+yyyy.MM.dd}"

# === Configuration: Monitoring Kubernetes ===

filebeat.autodiscover:
  providers:
    - type: kubernetes
      node: ${NODE_NAME}
      hints.enabled: true
      hints.default_config:
        type: container
        paths:
          - /var/log/containers/*${data.kubernetes.container.id}.log

processors:
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      - logs_path:
          logs_path: "/var/log/containers/"
  
  - drop_event:
      when:
        equals:
          kubernetes.namespace: "kube-system"

output.elasticsearch:
  hosts: ["${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}"]
  username: ${ELASTICSEARCH_USERNAME}
  password: ${ELASTICSEARCH_PASSWORD}
  index: "k8s-logs-%{[agent.version]}-%{+yyyy.MM.dd}"


[OK] PATTERNS GROK PERSONNALISÉS

# Créer fichier: /etc/logstash/patterns/custom_patterns

# === Format ===
PATTERN_NAME regex

# === Exemples ===

# Log application custom
MYAPP_LOG %{TIMESTAMP_ISO8601:timestamp} \| %{LOGLEVEL:level} \| %{DATA:module} \| %{GREEDYDATA:message}

# Log avec user ID
MYAPP_USER_LOG \[%{DATA:user_id}\] %{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA:message}

# Format de transaction
TRANSACTION_ID TXN-%{INT:transaction_id}
TRANSACTION_LOG %{TRANSACTION_ID} - %{WORD:status} - %{NUMBER:amount:float} %{WORD:currency}

# Email pattern
EMAIL_ADDR [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}

# IP avec port
IPPORT %{IP:ip}:%{INT:port}

# === Utiliser dans Logstash ===

filter {
  grok {
    patterns_dir => ["/etc/logstash/patterns"]
    match => { 
      "message" => "%{MYAPP_LOG}" 
    }
  }
}


[OK] INDEX LIFECYCLE MANAGEMENT (ILM)

# ILM = Gérer automatiquement le cycle de vie des index
# Phases: Hot > Warm > Cold > Frozen > Delete

# === Créer politique ILM ===

curl -X PUT "localhost:9200/_ilm/policy/logs_policy?pretty" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d",
            "max_docs": 10000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "searchable_snapshot": {
            "snapshot_repository": "my_backup"
          },
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
'

# === Appliquer politique à index template ===

curl -X PUT "localhost:9200/_index_template/logs_template?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs_policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}
'

# === Créer index initial avec alias ===

curl -X PUT "localhost:9200/logs-000001?pretty" -H 'Content-Type: application/json' -d'
{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}
'

# === Voir statut ILM ===

# Lister politiques
curl -X GET "localhost:9200/_ilm/policy?pretty"

# Voir politique spécifique
curl -X GET "localhost:9200/_ilm/policy/logs_policy?pretty"

# Expliquer état ILM d'un index
curl -X GET "localhost:9200/logs-000001/_ilm/explain?pretty"

# === Gestion ILM ===

# Arrêter ILM
curl -X POST "localhost:9200/_ilm/stop?pretty"

# Démarrer ILM
curl -X POST "localhost:9200/_ilm/start?pretty"

# Statut ILM
curl -X GET "localhost:9200/_ilm/status?pretty"

# Forcer rollover manuel
curl -X POST "localhost:9200/logs/_rollover?pretty"

# Réessayer action échouée
curl -X POST "localhost:9200/logs-000001/_ilm/retry?pretty"

# Supprimer index de ILM
curl -X POST "localhost:9200/logs-000001/_ilm/remove?pretty"


[OK] SÉCURITÉ - CONFIGURATION

# === Activer X-Pack Security ===

# Dans elasticsearch.yml
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

# Générer certificats
cd /usr/share/elasticsearch
bin/elasticsearch-certutil ca
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12

# Copier certificats
cp elastic-certificates.p12 /etc/elasticsearch/
chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-certificates.p12

# Configuration SSL dans elasticsearch.yml
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12

# Redémarrer Elasticsearch
sudo systemctl restart elasticsearch

# === Configurer mots de passe ===

# Mode interactif
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

# Mode automatique (génère mots de passe aléatoires)
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto

# Utilisateurs créés:
# - elastic (superuser)
# - kibana_system (pour Kibana)
# - logstash_system (pour Logstash)
# - beats_system (pour Beats)
# - apm_system (pour APM)
# - remote_monitoring_user

# === Changer mot de passe utilisateur ===

curl -X POST "localhost:9200/_security/user/elastic/_password?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "password" : "nouveau_mot_de_passe"
}
'

# === Créer utilisateur personnalisé ===

curl -X POST "localhost:9200/_security/user/mon_utilisateur?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "password" : "password123",
  "roles" : [ "kibana_admin", "monitoring_user" ],
  "full_name" : "Jean Dupont",
  "email" : "jean@example.com"
}
'

# === Rôles prédéfinis ===

# superuser - Accès complet
# kibana_admin - Admin Kibana
# kibana_user - Utilisateur Kibana
# monitoring_user - Voir monitoring
# ingest_admin - Gérer pipelines
# logstash_admin - Admin Logstash
# beats_admin - Admin Beats
# reporting_user - Générer rapports
# viewer - Lecture seule

# === Créer rôle personnalisé ===

curl -X POST "localhost:9200/_security/role/logs_reader?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "cluster": ["monitor"],
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read", "view_index_metadata"]
    }
  ]
}
'

# === Créer rôle avec Field Level Security ===

curl -X POST "localhost:9200/_security/role/limited_user?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["read"],
      "field_security": {
        "grant": ["@timestamp", "message", "level"],
        "except": ["password", "credit_card"]
      },
      "query": "{\"match\": {\"department\": \"sales\"}}"
    }
  ]
}
'

# === Configuration Kibana avec sécurité ===

# Dans kibana.yml
elasticsearch.username: "kibana_system"
elasticsearch.password: "mot_de_passe"

# SSL
elasticsearch.ssl.verificationMode: certificate
elasticsearch.ssl.certificateAuthorities: [ "/path/to/ca.crt" ]

# Redémarrer Kibana
sudo systemctl restart kibana

# === Configuration Logstash avec sécurité ===

# Dans pipeline
output {
  elasticsearch {
    hosts => ["https://localhost:9200"]
    user => "logstash_system"
    password => "mot_de_passe"
    ssl => true
    cacert => "/path/to/ca.crt"
    index => "logs-%{+YYYY.MM.dd}"
  }
}

# === Configuration Filebeat avec sécurité ===

# Dans filebeat.yml
output.elasticsearch:
  hosts: ["https://localhost:9200"]
  username: "beats_system"
  password: "mot_de_passe"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

setup.kibana:
  host: "https://localhost:5601"
  username: "elastic"
  password: "mot_de_passe"
  ssl.certificate_authorities: ["/path/to/ca.crt"]

# === API Keys (Alternative aux mots de passe) ===

# Créer API key
curl -X POST "localhost:9200/_security/api_key?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "name": "my-api-key",
  "role_descriptors": {
    "logs_writer": {
      "cluster": ["monitor"],
      "index": [
        {
          "names": ["logs-*"],
          "privileges": ["create_index", "write"]
        }
      ]
    }
  }
}
'

# Réponse contient: id et api_key
# Utiliser: base64(id:api_key)

# Utiliser dans Filebeat
output.elasticsearch:
  hosts: ["localhost:9200"]
  api_key: "id:api_key"

# Lister API keys
curl -X GET "localhost:9200/_security/api_key?pretty" -u elastic

# Révoquer API key
curl -X DELETE "localhost:9200/_security/api_key?pretty" -u elastic -H 'Content-Type: application/json' -d'
{
  "id": "key_id"
}
'


[OK] MONITORING & PERFORMANCE

# === Monitoring du cluster ===

# Santé cluster
curl -X GET "localhost:9200/_cluster/health?pretty"

# Stats cluster
curl -X GET "localhost:9200/_cluster/stats?pretty"

# État des nœuds
curl -X GET "localhost:9200/_nodes/stats?pretty"

# Tâches en cours
curl -X GET "localhost:9200/_tasks?pretty"

# Tâches détaillées
curl -X GET "localhost:9200/_tasks?detailed=true&actions=*search&pretty"

# Annuler tâche
curl -X POST "localhost:9200/_tasks/task_id/_cancel?pretty"

# === Hot Threads (Debug performance) ===

curl -X GET "localhost:9200/_nodes/hot_threads?pretty"

# === Monitoring des index ===

# Stats index
curl -X GET "localhost:9200/_stats?pretty"
curl -X GET "localhost:9200/logs-*/_stats?pretty"

# Segments info
curl -X GET "localhost:9200/_cat/segments?v"

# Recovery info
curl -X GET "localhost:9200/_cat/recovery?v"

# Shards allocation
curl -X GET "localhost:9200/_cat/shards?v"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,node&s=index"

# === Cache & Memory ===

# Clear cache
curl -X POST "localhost:9200/_cache/clear?pretty"
curl -X POST "localhost:9200/logs-*/_cache/clear?pretty"

# Field data cache
curl -X POST "localhost:9200/_cache/clear?fielddata=true&pretty"

# Query cache
curl -X POST "localhost:9200/_cache/clear?query=true&pretty"

# Request cache
curl -X POST "localhost:9200/_cache/clear?request=true&pretty"

# === Optimisation ===

# Forcemerge (optimiser segments)
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1&pretty"

# Refresh (rendre documents cherchables)
curl -X POST "localhost:9200/_refresh?pretty"
curl -X POST "localhost:9200/logs-*/_refresh?pretty"

# Flush (écrire sur disque)
curl -X POST "localhost:9200/_flush?pretty"

# === Allocation des shards ===

# Voir allocation
curl -X GET "localhost:9200/_cat/allocation?v"

# Explication allocation
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Réallouer shard manuellement
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "move": {
        "index": "logs-2024-01",
        "shard": 0,
        "from_node": "node1",
        "to_node": "node2"
      }
    }
  ]
}
'

# Réessayer shards échoués
curl -X POST "localhost:9200/_cluster/reroute?retry_failed=true&pretty"

# === Paramètres cluster ===

# Voir settings
curl -X GET "localhost:9200/_cluster/settings?pretty&include_defaults=true"

# Désactiver allocation (maintenance)
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "none"
  }
}
'

# Réactiver allocation
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all"
  }
}
'

# Limiter recovery concurrent
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.node_concurrent_recoveries": 2
  }
}
'

# === Monitoring avec Stack Monitoring ===

# Activer dans Kibana: Stack Monitoring
# Automatiquement collecte métriques Elasticsearch, Logstash, Kibana

# Ou configurer manuellement dans elasticsearch.yml
xpack.monitoring.collection.enabled: true
xpack.monitoring.elasticsearch.collection.enabled: true

# Voir données monitoring
curl -X GET "localhost:9200/.monitoring-es-*/_search?pretty"

# === Métriques JVM ===

curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# Heap usage
curl -X GET "localhost:9200/_nodes/stats?filter_path=nodes.*.jvm.mem.heap_*&pretty"

# GC stats
curl -X GET "localhost:9200/_nodes/stats?filter_path=nodes.*.jvm.gc&pretty"

# === Slow logs ===

# Configuration dans elasticsearch.yml ou dynamique:

# Slow search logs
curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.query.debug": "2s",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.level": "info"
}
'

# Slow index logs
curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.threshold.index.info": "5s",
  "index.indexing.slowlog.level": "info"
}
'

# Logs dans: /var/log/elasticsearch/cluster-name_index_search_slowlog.log


[OK] DÉPANNAGE & PROBLÈMES COURANTS

# === Problème: Elasticsearch ne démarre pas ===

# Vérifier logs
sudo journalctl -u elasticsearch.service -f
tail -f /var/log/elasticsearch/elasticsearch.log

# Vérifier configuration
/usr/share/elasticsearch/bin/elasticsearch -V

# Vérifier ports
sudo netstat -tulpn | grep 9200
sudo lsof -i :9200

# Vérifier permissions
ls -la /var/lib/elasticsearch
ls -la /var/log/elasticsearch

# Réparer permissions
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/log/elasticsearch

# === Problème: Mémoire insuffisante ===

# Erreur: "OutOfMemoryError"
# Solution: Augmenter heap JVM

# Éditer /etc/elasticsearch/jvm.options
-Xms4g
-Xmx4g

# Règle: 50% RAM max, ne pas dépasser 32GB

# Vérifier utilisation mémoire
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# === Problème: Cluster status YELLOW ===

# Cause: Replicas non assignés
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Solution 1: Réduire nombre de replicas
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# Solution 2: Ajouter nœuds au cluster

# === Problème: Cluster status RED ===

# Cause: Shards primaires manquants (GRAVE!)
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason"

# Expliquer pourquoi shard non assigné
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Solution: Restaurer depuis snapshot si possible
# Ou réallouer manuellement (risque perte données)
curl -X POST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "mon-index",
        "shard": 0,
        "node": "node-1",
        "accept_data_loss": true
      }
    }
  ]
}
'

# === Problème: Disque plein ===

# Elasticsearch bloque écriture si disque > 95% plein

# Vérifier espace disque
df -h
curl -X GET "localhost:9200/_cat/allocation?v"

# Supprimer vieux index
curl -X DELETE "localhost:9200/logs-2023-*?pretty"

# Ou utiliser Curator (outil de gestion)
pip install elasticsearch-curator

# curator.yml
curator --config curator.yml actions.yml

# === Problème: Recherches lentes ===

# Vérifier slow logs
tail -f /var/log/elasticsearch/*_search_slowlog.log

# Profiler query
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "profile": true,
  "query": {
    "match": {
      "message": "error"
    }
  }
}
'

# Optimisations:
# - Utiliser filters au lieu de queries (cachés)
# - Réduire number_of_shards
# - Forcemerge index anciens
# - Augmenter refresh_interval

curl -X PUT "localhost:9200/logs-*/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "30s"
  }
}
'

# === Problème: Indexation lente ===

# Désactiver refresh temporairement
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "-1"
  }
}
'

# Bulk insert
# Réactiver après
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "1s"
  }
}
'

# Réduire replicas pendant indexation
curl -X PUT "localhost:9200/mon-index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}
'

# === Problème: Trop de segments ===

# Vérifier
curl -X GET "localhost:9200/_cat/segments?v"

# Forcemerge
curl -X POST "localhost:9200/logs-2024-01/_forcemerge?max_num_segments=1&pretty"

# === Problème: Circuit breaker ===

# Erreur: "Data too large, circuit breaker"
# Cause: Query trop gourmande en mémoire

# Vérifier breakers
curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

# Augmenter limite (temporaire)
curl -X PUT "localhost:9200/_cluster/settings?pretty" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "80%"
  }
}
'

# Meilleures solutions:
# - Optimiser query
# - Augmenter RAM
# - Réduire taille résultats

# === Problème: Version conflict ===

# Erreur: "version_conflict_engine_exception"
# Cause: Document modifié entre lecture et écriture

# Solutions:
# - Utiliser retry_on_conflict
curl -X POST "localhost:9200/users/_update/1?retry_on_conflict=3&pretty" -H 'Content-Type: application/json' -d'
{
  "doc": {
    "age": 26
  }
}
'

# - Utiliser version externe
# - Utiliser scripts pour updates

# === Problème: Connexion refusée ===

# Vérifier Elasticsearch écoute
curl -X GET "localhost:9200"

# Vérifier network.host dans elasticsearch.yml
network.host: 0.0.0.0

# Vérifier firewall
sudo ufw status
sudo ufw allow 9200/tcp

# === Problème: Kibana ne se connecte pas à Elasticsearch ===

# Vérifier kibana.yml
elasticsearch.hosts: ["http://localhost:9200"]

# Tester connexion
curl -X GET "http://localhost:9200"

# Vérifier logs Kibana
tail -f /var/log/kibana/kibana.log

# Avec sécurité: vérifier username/password
elasticsearch.username: "kibana_system"
elasticsearch.password: "correct_password"

# === Problème: Logstash ne démarre pas ===

# Tester config
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --config.test_and_exit

# Vérifier logs
tail -f /var/log/logstash/logstash-plain.log

# Mode debug
/usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf --log.level=debug

# === Problème: Filebeat ne envoie pas de données ===

# Test config
filebeat test config
filebeat test output

# Mode debug
filebeat -e -d "*"

# Vérifier registry (position lecture fichiers)
cat /var/lib/filebeat/registry/filebeat/data.json

# Reset registry (relit depuis début)
sudo systemctl stop filebeat
sudo rm /var/lib/filebeat/registry/filebeat/data.json
sudo systemctl start filebeat


[OK] COMMANDES UTILES - ELASTICSEARCH

# === Cat API (Format lisible) ===

# Tous les cat endpoints
curl -X GET "localhost:9200/_cat?pretty"

# Indices
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET "localhost:9200/_cat/indices?v&s=store.size:desc"
curl -X GET "localhost:9200/_cat/indices?v&h=index,docs.count,store.size"

# Shards
curl -X GET "localhost:9200/_cat/shards?v"
curl -X GET "localhost:9200/_cat/shards/logs-*?v"

# Nœuds
curl -X GET "localhost:9200/_cat/nodes?v"
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m"

# Master
curl -X GET "localhost:9200/_cat/master?v"

# Allocation
curl -X GET "localhost:9200/_cat/allocation?v"

# Count
curl -X GET "localhost:9200/_cat/count?v"
curl -X GET "localhost:9200/_cat/count/logs-*?v"

# Health
curl -X GET "localhost:9200/_cat/health?v"

# Segments
curl -X GET "localhost:9200/_cat/segments?v"

# Templates
curl -X GET "localhost:9200/_cat/templates?v"

# Aliases
curl -X GET "localhost:9200/_cat/aliases?v"

# Plugins
curl -X GET "localhost:9200/_cat/plugins?v"

# Tasks
curl -X GET "localhost:9200/_cat/tasks?v"

# === Scripts utiles ===

# Compter documents dans tous les index
for index in $(curl -s 'localhost:9200/_cat/indices?h=index'); do
  count=$(curl -s "localhost:9200/${index}/_count" | jq -r '.count')
  echo "${index}: ${count}"
done

# Supprimer tous les index vieux de +30 jours
curl -s 'localhost:9200/_cat/indices?h=index' | grep 'logs-2023' | xargs -I {} curl -X DELETE "localhost:9200/{}"

# Backup tous les index
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_$(date +%Y%m%d)?wait_for_completion=false&pretty"

# === Requêtes complexes ===

# Aggregation multi-niveaux
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_status": {
      "terms": {
        "field": "response_code",
        "size": 10
      },
      "aggs": {
        "par_heure": {
          "date_histogram": {
            "field": "@timestamp",
            "calendar_interval": "hour"
          },
          "aggs": {
            "temps_reponse_moyen": {
              "avg": {
                "field": "response_time"
              }
            }
          }
        }
      }
    }
  }
}
'

# Percentiles
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "response_time_percentiles": {
      "percentiles": {
        "field": "response_time",
        "percents": [50, 95, 99]
      }
    }
  }
}
'

# Top hits (exemples dans chaque bucket)
curl -X GET "localhost:9200/logs-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "par_ip": {
      "terms": {
        "field": "client_ip.keyword",
        "size": 10
      },
      "aggs": {
        "exemples": {
          "top_hits": {
            "size": 3,
            "_source": ["@timestamp", "request", "response_code"]
          }
        }
      }
    }
  }
}
'


[OK] BONNES PRATIQUES

# === Naming conventions ===

# Index: lowercase, pattern avec date
# [OK] logs-nginx-2024-01-15
# [OK] metrics-system-2024-01
# [X] Logs_Nginx_20240115

# Aliases: utiliser pour applications
# [OK] logs-current -> logs-2024-01-15
# [OK] logs-errors -> logs-* (avec filtre)

# === Structure des données ===

# Utiliser types appropriés
# - keyword: ID, email, username (exact match)
# - text: Message, description (full-text search)
# - date: Timestamps
# - integer/long: Compteurs, IDs numériques
# - float/double: Valeurs décimales
# - boolean: Flags
# - ip: Adresses IP
# - geo_point: Coordonnées GPS

# Éviter nested/object si possible (plus lent)

# === Sharding ===

# Règle: 1 shard = 10-50 GB max
# Trop de shards = overhead
# Trop peu = distribution inégale

# Petit cluster (< 50 GB data): 1 shard
# Cluster moyen: 3-5 shards
# Grand cluster: calculer selon volume

# Replicas:
# - Production: minimum 1 replica
# - Dev: 0 replica OK
# - HA critique: 2+ replicas

# === Refresh interval ===

# Défaut: 1s (bon pour recherche temps réel)
# Indexation bulk: augmenter à 30s ou -1 (désactiver)
# Logs anciens: 30s ou plus

# === Index lifecycle ===

# Utiliser ILM pour:
# - Rollover automatique
# - Compression (warm phase)
# - Suppression automatique
# - Économiser espace/ressources

# === Monitoring ===

# Surveiller:
# - Heap usage (< 75%)
# - Disk usage (< 85%)
# - Cluster health
# - Search/indexing latency
# - Node count

# Alertes sur:
# - Cluster RED/YELLOW
# - Heap > 80%
# - Disk > 90%
# - Slow queries
# - Failed shards

# === Sécurité ===

# [OK] Activer X-Pack Security
# [OK] Utiliser HTTPS
# [OK] Authentification forte
# [OK] Principe least privilege (rôles)
# [OK] API keys pour applications
# [OK] Firewall (limiter accès 9200/9300)
# [OK] Monitoring accès
# [OK] Backups réguliers

# === Performance ===

# Indexation:
# - Bulk API (batch 5-15 MB)
# - Désactiver refresh si bulk important
# - Réduire replicas temporairement
# - Utiliser pipelines Ingest pour transformations

# Recherche:
# - Utiliser filters (cachés)
# - Limiter size des résultats
# - Utiliser scroll API pour grandes données
# - Index appropriate fields as keyword
# - Utiliser routing pour cibler shards

# Optimisation index:
# - Forcemerge index read-only
# - Désactiver _source si non nécessaire
# - Utiliser _source includes/excludes
# - Doc values pour aggregations

# === Backups ===

# Stratégie 3-2-1:
# - 3 copies
# - 2 médias différents
# - 1 offsite

# Automatiser snapshots:
# - Quotidien pour données critiques
# - Hebdomadaire pour archives
# - Tester restauration régulièrement


[OK] CAS D'USAGE PRATIQUES

# === Use Case 1: Centralisation logs applications ===

# Architecture:
# Applications -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat sur chaque serveur:
filebeat.inputs:
- type: log
  paths:
    - /var/log/app/*.log
  fields:
    app: mon-app
    env: production
  fields_under_root: true

output.logstash:
  hosts: ["logstash:5044"]

# Logstash pipeline:
input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:level}\] %{GREEDYDATA:log_message}" }
  }
  
  if [level] == "ERROR" {
    mutate {
      add_tag => ["alert"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "%{[fields][app]}-logs-%{+YYYY.MM.dd}"
  }
}

# Kibana: Dashboard avec visualizations
# - Logs par niveau (pie chart)
# - Timeline des erreurs (line chart)
# - Top erreurs (data table)
# - Alertes sur erreurs critiques

# === Use Case 2: Monitoring infrastructure ===

# Architecture:
# Serveurs -> Metricbeat -> Elasticsearch -> Kibana

# Metricbeat configuration:
metricbeat.modules:
- module: system
  metricsets:
    - cpu
    - memory
    - network
    - diskio
    - filesystem
  period: 10s

- module: docker
  metricsets:
    - container
    - cpu
    - diskio
    - memory
    - network
  period: 10s

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  index: "metricbeat-%{+yyyy.MM.dd}"

setup.kibana:
  host: "kibana:5601"

# Setup dashboards:
metricbeat setup --dashboards

# Kibana: Dashboards automatiques
# - System Overview
# - CPU usage
# - Memory usage
# - Network traffic
# - Docker containers

# Alertes:
# - CPU > 80% pendant 5 min
# - Memory > 90%
# - Disk > 85%

# === Use Case 3: Analyse e-commerce ===

# Architecture:
# Application -> HTTP input -> Logstash -> Elasticsearch -> Kibana

# Application envoie events JSON:
POST http://logstash:8080
{
  "event_type": "purchase",
  "user_id": "12345",
  "product_id": "ABC123",
  "amount": 49.99,
  "currency": "EUR",
  "timestamp": "2024-01-15T10:30:00Z"
}

# Logstash:
input {
  http {
    port => 8080
    codec => json
  }
}

filter {
  date {
    match => [ "timestamp", "ISO8601" ]
  }
  
  mutate {
    convert => {
      "amount" => "float"
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "ecommerce-events-%{+YYYY.MM}"
  }
}

# Kibana visualizations:
# - Revenus par jour (line chart)
# - Top produits (bar chart)
# - Conversion funnel
# - Heatmap achats par heure
# - Geo map des ventes

# Aggregations utiles:
curl -X GET "localhost:9200/ecommerce-events-*/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "revenus_quotidiens": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "total_revenus": {
          "sum": {
            "field": "amount"
          }
        },
        "nombre_ventes": {
          "value_count": {
            "field": "amount"
          }
        },
        "panier_moyen": {
          "avg": {
            "field": "amount"
          }
        }
      }
    },
    "top_produits": {
      "terms": {
        "field": "product_id.keyword",
        "size": 10
      },
      "aggs": {
        "revenus": {
          "sum": {
            "field": "amount"
          }
        }
      }
    }
  }
}
'

# === Use Case 4: Security monitoring (SIEM) ===

# Architecture:
# Firewalls/IDS -> Filebeat -> Logstash -> Elasticsearch -> Kibana

# Filebeat modules:
filebeat.modules:
- module: iptables
- module: suricata
- module: zeek

# Logstash enrichissement:
filter {
  # GeoIP
  geoip {
    source => "source_ip"
    target => "source_geo"
  }
  
  # Threat intelligence
  translate {
    field => "source_ip"
    destination => "threat_level"
    dictionary_path => "/etc/logstash/threat_ips.yml"
    fallback => "unknown"
  }
  
  # Détection patterns suspects
  if [destination_port] in [22, 3389] and [failed_login] {
    mutate {
      add_tag => ["brute_force_attempt"]
    }
  }
}

# Kibana SIEM:
# - Timeline événements
# - Carte attaques géographiques
# - Top IPs suspectes
# - Anomalies détectées

# Alertes:
# - Multiple failed logins
# - Traffic suspect
# - Port scans
# - Malware detected

# === Use Case 5: IoT data collection ===

# Architecture:
# IoT devices -> MQTT -> Logstash -> Elasticsearch -> Kibana

# Logstash MQTT input:
input {
  mqtt {
    host => "mqtt-broker"
    port => 1883
    topic => "sensors/#"
    codec => json
  }
}

filter {
  # Ajouter metadata
  mutate {
    add_field => {
      "device_type" => "sensor"
    }
  }
  
  # Convertir types
  mutate {
    convert => {
      "temperature" => "float"
      "humidity" => "float"
    }
  }
  
  # Alertes sur seuils
  if [temperature] > 30 {
    mutate {
      add_tag => ["high_temperature"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "iot-sensors-%{+YYYY.MM.dd}"
  }
}

# Kibana:
# - Time series température/humidité
# - Heatmap par location
# - Alertes sur anomalies
# - Prédictions ML


[OK] OUTILS COMPLÉMENTAIRES

# === Curator (Gestion automatique index) ===

# Installer
pip install elasticsearch-curator

# Configuration: curator.yml
client:
  hosts:
    - localhost
  port: 9200
  timeout: 30

# Actions: actions.yml
actions:
  1:
    action: delete_indices
    description: Supprimer index > 30 jours
    options:
      ignore_empty_list: True
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 30
  
  2:
    action: forcemerge
    description: Forcemerge index > 2 jours
    options:
      max_num_segments: 1
    filters:
    - filtertype: pattern
      kind: prefix
      value: logs-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 2

# Exécuter
curator --config curator.yml actions.yml

# Cron quotidien
# crontab -e
0 2 * * * /usr/local/bin/curator --config /etc/curator/curator.yml /etc/curator/actions.yml

# === ElastAlert (Alerting avancé) ===

# Installer
pip install elastalert

# Configuration: config.yaml
rules_folder: rules
run_every:
  minutes: 1
buffer_time:
  minutes: 15
es_host: localhost
es_port: 9200
writeback_index: elastalert_status

# Règle: spike_rule.yaml
name: Spike in errors
type: spike
index: logs-*
timeframe:
  minutes: 10
threshold_cur: 5
threshold_ref: 5
spike_height: 2
spike_type: up
filter:
- term:
    level: "ERROR"
alert:
- email
email:
- ops@example.com

# Lancer
elastalert --config config.yaml --rule spike_rule.yaml

# === Elasticsearch SQL ===

# Requêtes SQL sur Elasticsearch (v7+)
curl -X POST "localhost:9200/_sql?format=txt&pretty" -H 'Content-Type: application/json' -d'
{
  "query": "SELECT @timestamp, level, message FROM \"logs-*\" WHERE level = '\''ERROR'\'' LIMIT 10"
}
'

# Avec Kibana Console:
POST _sql?format=txt
{
  "query": "SELECT COUNT(*) FROM \"logs-*\" GROUP BY level"
}

# Translate to Query DSL:
POST _sql/translate
{
  "query": "SELECT * FROM \"logs-*\" WHERE response_code >= 400"
}

# === Elastic APM (Application Performance Monitoring) ===

# Installer APM Server
apt-get install apm-server

# Configuration: apm-server.yml
apm-server:
  host: "0.0.0.0:8200"

output.elasticsearch:
  hosts: ["localhost:9200"]

setup.kibana:
  host: "localhost:5601"

# Instrumenter application (Python exemple)
pip install elastic-apm

# app.py
from elasticapm import Client
from elasticapm.contrib.flask import ElasticAPM

app = Flask(__name__)
app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': 'my-app',
    'SERVER_URL': 'http://localhost:8200',
    'ENVIRONMENT': 'production',
}
apm = ElasticAPM(app)

# Voir traces dans Kibana APM

# === Elasticsearch Watcher (Alerting natif) ===

# Créer watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate?pretty" -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": {
      "interval": "5m"
    }
  },
  "input": {
    "search": {
      "request": {
        "indices": ["logs-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "level": "ERROR"
                  }
                },
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-5m"
                    }
                  }
                }
              ]
            }
          },
          "aggs": {
            "error_count": {
              "value_count": {
                "field": "level"
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.aggregations.error_count.value": {
        "gt": 100
      }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "ops@example.com",
        "subject": "High error rate detected",
        "body": "Detected {{ctx.payload.aggregations.error_count.value}} errors in last 5 minutes"
      }
    }
  }
}
'

# Lister watches
curl -X GET "localhost:9200/_watcher/_query/watches?pretty"

# Activer/Désactiver watch
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_activate?pretty"
curl -X PUT "localhost:9200/_watcher/watch/high_error_rate/_deactivate?pretty"

# === Elasticsearch Hadoop ===

# Connecter Elasticsearch avec Hadoop/Spark

# Spark exemple (Scala):
import org.elasticsearch.spark.sql._

val df = spark.read
  .format("es")
  .load("logs-*/doc")

df.filter(df("level") === "ERROR")
  .groupBy("source")
  .count()
  .show()


[OK] RESSOURCES & DOCUMENTATION

# === Documentation officielle ===

# Elasticsearch
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

# Logstash
https://www.elastic.co/guide/en/logstash/current/index.html

# Kibana
https://www.elastic.co/guide/en/kibana/current/index.html

# Beats
https://www.elastic.co/guide/en/beats/libbeat/current/index.html

# === Guides pratiques ===

# Getting Started
https://www.elastic.co/guide/en/elastic-stack-get-started/current/index.html

# Elasticsearch: The Definitive Guide (livre)
https://www.elastic.co/guide/en/elasticsearch/guide/current/index.html

# Blog Elastic
https://www.elastic.co/blog/

# === Forums & Support ===

# Discuss Elastic (forum communauté)
https://discuss.elastic.co/

# GitHub Issues
https://github.com/elastic/elasticsearch/issues

# Stack Overflow
https://stackoverflow.com/questions/tagged/elasticsearch

# === Formations ===

# Elastic Training (officiel)
https://www.elastic.co/training/

# Free fundamentals courses
https://www.elastic.co/training/free

# === Outils en ligne ===

# Grok Debugger (tester patterns)
http://grokdebug.herokuapp.com/

# JSON formatter
https://jsonformatter.org/

# Elasticsearch Head (plugin navigateur)
https://github.com/mobz/elasticsearch-head

# === Communauté ===

# Meetups Elastic
https://www.elastic.co/community/

# ElasticON (conférence annuelle)
https://www.elastic.co/elasticon/

# === Versions & Compatibilité ===

# Support matrix
https://www.elastic.co/support/matrix

# Release notes
https://www.elastic.co/downloads/past-releases

# Breaking changes
https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html


[OK] EXEMPLES DE SCRIPTS MAINTENANCE

# === Backup automatique quotidien (Bash) ===

#!/bin/bash
# backup_elasticsearch.sh

REPOSITORY="my_backup"
SNAPSHOT_NAME="snapshot_$(date +%Y%m%d_%H%M%S)"
ES_HOST="localhost:9200"

# Créer snapshot
curl -X PUT "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}?wait_for_completion=false" \
  -H 'Content-Type: application/json' -d'
{
  "indices": "logs-*,metrics-*",
  "ignore_unavailable": true,
  "include_global_state": false
}
'

# Vérifier statut
sleep 10
STATUS=$(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/${SNAPSHOT_NAME}" | jq -r '.snapshots[0].state')

if [ "$STATUS" == "SUCCESS" ]; then
  echo "Backup réussi: ${SNAPSHOT_NAME}"
  
  # Supprimer snapshots > 7 jours
  CUTOFF_DATE=$(date -d "7 days ago" +%Y%m%d)
  for snapshot in $(curl -s "${ES_HOST}/_snapshot/${REPOSITORY}/_all" | jq -r '.snapshots[].snapshot'); do
    SNAPSHOT_DATE=$(echo $snapshot | grep -oP '\d{8}')
    if [ "$SNAPSHOT_DATE" -lt "$CUTOFF_DATE" ]; then
      echo "Suppression ancien snapshot: $snapshot"
      curl -X DELETE "${ES_HOST}/_snapshot/${REPOSITORY}/${snapshot}"
    fi
  done
else
  echo "Erreur backup: ${STATUS}"
  exit 1
fi

# Cron: 0 2 * * * /usr/local/bin/backup_elasticsearch.sh

# === Monitoring santé cluster (Python) ===

#!/usr/bin/env python3
# monitor_cluster.py

import requests
import json
import sys
from datetime import datetime

ES_HOST = "http://localhost:9200"
WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

def check_cluster_health():
    try:
        response = requests.get(f"{ES_HOST}/_cluster/health")
        health = response.json()
        
        status = health['status']
        cluster_name = health['cluster_name']
        
        if status in ['yellow', 'red']:
            message = {
                "text": f"[ATTENTION] Cluster {cluster_name} status: {status}",
                "attachments": [{
                    "color": "warning" if status == "yellow" else "danger",
                    "fields": [
                        {"title": "Status", "value": status, "short": True},
                        {"title": "Nodes", "value": str(health['number_of_nodes']), "short": True},
                        {"title": "Active Shards", "value": str(health['active_shards']), "short": True},
                        {"title": "Unassigned Shards", "value": str(health['unassigned_shards']), "short": True},
                    ],
                    "footer": f"Checked at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
                }]
            }
            
            # Envoyer alerte Slack
            requests.post(WEBHOOK_URL, json=message)
            return False
        
        return True
        
    except Exception as e:
        print(f"Erreur: {e}")
        sys.exit(1)

def check_disk_usage():
    try:
        response = requests.get(f"{ES_HOST}/_cat/allocation?format=json")
        allocations = response.json()
        
        for alloc in allocations:
            disk_percent = float(alloc['disk.percent'])
            if disk_percent > 85:
                message = {
                    "text": f"[ROUGE] Disk usage high on node {alloc['node']}: {disk_percent}%"
                }
                requests.post(WEBHOOK_URL, json=message)
                
    except Exception as e:
        print(f"Erreur: {e}")

if __name__ == "__main__":
    check_cluster_health()
    check_disk_usage()

# Cron: */5 * * * * /usr/local/bin/monitor_cluster.py

# === Nettoyage index anciens (Python) ===

#!/usr/bin/env python3
# cleanup_old_indices.py

import requests
from datetime import datetime, timedelta

ES_HOST = "http://localhost:9200"
RETENTION_DAYS = 30
INDEX_PATTERN = "logs-"

def get_indices():
    response = requests.get(f"{ES_HOST}/_cat/indices?h=index&format=json")
    return [idx['index'] for idx in response.json()]

def delete_old_indices():
    indices = get_indices()
    cutoff_date = datetime.now() - timedelta(days=RETENTION_DAYS)
    
    for index in indices:
        if not index.startswith(INDEX_PATTERN):
            continue
            
        try:
            # Extraire date du nom (format: logs-YYYY.MM.DD)
            date_str = index.replace(INDEX_PATTERN, "")
            index_date = datetime.strptime(date_str, "%Y.%m.%d")
            
            if index_date < cutoff_date:
                print(f"Suppression index: {index}")
                response = requests.delete(f"{ES_HOST}/{index}")
                if response.status_code == 200:
                    print(f"[OK] {index} supprimé")
                else:
                    print(f"[X] Erreur suppression {index}: {response.text}")
                    
        except ValueError:
            print(f"Format date invalide pour: {index}")
            continue

if __name__ == "__main__":
    delete_old_indices()

# Cron: 0 3 * * * /usr/local/bin/cleanup_old_indices.py


# === FIN DE LA CHEATSHEET ===

# Cette cheatsheet couvre:
# [OK] Installation complète ELK Stack
# [OK] Configuration Elasticsearch, Logstash, Kibana, Filebeat
# [OK] API REST Elasticsearch (CRUD, recherche, agrégations)
# [OK] Pipelines Logstash avec exemples réels
# [OK] Visualisations et dashboards Kibana
# [OK] Gestion de sécurité (X-Pack)
# [OK] Monitoring et performance
# [OK] ILM (Index Lifecycle Management)
# [OK] Dépannage et problèmes courants
# [OK] Cas d'usage pratiques
# [OK] Outils complémentaires
# [OK] Scripts de maintenance
# [OK] Bonnes pratiques

# Pour aller plus loin:
# - Elastic Certified Engineer
# - Architecture clusters multi-nœuds
# - Machine Learning avancé
# - Cross-cluster search
# - Elasticsearch SQL
# - Canvas pour présentations
# - APM (Application Performance Monitoring)