Pourquoi Logging et monitoring Rails ?
Au quotidien, un développeur Ruby on Rails peut rencontrer des problèmes imprévus que le debugger ne peut pas capturer tout seul. Un bon système de logging et de monitoring est essentiel pour identifier les erreurs, comprendre leur source et les corriger rapidement.
Un cas concret : imaginez une application web qui gère des paiements pour des clients. Si un utilisateur rencontre un problème avec son paiement, comment saura-t-on que le problème vient du côté serveur ? Avec une bonne configuration de logging et de monitoring, vous pourriez détecter ce problème dans les journaux et diagnostiquer rapidement l'origine.
Prerequis
- Connaissance avancée de Ruby on Rails
- Familiarité avec les bases de la programmation asynchrone et des tâches en arrière-plan (Background Jobs)
- Connaissance des outils de monitoring et logging comme Sidekiq, Delayed Job ou ActiveJob
- Expérience avec les systèmes d'alerte et de notifications
Concepts fondamentaux
Logging
Le logging est le processus de stockage des informations sur l'exécution d'une application. Ces informations peuvent être utilisées pour déboguer les erreurs, surveiller les performances et analyser les tendances.
Schéma mental :
- Logger : Objet qui enregistre les messages.
- Levels : Niveaux de gravité des messages (DEBUG, INFO, WARN, ERROR, FATAL).
- Files : Destination des journaux (console, fichiers).
## config/environments/development.rb
Rails.application.configure do
config.logger = Logger.new(STDOUT)
config.log_level = :debug
end
Monitoring
Le monitoring est le processus de surveillance en temps réel de la performance et des état d'une application. Cela permet de détecter les problèmes et de prendre des mesures immédiates.
Schéma mental :
- Metrics : Mesures clés de performance (CPU, mémoire, requêtes HTTP).
- Alerts : Notifications automatiques en cas de défaillance.
- Dashboard : Vue d'ensemble des performances et des alertes.
Middleware
Les middleware sont des composants qui s'exécutent entre les requêtes et les réponses dans le cycle de traitement des requêtes d'une application Rails. Ils peuvent être utilisés pour ajouter des fonctionnalités comme la compression, la sécurité et le logging.
Schéma mental :
- Request : Requête HTTP.
- Response : Réponse HTTP.
- Middleware Stack : Ordre des middleware à exécuter.
## app/middleware/custom_logger.rb
class CustomLogger
def initialize(app)
@app = app
end
def call(env)
Rails.logger.info "Request received: #{env['REQUEST_URI']}"
status, headers, body = @app.call(env)
Rails.logger.info "Response sent: #{status}"
[status, headers, body]
end
end
Mise en pratique : projet fil rouge
Mini-projet : Gestionnaire de tâches
Nous allons créer un simple gestionnaire de tâches avec les fonctionnalités suivantes :
- Ajouter une nouvelle tâche
- Marquer une tâche comme terminée
- Supprimer une tâche
Étape 1 : Configuration du projet
rails new task_manager
cd task_manager
Étape 2 : Création des modèles et migrations
rails generate model Task title:string description:text completed:boolean
rake db:migrate
Étape 3 : Création des contrôleurs et vues
rails generate controller Tasks index new create edit update destroy
Créer les vues correspondantes dans app/views/tasks.
Étape 4 : Implémentation des routes
Modifier config/routes.rb pour ajouter les routes nécessaires.
Rails.application.routes.draw do
resources :tasks, only: [:index, :new, :create, :edit, :update, :destroy]
end
Étape 5 : Ajout de middleware personnalisé
Ajouter le middleware CustomLogger dans app/middleware/custom_logger.rb.
## app/middleware/custom_logger.rb
class CustomLogger
def initialize(app)
@app = app
end
def call(env)
Rails.logger.info "Request received: #{env['REQUEST_URI']}"
status, headers, body = @app.call(env)
Rails.logger.info "Response sent: #{status}"
[status, headers, body]
end
end
Ajouter le middleware dans config/application.rb.
## config/application.rb
module TaskManager
class Application < Rails::Application
# Middleware custom pour logging des requêtes
config.middleware.use CustomLogger
end
end
Étape 6 : Ajout de monitoring avec Prometheus et Sidekiq
Installer les gems nécessaires.
gem 'prometheus-client'
gem 'sidekiq'
bundle install
Créer un fichier config/prometheus.yml pour configurer Prometheus.
## config/prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'rails'
static_configs:
- targets: ['localhost:3000']
- job_name: 'sidekiq'
static_configs:
- targets: ['localhost:7418']
Créer un fichier config/sidekiq.yml pour configurer Sidekiq.
## config/sidekiq.yml
---
:concurrency: 25
:queues:
- default
- mailers
Étape 7 : Ajout de logging personnalisé
Ajouter des logs personnalisés dans le middleware et les contrôleurs.
## app/middleware/custom_logger.rb
class CustomLogger
def initialize(app)
@app = app
end
def call(env)
Rails.logger.info "Request received: #{env['REQUEST_URI']}"
status, headers, body = @app.call(env)
Rails.logger.info "Response sent: #{status}"
[status, headers, body]
end
end
ruby
## app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
Rails.logger.info 'Listing tasks'
@tasks = Task.all
end
def new
Rails.logger.info 'Creating a new task'
@task = Task.new
end
def create
Rails.logger.info 'Creating task with params: #{params[:task]}'
@task = Task.new(task_params)
if @task.save
Rails.logger.info 'Task created successfully'
redirect_to tasks_path, notice: 'Task was successfully created.'
else
Rails.logger.error 'Failed to create task'
render :new
end
end
def edit
Rails.logger.info "Editing task with id #{params[:id]}"
@task = Task.find(params[:id])
end
def update
Rails.logger.info "Updating task with params: #{params[:task]} and id #{params[:id]}"
@task = Task.find(params[:id])
if @task.update(task_params)
Rails.logger.info 'Task updated successfully'
redirect_to tasks_path, notice: 'Task was successfully updated.'
else
Rails.logger.error 'Failed to update task'
render :edit
end
end
def destroy
Rails.logger.info "Deleting task with id #{params[:id]}"
@task = Task.find(params[:id])
@task.destroy
Rails.logger.info 'Task deleted successfully'
redirect_to tasks_path, notice: 'Task was successfully destroyed.'
end
private
def task_params
params.require(:task).permit(:title, :description, :completed)
end
end
Erreurs frequentes et debugging
Erreur 1 : NoMethodError dans un contrôleur
Message d'erreur :
NoMethodError (undefined method `save' for nil:NilClass):
app/controllers/tasks_controller.rb:16:in `create'
Code incorrect :
def create
@task = Task.new(params[:task])
if @task.save
redirect_to tasks_path, notice: 'Task was successfully created.'
else
render :new
end
end
Code correct :
def create
@task = Task.new(task_params)
if @task.save
redirect_to tasks_path, notice: 'Task was successfully created.'
else
render :new
end
end
private
def task_params
params.require(:task).permit(:title, :description, :completed)
end
Erreur 2 : ActiveRecord::RecordInvalid dans un contrôleur
Message d'erreur :
ActiveRecord::RecordInvalid (Validation failed: Title can't be blank):
app/controllers/tasks_controller.rb:16:in `create'
Code incorrect :
def create
@task = Task.new(params[:task])
if @task.save!
redirect_to tasks_path, notice: 'Task was successfully created.'
else
render :new
end
end
Code correct :
def create
@task = Task.new(task_params)
if @task.save!
redirect_to tasks_path, notice: 'Task was successfully created.'
else
render :new
end
end
private
def task_params
params.require(:task).permit(:title, :description, :completed)
end
Erreur 3 : Sidekiq::Worker::Error dans un worker Sidekiq
Message d'erreur :
Sidekiq::Worker::Error (undefined method `perform' for nil:NilClass):
app/workers/my_worker.rb:5:in `perform'
Code incorrect :
class MyWorker
include Sidekiq::Worker
def perform(arg)
task = Task.find(arg)
task.perform!
end
end
Code correct :
class MyWorker
include Sidekiq::Worker
def perform(task_id)
task = Task.find(task_id)
task.perform!
end
end
Pour aller plus loin
Piste 1 : Utilisation de Sentry pour le monitoring des erreurs en production
Lien vers la documentation de Sentry avec Rails : https://docs.sentry.io/platforms/ruby/
Piste 2 : Intégration de New Relic pour le monitoring des performances
Lien vers la documentation de New Relic avec Rails : https://docs.newrelic.com/docs/ruby/new-relic-ruby/getting-started/install-new-relic-rails/
Piste 3 : Création d'un système de logging personnalisé pour les journaux métier
Créez un fichier config/initializers/custom_logger.rb et configurez un logger personnalisé qui enregistre des informations métier.
## config/initializers/custom_logger.rb
Rails.application.config.middleware.use CustomLogger, level: :info
Défi pratique : Ajout d'un système de notifications pour les tâches terminées
Implémentez une notification email ou SMS lorsqu'une tâche est marquée comme terminée.
Lien vers la documentation Action Mailer Rails : https://guides.rubyonrails.org/action_mailer_basics.html