edge
Plus sur rubyonrails.org: Plus de Ruby on Rails

Rapports d'erreurs dans les applications Rails

Ce guide présente les différentes façons de gérer les exceptions qui se produisent dans les applications Ruby on Rails.

Après avoir lu ce guide, vous saurez :

1 Rapport d'erreurs

Le rapporteur d'erreurs de Rails error reporter fournit une méthode standard pour collecter les exceptions qui se produisent dans votre application et les signaler à votre service ou emplacement préféré.

Le rapporteur d'erreurs vise à remplacer le code de gestion d'erreur redondant comme celui-ci :

begin
  faire_quelque_chose
rescue QuelqueChoseEstCassé => erreur
  MonServiceDeRapportD'Erreurs.notifier(erreur)
end

avec une interface cohérente :

Rails.error.handle(QuelqueChoseEstCassé) do
  faire_quelque_chose
end

Rails enveloppe toutes les exécutions (comme les requêtes HTTP, les jobs et les invocations de rails runner) dans le rapporteur d'erreurs, de sorte que toutes les erreurs non gérées levées dans votre application seront automatiquement signalées à votre service de rapport d'erreurs via leurs abonnés.

Cela signifie que les bibliothèques de rapport d'erreurs tierces n'ont plus besoin d'insérer un middleware Rack ou de faire des patchs pour capturer les exceptions non gérées. Les bibliothèques qui utilisent ActiveSupport peuvent également l'utiliser pour signaler de manière non intrusive les avertissements qui auraient été perdus auparavant dans les journaux.

L'utilisation du rapporteur d'erreurs de Rails n'est pas obligatoire. Tous les autres moyens de capturer les erreurs fonctionnent toujours.

1.1 Abonnement au rapporteur

Pour utiliser le rapporteur d'erreurs, vous avez besoin d'un abonné. Un abonné est n'importe quel objet avec une méthode report. Lorsqu'une erreur se produit dans votre application ou est signalée manuellement, le rapporteur d'erreurs de Rails appelle cette méthode avec l'objet d'erreur et certaines options.

Certaines bibliothèques de rapport d'erreurs, telles que Sentry et Honeybadger, enregistrent automatiquement un abonné pour vous. Consultez la documentation de votre fournisseur pour plus de détails.

Vous pouvez également créer un abonné personnalisé. Par exemple :

# config/initializers/error_subscriber.rb
class ErrorSubscriber
  def report(erreur, géré:, gravité:, contexte:, source: nil)
    MonServiceDeRapportD'Erreurs.rapporter_erreur(erreur, contexte: contexte, géré: géré, niveau: gravité)
  end
end

Après avoir défini la classe de l'abonné, enregistrez-la en appelant la méthode Rails.error.subscribe :

Rails.error.subscribe(ErrorSubscriber.new)

Vous pouvez enregistrer autant d'abonnés que vous le souhaitez. Rails les appellera successivement, dans l'ordre dans lequel ils ont été enregistrés.

NOTE : Le rapporteur d'erreurs de Rails appellera toujours les abonnés enregistrés, quel que soit votre environnement. Cependant, de nombreux services de rapport d'erreurs ne signalent que les erreurs en production par défaut. Vous devez configurer et tester votre configuration dans tous les environnements selon vos besoins.

1.2 Utilisation du rapporteur d'erreurs

Il existe trois façons d'utiliser le rapporteur d'erreurs :

1.2.1 Signalement et suppression des erreurs

Rails.error.handle signalera toute erreur levée dans le bloc. Elle supprimera ensuite l'erreur, et le reste de votre code en dehors du bloc continuera normalement.

résultat = Rails.error.handle do
  1 + '1' # lève TypeError
end
résultat # => nil
1 + 1 # Cela sera exécuté

Si aucune erreur n'est levée dans le bloc, Rails.error.handle renverra le résultat du bloc, sinon il renverra nil. Vous pouvez remplacer cela en fournissant une fallback :

utilisateur = Rails.error.handle(fallback: -> { Utilisateur.anonyme }) do
  Utilisateur.find_by(params[:id])
end

1.2.2 Signalement et re-lancement des erreurs

Rails.error.record signalera les erreurs à tous les abonnés enregistrés, puis re-lancera l'erreur, ce qui signifie que le reste de votre code ne sera pas exécuté.

Rails.error.record do
  1 + '1' # lève TypeError
end
1 + 1 # Cela ne sera pas exécuté

Si aucune erreur n'est levée dans le bloc, Rails.error.record renverra le résultat du bloc.

1.2.3 Signalement manuel des erreurs

Vous pouvez également signaler manuellement des erreurs en appelant Rails.error.report :

begin
  # code
rescue StandardError => e
  Rails.error.report(e)
end

Toutes les options que vous transmettez seront transmises aux abonnés d'erreurs.

1.3 Options de signalement d'erreurs

Les 3 API de signalement (#handle, #record et #report) prennent en charge les options suivantes, qui sont ensuite transmises à tous les abonnés enregistrés :

  • géré : un Boolean pour indiquer si l'erreur a été gérée. Cela est défini par défaut sur true. #record le définit sur false.
  • gravité : un Symbol décrivant la gravité de l'erreur. Les valeurs attendues sont : :error, :warning et :info. #handle le définit sur :warning, tandis que #record le définit sur :error.
  • contexte : un Hash pour fournir plus de contexte sur l'erreur, comme les détails de la requête ou de l'utilisateur.
  • source : une String sur la source de l'erreur. La source par défaut est "application". Les erreurs signalées par des bibliothèques internes peuvent utiliser d'autres sources ; par exemple, la bibliothèque de cache Redis peut utiliser "redis_cache_store.active_support". Votre abonné peut utiliser la source pour ignorer les erreurs qui ne vous intéressent pas. ruby Rails.error.handle(contexte: { user_id: user.id }, gravite: :info) do # ... end

1.4 Filtrage par classes d'erreurs

Avec Rails.error.handle et Rails.error.record, vous pouvez également choisir de ne signaler que les erreurs de certaines classes. Par exemple:

Rails.error.handle(IOError) do
  1 + '1' # lève TypeError
end
1 + 1 # Les TypeErrors ne sont pas des IOError, donc cela ne sera *pas* exécuté

Ici, le TypeError ne sera pas capturé par le rapporteur d'erreurs de Rails. Seules les instances de IOError et de ses descendants seront signalées. Toutes les autres erreurs seront levées normalement.

1.5 Définition du contexte globalement

En plus de définir le contexte via l'option contexte, vous pouvez utiliser l'API #set_context. Par exemple:

Rails.error.set_context(section: "checkout", user_id: @user.id)

Tout contexte défini de cette manière sera fusionné avec l'option contexte

Rails.error.set_context(a: 1)
Rails.error.handle(contexte: { b: 2 }) { raise }
# Le contexte signalé sera: {:a=>1, :b=>2}
Rails.error.handle(contexte: { b: 3 }) { raise }
# Le contexte signalé sera: {:a=>1, :b=>3}

1.6 Pour les bibliothèques

Les bibliothèques de signalement d'erreurs peuvent enregistrer leurs abonnés dans un Railtie:

module MySdk
  class Railtie < ::Rails::Railtie
    initializer "my_sdk.error_subscribe" do
      Rails.error.subscribe(MyErrorSubscriber.new)
    end
  end
end

Si vous enregistrez un abonné d'erreur, mais que vous avez toujours d'autres mécanismes d'erreur comme un middleware Rack, vous risquez de signaler les erreurs plusieurs fois. Vous devez soit supprimer vos autres mécanismes, soit ajuster votre fonctionnalité de rapport pour qu'elle ignore le signalement d'une exception qu'elle a déjà rencontrée.

Retour d'information

Vous êtes encouragé à contribuer à l'amélioration de la qualité de ce guide.

Veuillez contribuer si vous trouvez des fautes de frappe ou des erreurs factuelles. Pour commencer, vous pouvez lire notre contribution à la documentation section.

Vous pouvez également trouver du contenu incomplet ou des informations qui ne sont pas à jour. Veuillez ajouter toute documentation manquante pour la version principale. Assurez-vous de vérifier Edge Guides d'abord pour vérifier si les problèmes ont déjà été résolus ou non sur la branche principale. Consultez les Directives des guides Ruby on Rails pour le style et les conventions.

Si pour une raison quelconque vous repérez quelque chose à corriger mais ne pouvez pas le faire vous-même, veuillez ouvrir un problème.

Et enfin, toute discussion concernant la documentation de Ruby on Rails est la bienvenue sur le Forum officiel de Ruby on Rails.