Hvordan håndtere unntak i Elixir

Unntakshåndtering er en god praksis for enhver programvareutviklingsmetode. Enten det er for testbasert utvikling, fleksible sprints eller en hacking-økt med bare en god gammel todo-liste, kan vi alle dra nytte av å sikre at våre baser er dekket med en robust tilnærming til feilhåndtering. 

Det er viktig å sikre at feil blir tatt vare på, samtidig som det er estetisk tiltalende og selvfølgelig ikke blir et stort problem logisk med kryptiske meldinger for sluttbrukeren for å prøve å hente mening fra. Hvis du gjør det, er du sikkert på en fin måte å lage en solid, stabil og klebrig app som brukere liker å jobbe med og vil anbefale sterkt til andre.

Ideelt for oss gir Elixir omfattende unntakshåndtering via flere mekanismer som prøve / fangst, kaster, og : feil, grunn tuppel.

For å vise en feil, bruk heve i ditt interaktive skall for å få en første smak:

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

Vi kan også legge til en type til dette slik:

iex> heiser ArgumentError, melding: "error message here ..." ** (ArgumentError) feilmelding her ... 

Hvordan Feilhåndtering Fungerer i Elixir

Noen av måtene feilene behandles i Elixir kan ikke være åpenbare ved første øyekast.

  • For det første om prosesser-i bruk spawn, Vi kan skape uavhengige prosesser. Det betyr at en feil på en tråd ikke bør påvirke noen annen prosess, med mindre det var noen sammenheng på noen måte. Men som standard vil alt bli stabilt.
  • For å varsle systemet om en feil i en av disse prosessene, kan vi bruke spawn_link makro. Dette er en toveis kobling, som betyr at hvis en koblet prosess avsluttes, vil et utgangssignal utløses.
  • Hvis utgangssignalet er noe annet enn :normal, Vi vet at vi har et problem. Og hvis vi fanger utgangssignalet med Process.flag (: trap_exit, true), utgangssignalet vil bli sendt til prosessens postkasse, der logikken kan plasseres på hvordan du håndterer meldingen, og dermed unngår en hard krasj.
  • Til slutt har vi skjermer som ligner på spawn_links, men disse er ensrettede lenker, og vi kan lage dem med Process.monitor
  • Prosessen som påkaller Process.monitor vil motta feilmeldingene ved feil.

For en prøvefeil, prøv å legge til et tall til et atom, og du vil få følgende:

iex>: foo + 69 ** (ArithmeticError) dårlig argument i aritmetisk uttrykk: erlang. + (: foo, 69)

For å sikre at sluttbrukeren ikke blir feilt, kan vi bruke forsøk, fangst og redningsmetoder fra Elixir.

Prøv / Rescue

Først i vår verktøykasse for unntakshåndtering er prøve / redning, som fanger feil produsert ved å bruke heve så er egentlig best egnet for utviklerfeil, eller eksepsjonelle omstendigheter som inngangsfeil.

prøve / redning er lik i bruk til a prøve / fangst blokkere du har sett i andre programmeringsspråk. La oss se på et eksempel i aksjon:

iex> prøv å gjøre ...> øke "ikke mislyktes!" ...> redning ...> e i RuntimeError -> IO.puts ("Feil:" <> e.message) ...> End Error: mislyktes! : ok

Her bruker vi prøve / redning blokkere og nevnte heve å fange RuntimeError

Dette betyr at ** (RuntimeError) standard utgang av heve vises ikke, og erstattes med en bedre formatert utgang fra IO.puts anrop. 

Som en god praksis må du bruke feilmeldingen for å gi brukeren nyttig utdata på vanlig engelsk, noe som hjelper dem med problemet. Vi ser på det mer i det neste eksemplet.

Flere feil i en prøve / redning

En stor fordel ved Elixir er at du kan fange flere utfall i en av disse prøve / redning blokker. Se på dette eksempelet:

Prøv å velge |> Keyword.fetch! (: source_file) |> File.read! redde e i KeyError -> IO.puts "mangler: source_file-alternativet" e i File.Error -> IO.puts "kan ikke lese kildefilens slutt

Her har vi tatt to feil i redde

  1. Hvis filen ikke er i stand til å lese.
  2. Hvis :kildefil symbolet mangler. 

Som nevnt tidligere kan vi bruke dette til å gjøre feilmeldinger for sluttbrukeren vår. 

Denne kraftige og minimal syntax-tilnærmingen til Elixir gjør at du kan skrive flere sjekker som er svært tilgjengelige for oss, for å sjekke mange mulige feilfeil, på en fin og konsis måte. Dette hjelper oss med å sikre at vi ikke trenger å skrive utarbeidede betingelser som gjør langvarige skript som kan være vanskelig å visualisere fullt og feilsøke riktig under senere utvikling eller for en ny utvikler å bli med.

Som alltid når du arbeider i Elixir, er KISS den beste tilnærmingen til å ta. 

Etter

Det er situasjoner når du vil kreve en bestemt handling utført etter prøve / redningsblokken, uansett om det oppstod en feil. For Java eller PHP utviklere, kan du tenke på prøve / fangst / slutt eller Ruby's Innledning / redning / sikre.

La oss ta en titt på et enkelt eksempel på bruk av etter.

iex> prøv ...> hei "Jeg vil snakke med sjefen!" ...> redning ...> e i RuntimeError -> IO.puts ("En feil oppsto:" <> e.message) ...> etter ...> IO.puts "Uansett hva som skjer, blir jeg alltid som en dårlig penny." ...> End En feil oppstod: Jeg vil snakke med sjefen! Uansett hva som skjer, blir jeg alltid som en dårlig krone. : ok

Her ser du etter blir vant til å hele tiden lage et meldingsskjermbilde (eller dette kan være en hvilken som helst funksjon du ønsket å kaste inn der).

En mer vanlig praksis du finner finner du her hvor en fil er tilgjengelig, for eksempel her:

: ok, file = File.open "would_defo_root.jpg" prøv å gjøre # Prøv å få tilgang til filen her etter # Kontroller at vi rydder opp etterpå File.close (fil) avslutter

kaster

Samt heve og prøve / fangst metoder vi har skissert tidligere, har vi også kaste og fange makroer.

Bruker kaste Metoden avslutter kjøring med en bestemt verdi vi kan se etter i vår å fange blokkere og bruk videre slik:

iex> prøv å gjøre ...> for x <- 0… 10 do… > hvis x == 3, gjør: kaste (x) ...> IO.puts (x) ...> slutt ...> fange ...> x -> "Fanget: # x" ...> ende 0 1 2 "Fanget: 3"

Så her har vi evnen til å fange noe vi kaste inne i forsøksblokken. I dette tilfellet er det betinget hvis x == 3 er utløseren for vår gjøre: kaste (x).

Utgangen fra iterasjonen produsert fra forløpet gir oss en klar forståelse av hva som har skjedd programmatisk. I økende grad har vi gått fremover, og utførelsen har blitt stanset på å fange.  

På grunn av denne funksjonaliteten kan det noen ganger være vanskelig å se hvor kaste å fange ville bli implementert i appen din. Et primært sted vil være i bruk av et bibliotek der API-en ikke har tilstrekkelig funksjonalitet for alle utfallene som presenteres for brukeren, og en fangst ville nok til å raskt navigere rundt problemet, i stedet for å måtte utvikle mye mer i biblioteket for å håndtere problemet og returnere riktig for det.

avslutter

Endelig har vi i vår Elixir feilhåndteringsarsenal vi exit. Exiting er gjort ikke gjennom gavebutikken, men eksplisitt når en prosess dør. 

Utganger er signalisert slik:

iex> spawn_link fn -> exit ("du er ferdig sønn!") slutten ** (EXIT fra #PID<0.101.0>) "du er ferdig sønn!"

Utgangssignaler utløses av prosesser av en av følgende tre grunner:

  1. En vanlig utgang: Dette skjer når en prosess har fullført jobben og avslutter kjøringen. Siden disse utgangene er helt normale, må det vanligvis ikke gjøres noe når de skjer, mye som en exit (0) i C. Utgangsårsaken til denne typen utgang er atomet :normal.
  2. På grunn av uhåndterte feil: Dette skjer når et uncaught unntak heves inne i prosessen, uten prøve / fangst / redning blokkere eller kaste / fangst å takle det.
  3. Kraftig drept: Dette skjer når en annen prosess sender et utgangssignal med grunnen :drepe, som tvinger mottaksprosessen til å avslutte.

Stack Traces

Ved en hvilken som helst oppdateringstidspunkt på kaste, exit eller feil, ringer til System.stacktrace vil returnere den siste forekomsten i den nåværende prosessen. 

Stakkesporet kan formateres ganske mye, men dette kan endres i nyere versjoner av Elixir. For mer informasjon om dette, vennligst se den manuelle siden.

For å returnere stakksporingen for gjeldende prosess, kan du bruke følgende:

Process.info (self (),: current_stacktrace)

Gjør dine egne feil

Ja, Elixir kan også gjøre det. Selvfølgelig har du alltid de innebygde typene som RuntimeError til din disposisjon. Men ville det ikke vært fint hvis du kunne gå et skritt videre?

Å lage din egen tilpassede feiltype er enkelt ved å bruke defexception makro, som lett vil akseptere :budskap alternativ, for å angi en standard feilmelding som slik:

defmodule MyError gjør defexception melding: "din egendefinerte feil har oppstått" slutt

Slik bruker du den i koden din:

iex> prøv å gjøre ...> heve MyError ...> redning ...> e i MyError -> e ...> slutt% MyError message: "din tilpassede feil har oppstått"

Konklusjon

Feilhåndtering i et meta-programmeringsspråk som Elixir har en hel masse potensielle konsekvenser for hvordan vi designer våre applikasjoner og gjør dem robuste nok til den strenge bashing av produksjonsmiljøet. 

Vi kan sikre at sluttbrukeren alltid er igjen med en anelse - en enkel og lettforståelig veiledningsmelding, som ikke vil gjøre oppgaven vanskelig, men heller den omvendte. Feilmeldinger må alltidSkrive på vanlig engelsk og gi rikelig med informasjon. Kryptiske feilkoder og variable navn er ikke gode for de gjennomsnittlige brukerne, og kan til og med forvirre utviklere!

Fremover kan du overvåke unntakene som er oppvokst i Elixir-applikasjonen og sette opp spesifikk logging for bestemte problemer, slik at du kan analysere og planlegge reparasjonen din, eller du kan se på å bruke en hylleløsning.

Tredjeparts tjenester

Forbedre nøyaktigheten av vårt feilsøkingsarbeid, og gjør det mulig å overvåke appens stabilitet med disse tredjepartstjenestene som er tilgjengelige for Elixir:

  • AppSignal kan være svært gunstig for kvalitetssikringsfasen av utviklingssyklusen. 
  • GitHub repo bugsnex er et flott prosjekt for å bruke API-grensesnittet med Bugsnag for å videre oppdage feil i din Elixir app.
  • Overvåk oppetid, systemminne og feil med Honeybadger, som gir produksjonsfeilovervåkning, slik at du ikke trenger å babysit din app.

Utvidelse av feilhåndtering videre

Videre kan du ønske å ytterligere utvide feilsøkingsegenskapene til appen din og gjøre koden enklere å lese. For dette anbefaler jeg at du sjekker ut dette prosjektet for elegant feilsøking på GitHub.