it-swarm-eu.dev

Jak napsat dobrou výjimku

V současné době provádím kontrolu kódu a jednou z věcí, které si všimnu, je počet výjimek, u nichž se zdá, že zpráva o výjimce zopakuje opakování kde došlo k výjimce. např.

throw new Exception("BulletListControl: CreateChildControls failed.");

Všechny tři položky v této zprávě mohu vyjít ze zbytku výjimky. Znám stopu a metodu z trasování zásobníku a vím, že selhala (protože mám výjimku).

Přivedlo mě to k přemýšlení o tom, jakou zprávu jsem vložil do výjimečných zpráv. Nejprve vytvořím třídu výjimek, pokud již neexistuje, z obecného důvodu (např. PropertyNotFoundException - proč), a poté, když ji hodím, zpráva ukazuje, co se pokazilo (např. „Nelze najít vlastnost 'IDontExist' na Node 1234" - what). Kde je v StackTrace. The - Kdy může skončit v protokolu (pokud existuje). jak je pro vývojáře, aby vypracoval (a opravil)

Máte nějaké další tipy pro házení výjimek? Konkrétně s ohledem na vytváření nových typů a zprávu o výjimce.

102
Colin Mackay

Zaměřím svou odpověď více na to, co přichází po výjimce: k čemu je dobrý a jak by se měl software chovat, co by měli uživatelé dělat s výjimkou? Skvělou technikou, se kterou jsem se setkal na začátku své kariéry, bylo vždy hlásit problémy a chyby ve 3 částech: kontext, problém a řešení. Používání této dicipliny značně mění zpracování chyb a činí software pro operátory výrazně lepší.

Zde je několik příkladů.

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

V tomto případě operátor přesně ví, co má dělat a na který soubor se musí vztahovat. Také vědí, že změny sdružování připojení nenastaly a měly by být opakovány.

Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell '[email protected]' about this problem.

Píšu systémy na straně serveru a moji operátoři jsou obecně technicky důvtipní podpory první linie. Zprávy bych psal jinak pro stolní software, který má jiné publikum, ale obsahuje stejné informace.

Pokud někdo použije tuto techniku, stane se několik úžasných věcí. Vývojář softwaru má často nejlepší předpoklady, aby věděl, jak řešit problémy ve svém vlastním kódu, takže řešení kódování tímto způsobem při psaní kódu je velkým přínosem pro koncové uživatele, kteří jsou v nevýhodě při hledání řešení, protože jim často chybí informace o co přesně software dělal. Každý, kdo někdy četl chybovou zprávu Oracle, bude vědět, co tím myslím.

Druhá úžasná věc, která vám přijde na mysl, je, když zjistíte, že se pokoušíte popsat řešení ve vaší výjimce a píšete „Check X a A pak B else C“. To je velmi jasné a zřejmé znamení, že vaše výjimka je kontrolována na nesprávném místě. Vy programátor máte schopnost porovnávat věci v kódu, takže "if" příkazy by měly být spouštěny v kódu, proč zapojit uživatele do něčeho, co lze automatizovat? Šance jsou z hlubšího kódu a někdo udělal línou věc a hodil IOException z libovolného počtu metod a zachytil potenciální chyby od všech z nich v bloku volajícího kódu, který nedokáže adekvátně popsat, co se pokazilo, co specific kontext je a jak jej opravit. To vás povzbuzuje k tomu, abyste psali chyby jemnějších zrn, chytili je a zacházeli s nimi na správném místě v kódu, abyste mohli správně stanovit kroky, které by měl operátor provést.

V jedné společnosti jsme měli špičkové operátory, kteří se seznámili se softwarem opravdu dobře a vedli si vlastní „run book“, která rozšířila naše hlášení chyb a navrhovaná řešení. Aby to bylo možné rozpoznat, software začal zahrnovat odkazy wiki na runbook výjimečně tak, aby bylo k dispozici základní vysvětlení, jakož i odkazy na pokročilejší diskusi a pozorování provozovatelů v průběhu času.

Pokud jste dicipline vyzkoušeli tuto techniku, je mnohem jasnější, jak byste měli své výjimky v kódu pojmenovat při vytváření své vlastní. NonRecoverableConfigurationReadFailedException se stává trochu zkratkou pro to, co se chystáte podrobněji popsat operátorovi. Líbí se mi být podrobný a myslím, že to bude snazší pro další vývojáře, kteří se dotknou mého kódu, interpretovat.

72
Sir Wobin

V tato poslední otázka jsem uvedl, že výjimky by neměly obsahovat zprávu vůbec. Podle mého názoru je skutečnost, že ano, obrovská mylná představa. Navrhuji to

"Zpráva" výjimky je (plně kvalifikovaný) název třídy výjimky.

Výjimka by měla obsahovat ve svých vlastních členských proměnných co nejvíce podrobností o tom, co se přesně stalo; například parametr IndexOutOfRangeException by měl obsahovat hodnotu indexu, která byla shledána neplatnou, stejně jako horní a dolní hodnoty platné v okamžiku vyvolání výjimky. Tímto způsobem můžete pomocí reflexe automaticky sestavit zprávu, která zní takto: IndexOutOfRangeException: index = -1; min=0; max=5 a to spolu s trasováním zásobníku by měly být všechny objektivní informace, které potřebujete k vyřešení problému. Formátování do hezké zprávy jako „index -1 nebyl mezi 0 a 5“ nepřidává žádnou hodnotu.

V konkrétním příkladu by třída NodePropertyNotFoundException obsahovala název vlastnosti, která nebyla nalezena, a odkaz na uzel, který tuto vlastnost neobsahoval. To je důležité: nemělo by obsahovat název uzlu; měl by obsahovat odkaz na skutečný uzel. Ve vašem konkrétním případě to nemusí být nutné, ale je to věc principu a upřednostňovaného způsobu myšlení: při vytváření výjimky je hlavním problémem to, že musí být použitelné kódem, který by jej mohl chytit. Použitelnost lidmi je důležitým, ale pouze druhotným problémem.

Postará se o velmi frustrující situaci, kterou jste mohli být svědky v určitém okamžiku své kariéry, kdy jste možná chytili výjimku obsahující důležité informace o tom, co se stalo v textu zprávy, ale ne v jejích proměnných členů, takže musel udělat řetězovou analýzu textu, aby zjistil, co se stalo, doufaje, že text zprávy zůstane stejný v budoucích verzích podkladové vrstvy, a modlil se, aby text zprávy nebyl v nějakém cizím jazyce, když je váš program běžet v jiných zemích.

Samozřejmě, protože název třídy výjimky je zprávou o výjimce (a členské proměnné výjimky jsou specifické podrobnosti), znamená to, že k předávání všech různých zpráv potřebujete spoustu a mnoho různých výjimek, a To je v pořádku.

Nyní, když píšeme kód, narazíme na chybnou situaci, pro kterou chceme jen rychle kódovat příkaz throw a pokračovat v psaní kódu místo toho, abychom museli přerušovat to, co děláme, abychom vytvořili nový třídy výjimek, abychom ji mohli hodit právě tam. V těchto případech mám třídu GenericException, která ve skutečnosti přijímá řetězcovou zprávu jako parametr doby výstavby, ale konstruktor této třídy výjimek je ozdoben velkým velkým obrovským jasně fialovým FIXME XXX TODO komentář uvádějící, že každá jednotlivá instance této třídy musí být nahrazena instancí nějaké specializovanější třídy výjimek před tím, než se uvolní softwarový systém, nejlépe před tím, než bude kód potvrzen.

23
Mike Nakis

Obecně platí, že výjimka by měla vývojářům pomoci určit příčin poskytnutím užitečných informací (očekávané hodnoty, skutečná hodnota, možné příčiny/řešení atd.).

Nové typy výjimek by měly být vytvořeny, pokud žádný z vestavěných typů nedává smysl. Specifický typ umožňuje ostatním vývojářům zachytit konkrétní výjimku a zpracovat ji. Pokud by vývojář věděl, jak s vaší výjimkou zacházet, ale typem je Exception, nebude jej schopen správně zpracovat.

13
mbillard

V .NET nikdy throw new Exception("...") (jako se zobrazil autor otázky). Výjimka je kořenovým typem Výjimky a neměla by být nikdy přímo vyvolána. Místo toho hodte jeden z odvozených typů výjimek .NET nebo vytvořte vlastní vlastní výjimku, která je odvozena od výjimky (nebo jiného typu výjimky).

Proč nevyhodit výjimku? Protože vyvolání výjimky neudává nic, co by popisovalo vaši výjimku, a nutí váš volací kód, aby psal kód jako catch(Exception ex) { ... }, což obecně není dobrá věc! :-).

4
bytedev

Věci, které chcete „přidat“ k výjimce, jsou ty prvky dat, které nejsou vlastní výjimce ani trasování zásobníku. Zajímavou otázkou je, zda jsou součástí „zprávy“ nebo je třeba je při přihlášení připojit.

Jak jste již poznamenali, výjimka vám prozradí, co se stane, stacktrace vám pravděpodobně řekne, kde, ale „proč“ může být více zapojeno (mělo by být, dalo by se doufat), než jen jít na peer na linii nebo dva a říkat “ doh! Samozřejmě ". To platí ještě více při protokolování chyb ve výrobním kódu - příliš často mě kousla špatná data, která se dostala do živého systému, který v našich testovacích systémech neexistuje. Něco tak jednoduchého, jako vědět, co je ID záznamu v databázi, která způsobuje (nebo přispívá) k chybě, může ušetřit značné množství času.

Takže ... Buď uveden, nebo, pro .NET, přidán do sběru dat zaznamenaných výjimek (c.f. @Plip!):

  • Parametry (to může být trochu zajímavé - do sběru dat nelze přidat, pokud nebude serializovat a někdy může být jeden parametr překvapivě složitý)
  • Další data vrácená ADO.NET nebo Linq do SQL nebo podobným (to může být také trochu zajímavé!).
  • Cokoliv jiného nemusí být zřejmé.

Některé věci, samozřejmě, nebudete vědět, co potřebujete, dokud je nemáte ve své původní zprávě o chybě/protokolu. Některé věci, které si neuvědomujete, můžete získat, dokud nenajdete, že je potřebujete.

2
Murph

Jaké jsou výjimky pro?

(1) Řeknete uživateli, že se něco pokazilo?

To by měla být poslední možnost, protože váš kód by se měl přimlouvat a ukazovat jim něco „hezčího“ než výjimku.

„Chybová“ zpráva by měla jasně a stručně uvádět co pokazila se a co, pokud může něco udělat, může uživatel udělat obnovit z chybového stavu.

např. "Prosím, nestiskněte toto tlačítko znovu"

(2) Řekněte vývojáři, když se pokazil?

Toto je věc, kterou se přihlásíte do souboru pro následnou analýzu.
Stack Trace řekne vývojáři kde zlomil se kód; zpráva by měla opět znamenat co pokazilo se.

(3) Říká obsluha výjimky (tj. Kód), že se něco pokazilo?

Typ Výjimky rozhodne, který obsluha výjimky se na ni podívá, a vlastnosti definované v objektu Výjimka mu umožní manipulaci s ním.

Zpráva Výjimky je zcela irelevantní.

0
Phill W.