Retour au cours

projet : analyseur de logs

Objectif du Projet

Mettre en pratique les compétences acquises en manipulation de fichiers, chaînes de caractères, expressions régulières et hashes pour créer un outil qui analyse un fichier de log de serveur web et en extrait des statistiques utiles.

Le Scénario

Vous avez un fichier de log d’un serveur web (comme Nginx ou Apache) et vous voulez rapidement répondre à deux questions :

  1. Quelles sont les pages les plus visitées ?
  2. Quelles sont les adresses IP qui font le plus de requêtes ?

Format de log type

Chaque ligne ressemble à ceci, avec l’IP, la date, la requête, etc. 1.2.3.4 - - [12/Jul/2025:10:00:00 +0200] "GET /page1.html HTTP/1.1" 200 1234

La Stratégie

  1. Prendre un nom de fichier en argument.
  2. Lire ce fichier ligne par ligne.
  3. Pour chaque ligne, utiliser une expression régulière (Regex) pour extraire l’adresse IP et la page demandée.
  4. Stocker ces informations dans deux Hashes qui serviront de compteurs.
  5. À la fin, trier les hashes par nombre d’occurrences et afficher les plus populaires.

Le Script Complet

Ce script utilise la puissance de Ruby pour le traitement de texte.

#!/usr/bin/env ruby

# On vérifie qu'un nom de fichier est bien passé en argument
unless ARGV.length == 1
  puts "Usage: #{$0} <fichier_log>"
  exit 1
end

LOG_FILE = ARGV[0]

unless File.exist?(LOG_FILE)
  puts "Erreur : le fichier '#{LOG_FILE}' n'existe pas."
  exit 1
end

# Regex pour capturer l'IP (groupe 1) et la page demandée (groupe 2)
# Ex: 1.2.3.4 ... "GET /page.html ..."
LOG_REGEX = /^(\S+) \S+ \S+ \[.*?\] "GET (\S+) \S+"/ 

# Hash.new(0) est une astuce : si on demande une clé qui n'existe pas,
# elle est créée avec la valeur par défaut 0.
# page_visits["/une/nouvelle/page"] += 1 fonctionne donc directement.
page_visits = Hash.new(0)
ip_counts = Hash.new(0)
total_lines = 0

puts "Analyse de '#{LOG_FILE}' en cours..."

File.foreach(LOG_FILE) do |line|
  total_lines += 1
  match = line.match(LOG_REGEX)

  # On passe à la ligne suivante si la ligne ne correspond pas à notre format
  next unless match

  # match[1] contient le premier groupe capturé (l'IP)
  # match[2] contient le deuxième groupe (la page)
  ip = match[1]
  page = match[2]

  page_visits[page] += 1
  ip_counts[ip] += 1
end

puts "\n--- Analyse terminée ---"
puts "Lignes totales lues : #{total_lines}"

puts "\n--- Top 5 des pages les plus visitées ---"
# 1. .sort_by trie le hash par valeur (le compte)
# 2. .reverse pour avoir les plus grands en premier
# 3. .first(5) pour ne garder que les 5 premiers
top_pages = page_visits.sort_by { |page, count| count }.reverse.first(5)
top_pages.each_with_index do |(page, count), index|
  puts "#{index + 1}. #{page} (#{count} visites)"
end

puts "\n--- Top 5 des adresses IP les plus actives ---"
top_ips = ip_counts.sort_by { |ip, count| count }.reverse.first(5)
top_ips.each_with_index do |(ip, count), index|
  puts "#{index + 1}. #{ip} (#{count} requêtes)"
end

Bonnes Pratiques Mises en Œuvre

  • Validation des arguments : Le script vérifie la présence et l’existence du fichier de log avant de commencer.
  • Lecture de fichier efficace : File.foreach lit le fichier ligne par ligne et est donc très efficace en mémoire.
  • Expressions régulières : C’est l’outil parfait pour extraire des informations structurées d’un texte.
  • Hash.new(0) : Une technique idiomatique en Ruby pour créer facilement des compteurs.
  • Chaînage de méthodes Enumerable : La ligne sort_by{...}.reverse.first(5) est un excellent exemple de la puissance et de l’expressivité de Ruby pour la manipulation de collections.

Exercices

  1. Créer un fichier de test :

    • Créez un fichier test.log.
    • Copiez-y une dizaine de lignes en respectant le format, en variant les IPs et les pages, et en ajoutant quelques doublons.
    • Lancez le script : ruby parser.rb test.log.
  2. Ajouter une statistique :

    • Modifiez le script pour qu’il compte et affiche également le nombre de requêtes qui ont résulté en une erreur 404 Not Found.
    • Indice : il faudra modifier l’expression régulière pour capturer aussi le code de statut HTTP.
    • Format type : ... "GET /page HTTP/1.1" 404 ...
  3. Filtrer par IP :

    • Modifiez le script pour qu’il accepte un deuxième argument optionnel : une adresse IP.
    • Si cet argument est fourni, le script ne doit analyser et afficher que les lignes de log correspondant à cette adresse IP.