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 ...
Noen av måtene feilene behandles i Elixir kan ikke være åpenbare ved første øyekast.
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.spawn_link
makro. Dette er en toveis kobling, som betyr at hvis en koblet prosess avsluttes, vil et utgangssignal utløses.: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.spawn_links
, men disse er ensrettede lenker, og vi kan lage dem med Process.monitor
. 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.
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.
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
.
: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.
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
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.
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:
exit (0)
i C. Utgangsårsaken til denne typen utgang er atomet :normal
.prøve / fangst / redning
blokkere eller kaste / fangst
å takle det.:drepe
, som tvinger mottaksprosessen til å avslutte.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)
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"
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.
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:
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.
!