Comment gérer les exceptions dans Elixir

La gestion des exceptions est une excellente pratique pour toute méthodologie de développement logiciel. Que ce soit pour un développement basé sur des tests, des sprints agiles ou une session de piratage avec juste une bonne vieille liste de tâches, nous pouvons tous tirer profit de l'assurance que nos bases sont couvertes d'une approche robuste de la gestion des défauts. 

Il est primordial de veiller à ce que les erreurs soient corrigées, tout en étant esthétiques et, bien entendu, de ne pas devenir un gros problème logiquement avec des messages cryptés pour que l'utilisateur final puisse en tirer une signification. Si vous faites cela, vous êtes certainement sur la bonne voie pour créer une application solide, stable et collante avec laquelle les utilisateurs aiment travailler et que vous recommanderez vivement..

Idéalement pour nous, Elixir assure une gestion étendue des exceptions via plusieurs mécanismes tels que essayer / attraper, jette, et le : erreur, raison tuple.

Pour afficher une erreur, utilisez élever dans votre shell interactif pour avoir un avant-goût:

iex> augmenter "Oh noez!" ** (RuntimeError) Oh noez!

Nous pouvons aussi ajouter un type à ceci comme ceci:

iex> raise ArgumentError, message: "message d'erreur ici ..." ** message d'erreur (ArgumentError) ici ... 

Comment la gestion des erreurs fonctionne dans Elixir

Certaines des manières dont les erreurs sont traitées dans Elixir peuvent ne pas être évidentes à première vue.

  • Tout d'abord sur les processus en utilisant frayer, nous pouvons créer des processus indépendants. Cela signifie qu'une défaillance sur un thread ne devrait affecter aucun autre processus, sauf en cas de liaison. Mais par défaut, tout restera stable.
  • Pour notifier au système une défaillance de l’un de ces processus, nous pouvons utiliser le spawn_link macro. Il s’agit d’un lien bidirectionnel, ce qui signifie que si un processus lié se termine, un signal de sortie sera déclenché..
  • Si le signal de sortie est autre chose que :Ordinaire, nous savons que nous avons un problème. Et si on intercepte le signal de sortie avec Process.flag (: trap_exit, true), le signal de sortie sera envoyé à la boîte aux lettres du processus, où la logique peut être placée sur la façon de gérer le message, évitant ainsi un crash dur.
  • Enfin, nous avons des moniteurs, qui sont similaires à spawn_links, mais ce sont des liens unidirectionnels, et nous pouvons les créer avec Process.monitor
  • Le processus qui invoque le Process.monitor recevra les messages d'erreur en cas d'échec.

Pour un exemple d'erreur, essayez d'ajouter un nombre à un atome et vous obtiendrez ce qui suit:

iex>: foo + 69 ** (ArithmeticError) argument incorrect dans l'expression arithmétique: erlang. + (: foo, 69)

Pour vous assurer que l'utilisateur final ne se trompe pas, nous pouvons utiliser les méthodes try, catch and rescue fournies par Elixir..

Essayez / Rescue

Le premier de notre boîte à outils pour la gestion des exceptions est essayer / sauvetage, qui attrape les erreurs produites en utilisant élever est donc vraiment mieux adapté pour les erreurs de développement, ou des circonstances exceptionnelles telles que des erreurs de saisie.

essayer / sauvetage est similaire à un usage essayer / attraper bloquer que vous avez peut-être vu dans d'autres langages de programmation. Regardons un exemple en action:

iex> try do ...> relance "do failed!" ...> rescue ...> e dans RuntimeError -> IO.puts ("Erreur:" <> e.message) ...> end Erreur: ne réussit pas! :D'accord

Ici nous utilisons le essayer / sauvetage bloc et le susdit élever attraper le Erreur d'exécution

Cela signifie que le ** (Erreur d'exécution) sortie par défaut de élever n'est pas affiché et est remplacé par une sortie plus agréable de la IO.puts appel. 

Il est recommandé d'utiliser le message d'erreur pour donner à l'utilisateur une sortie utile en anglais simple, ce qui l'aide à résoudre le problème. Nous examinerons cela plus en détail dans l'exemple suivant.

Plusieurs erreurs dans un essai / sauvetage

Un avantage majeur de Elixir est que vous pouvez obtenir plusieurs résultats dans l’un de ces essayer / sauvetage des blocs. Regardez cet exemple:

essayez d'essayer |> Keyword.fetch! (: fichier_source) |> File.read! rescue e dans KeyError -> IO.puts "manquant: option source_file" e dans File.Error -> IO.puts "impossible de lire le fichier source" fin

Ici nous avons attrapé deux erreurs dans le porter secours

  1. Si le fichier est incapable de lire.
  2. Si la :fichier source le symbole est manquant. 

Comme mentionné précédemment, nous pouvons l'utiliser pour créer des messages d'erreur faciles à comprendre pour notre utilisateur final.. 

Cette approche syntaxique puissante et minimale d’Elixir rend l’écriture de plusieurs vérifications très accessible, ce qui nous permet de vérifier de nombreux points de défaillance possibles, de manière nette et concise. Cela nous aide à nous assurer que nous n’avons pas besoin d’écrire des conditionnelles complexes pour créer des scripts longs qui peuvent être difficiles à visualiser et à déboguer correctement lors d’un développement ultérieur ou à la connexion d’un nouveau développeur..

Comme toujours lorsque vous travaillez à Elixir, KISS est la meilleure approche à adopter.. 

Après

Il existe des situations dans lesquelles vous devrez exécuter une action spécifique après le blocage try / rescue, qu'il y ait eu une erreur ou non. Pour les développeurs Java ou PHP, vous pouvez penser à la essayer / attraper / enfin ou de Ruby commencer / secourir / assurer.

Jetons un coup d'oeil à un exemple simple d'utilisation après.

iex> essayez de faire>> relance "je veux parler au responsable!" ...> rescue ...> e dans RuntimeError -> IO.puts ("Une erreur est survenue:" <> e.message) ...> après ...> IO.puts "Peu importe ce qui se passe, je me présente toujours comme un gros sou."…> End Une erreur est survenue: je veux parler au responsable! Peu importe ce qui se passe, je me présente toujours comme un mauvais sou. :D'accord

Ici vous voyez le après être utilisé pour afficher constamment un message (ou cela peut être n'importe quelle fonction que vous souhaitez ajouter).

Une pratique plus courante dans laquelle vous trouverez ceci est l'utilisation d'un fichier, par exemple ici:

: ok, file = File.open "would_defo_root.jpg" try do # Essayez d'accéder au fichier ici après # Assurez-vous de nettoyer ensuite File.close (file) end

Jette

Aussi bien que élever et essayer / attraper méthodes que nous avons décrites plus tôt, nous avons également les macros lancer et attraper.

En utilisant le jeter la méthode quitte l'exécution avec une valeur spécifique que nous pouvons rechercher dans notre capture bloquer et utiliser ensuite comme ceci:

iex> essayez de faire ...> pour x <- 0… 10 do… > si x == 3, faites: lancer (x)…> IO.puts (x)…> fin…> attraper…> x -> "Capturé: # x"…> fin 0 1 2 "Capturé: 3"

Nous avons donc la possibilité de capture tout ce que nous jeter à l'intérieur du bloc try. Dans ce cas, le conditionnel si x == 3 est le déclencheur de notre faire: jeter (x).

La sortie de l'itération produite à partir de la boucle for nous donne une compréhension claire de ce qui s'est passé par programmation. Progressivement, nous avons avancé et l'exécution a été arrêtée le capture.  

En raison de cette fonctionnalité, il peut parfois être difficile d’imaginer où le jeter capture serait mis en œuvre dans votre application. Un lieu privilégié serait l’utilisation d’une bibliothèque dans laquelle l’API n’a pas les fonctionnalités adéquates pour tous les résultats présentés à l’utilisateur, et une capture suffirait pour naviguer rapidement autour du problème, au lieu d’avoir à développer beaucoup plus au sein de la bibliothèque pour gérer la question et le retour approprié pour elle.

Sorties

Enfin, dans notre arsenal de traitement des erreurs Elixir, nous avons le sortie. La sortie se fait non pas par la boutique de cadeaux, mais explicitement à la mort du processus.. 

Les sorties sont signalées comme suit:

iex> spawn_link fn -> exit ("vous avez fini, fils!") end ** (EXIT from #PID<0.101.0>) "tu as fini mon fils!"

Les signaux de sortie sont déclenchés par des processus pour l'une des trois raisons suivantes:

  1. Une sortie normale: Cela se produit lorsqu'un processus a terminé son travail et son exécution. Ces sorties étant tout à fait normales, il n’ya généralement rien à faire quand elles se produisent, un peu comme un sortie (0) en C. La raison de sortie pour ce genre de sortie est l'atome :Ordinaire.
  2. En raison d'erreurs non gérées: Cela se produit lorsqu'une exception non interceptée est déclenchée dans le processus, sans essayer / attraper / secourir bloquer ou lancer / attraper pour y faire face.
  3. Tué de force: Cela se produit lorsqu'un autre processus envoie un signal de sortie avec la raison :tuer, qui force le processus de réception à se terminer.

Traces de pile

À tout moment de la mise à jour sur jeter, sortie ou les erreurs, appeler le System.stacktrace renverra la dernière occurrence du processus en cours. 

La trace de pile peut être assez souvent formatée, mais cela peut changer dans les nouvelles versions d’Elixir. Pour plus d'informations à ce sujet, reportez-vous à la page de manuel.

Pour renvoyer la trace de la pile pour le processus en cours, vous pouvez utiliser les éléments suivants:

Process.info (self (),: current_stacktrace)

Faire vos propres erreurs

Oui, Elixir peut le faire aussi. Bien sûr, vous avez toujours les types intégrés tels que Erreur d'exécution a ta disposition. Mais ce ne serait pas bien si vous pouviez aller plus loin?

Il est facile de créer votre propre type d'erreur personnalisé en utilisant le défexception macro, qui acceptera commodément le :message option, pour définir un message d'erreur par défaut comme ceci:

defmodule MyError do message de defexception: "votre erreur personnalisée s'est produite" end

Voici comment l'utiliser dans votre code:

iex> essayez de faire> relancez MyError ...> rescue ...> e dans MyError -> e ...> end% MyError message: "votre erreur personnalisée s'est produite"

Conclusion

La gestion des erreurs dans un langage de méta-programmation tel qu'Elixir a de nombreuses implications potentielles sur la façon dont nous concevons nos applications et les rendons suffisamment robustes pour permettre un traitement rigoureux de l'environnement de production.. 

Nous pouvons nous assurer que les utilisateurs finaux disposent toujours d'un indice: un message d'orientation simple et facile à comprendre, qui ne rendra pas leur tâche plus difficile, mais plutôt l'inverse. Les messages d'erreur doivent toujoursêtre écrit en anglais simple et donner beaucoup d'informations. Les codes d'erreur cryptiques et les noms de variables ne sont pas bons pour les utilisateurs moyens, et peuvent même dérouter les développeurs!

À l'avenir, vous pouvez surveiller les exceptions générées dans votre application Elixir et configurer une journalisation spécifique pour certains points problématiques. Vous pourrez ainsi analyser et planifier votre solution ou utiliser une solution prête à l'emploi..

Services tiers

Améliorez la précision de nos travaux de débogage et permettez à la surveillance de la stabilité de vos applications de suivre ces services tiers disponibles pour Elixir:

  • AppSignal peut être très bénéfique pour la phase d’assurance qualité du cycle de développement.. 
  • GitHub repo bugsnex est un excellent projet pour utiliser l'interface API avec Bugsnag afin de détecter les défauts dans votre application Elixir..
  • Surveillez la disponibilité, la mémoire vive du système et les erreurs avec Honeybadger, qui fournit une surveillance des erreurs de production afin que vous n'ayez pas besoin de garder votre application..

Extension du traitement des erreurs

À l'avenir, vous voudrez peut-être étendre davantage les capacités de traitement des erreurs de votre application et rendre votre code plus facile à lire. Pour cela, je vous recommande de consulter ce projet pour une gestion élégante des erreurs sur GitHub..