Filtr chyb a výjimek pro Debugger
- mancze
- Člen | 58
Zdravím,
mám následující situaci:
V produkčním prostředí čas od času vyskočí nějaká nezajímavá
výjimka/chyba (například ojedinělé výpadky databáze). Přitom mám
nastavené zasílání reportů z Debuggeru. Ve stávající situaci mi tedy
přijde čas od času nezajímavý e-mail, který musím smazat a také se
přihlásit na FTP a promazat logy, aby mi mohl přijít e-mail další (a
třeba zajímavější).
Moje otázka zní: Jde nějak pěkně napsat filtr pro Debugger, který mi tyto nezajímavé zprávy bude ignorovat?
Nejspíš bych mohl použít vlastní proxy error handler a exception handler, který mi to bude filtrovat a zároveň napsat vlastní proxy Logger (protože bych to třeba rád v logu měl, ale ignoroval odeslání e-mailu a negeneroval bluescreen). Ale přijde mi to jako příliš nepěkné řešení, takové moc roztroušené. Proto se raději ptám, zda neexistuje lepší cesta. Případně zda to nezvážit jako předmět budoucího rozšíření.
Editoval mancze (15. 1. 2013 20:30)
- Filip Procházka
- Moderator | 4668
Ne, z jednoduchého důvodu: takhle musíš jít podívat se na hosting a zkontrolovat, co se pokazilo. Je to v tvém zájmu.
Stejně jako je v tvém zájmu, aby tvoje aplikace negenerovala notices a warningy ;)
- frosty22
- Člen | 373
Přesně tak souhlasím s Filipem a prakticky si ani nedokáži představit, jak bys chtěl hodnotit vážnost chyby. Notice a warningy by být neměli resp. ty se odstraní již při vývoji a fataly a nezachycené vyjímky již sami o sobě jsou kritické. Ostatně i notice může být velice kritický z pohledu business logiky. Například takové:
<?php
$price = 1000; // Cena k platbě
$payment = new Payment();
$payment->setPrice($priceo); // Taková sranda, překlep - chyba úrovně notice a hned se nemusí platit
$payment->send();
?>
Tímto chci jen podotknout, že nelze definovat, co je chyba, která tě nemusí tolik pálit jako chyba, která ano.
- mancze
- Člen | 58
Nesouhlasím s vámi. Asi není z mého dotazu zřejmé, že daným filtrem myslím vlastní implementaci nějaké třídy (nebo callable, to je fuk), kterou bych do Debuggeru snadno zaregistroval.
Samozřejmě že logování je užitečné a chyby bych si měl projít. Zmínil jsem ale krátkodobé výpadky databáze. Kvůli tomu mi přijde error report. Ale mne nezajímá, že 1 minutu databáze neodpovídá. Já s tím nic nezmůžu, to už tak hold při na daném hostingu je.
A proto chci napsat filtr, který mi související výjimky odfiltruje a případně s nimi sám naloží (například bude sledovat jejich četnost a pokud jich bude 100 během hodiny, tak je nechá projít).
Já mám funkční řešení napsané, ale nelíbí se mi, protože není systémové (a navíc závislé na stávající implementaci Debuggeru). Po registraci Debuggeru přebiji exception handler a nastavím vlastní, který mi už výjimky filtruje. Pro mne to takto zatím stačí, ale třeba tohle řešení znamená, že se mi vyfiltrované chyby nezalogují.
<?php
Debugger::enable();
// filter out uninteresting exception for logging
$fitleringExceptionHandler = function(\Exception $ex) {
$ignored = false;
if (Debugger::$productionMode) {
// my filtering logic
}
$logDir = Debugger::$logDirectory;
if ($ignored) {
// exception won't be logged when log dir is unset
Debugger::$logDirectory = false;
}
Debugger::_exceptionHandler($ex);
if ($ignored) {
// restore log dir
Debugger::$logDirectory = $logDir;
}
};
set_exception_handler($fitleringExceptionHandler);
?>
Editoval mancze (17. 1. 2013 14:08)
- Filip Procházka
- Moderator | 4668
Můžeš mi to prosím vysvětlit? Já to totiž nechápu. Protože tvůj hosting je k ničemu, tak budeš hackovat nette, aby se ti nelogovaly chyby, že se nejde připojit k databázi?
Už tohle je samo o sobě neskutečná hovadina.
Pokud ti jde jen o tohle, proč raději chybu neotchytáváš?
try {
$this->db->connect();
} catch (\Exception $e) {
die("Sorry, výpadek");
}
- LeonardoCA
- Člen | 296
Já to vidím ještě trochu jinak. Myslím si, že mancze má dobrou myšlenku, i když v jeho případě bych taky jako první měnil hosting.
Proč logovat a nebo posílat na email každý výskyt chyby, o které vím, že mohou občas nastat. V tom případě bych bral chybu jako jeden z validních stavů aplikace. Stále se hodí logovat četnost i časy výskytu, ale nepotřebuji info na email, protože vím, že k ní občas z neovlivnitelných důvodů dojde, mám ji ošetřenou a informuji uživatele, ať to zkusí o chvíli později znovu (a nebo jinak – viz níže)
Myslím si, že vlastní ExceptionHandler může být v tomto případě
také dobré řešení.
Logovat se stále dá „ručně“, zavoláním Debugger::log().
Ještě větší využití vidím v cron scriptech než ve skriptech generující výstupy na web, kdy vím že určité chyby mohu ignorovat, protože třeba o pár sekund nebo minut později se předchozí operace provede znovu a problém se „sám“ napraví.
Příklad:
Ještě jsem nedořešil podobný problém s tím, že externí API
občas/nepravidelně 1–2× denně pár minut neodpovídá nebo vrací
500 response. Tyto chyby nechci logovat, ale chci to vyřešit tak, že cron
job v tom případě bude opakovat poslední request až do doby kdy mu API
odpoví. Ale jen v případě konkrétní chyby, kdy externí služba
neodpovídá z důvodu zahlcení, údržby, apod.
Ostatní chyby se musí zpracovat standartně a normálně pokračovat s dalšími requesty a neopakovat neúspěšný request v případě, kdy nedostane správnou odpověď ze všech ostatních možných důvodů jako: nevalidní parametry, pokus o přístup k datům na které nemá oprávnění apod.
V mém případě by bylo hodně dobré neposílat informaci
o sporadických výpadcích, ale jen o dlouhodobých.
Například vím že výpadek trvající do 2–5 minut je „běžný“ a mohu
ho ignorovat.
Ale pokud výpadek trvá déle, může být důvod:
- problém na straně poskytovatel služby, na který bych ho měl upozornit
- změna API na kterou mne poskytovatel neupozornil
- vypršení platnosti přístupu na placené API
- update aplikace nebo změna konfigurace serveru a zanesení chyby, způsobující výpadky s větší četností než dříve
- nárust vytížení serveru a nutnost optimalizace nebo změny architektůry pro lepší škálovatelnost
- atd., atd.
a budu chtít info na email…
Podobně i databáze může být na pár chvil zahlcena, třeba proto že běží nějaké náročné údržbové skripty. Ale zas platí to, že logovat nebudu jen v případě, že požadovanou operaci budu dříve nebo později opakovat.
Jinak mám v kategorii todo „nice to have“ taky něco jako filtr na logy, ať je nemusím procházet ručně, ale řešil bych to asi vlastním loggerem. Taky se chci ještě inspirovat jak logování implementovali autoři composeru a jestli by se nedalo zakomponovat.
Ale to jsou všechno hodně specifické věci, byly by fajn jako rozšíření a přímo v nette být nemusí.
Editoval LeonardoCA (18. 1. 2013 0:19)
- Filip Procházka
- Moderator | 4668
Od čeho je try {} catch () {}
. Naučte se prosím pořádně
odchytávat chyby.
Nastane chyba → zalogovat. Pokud máš něco, co tě neštve, tak chybu odchytíš a ignoruješ (a třeba operaci zkusíš znova). Pokud je to něco, co chceš monitorovat, tak zase loguješ, a je jedno jakou formou.
To co tady navrhujete není řešení. To je jenom „budu doufat, že se to neposere a když se to posere tak o tom nechci vědět a budu dělat, že se to nestalo“.
Na druhou stranu, není vůbec špatný nápad posílat všechny chyby na nějaký centrální server. Například takto: http://doc.nellafw.org/cs/diagnostics
- LeonardoCA
- Člen | 296
Neberu ti tvůj názor a díky za odkaz, bude se mi z toho ještě hodit.
Mě třeba štve, když se někde tvoří zbytečné data a proto je chci filtrovat už při ukládání.
- Panda
- Člen | 569
Filip má pravdu. Koncepce výjimek a jejich zpracování je jedním z důležitých mechanismů, které jazyky vyšší úrovně nabízejí. Správná práce s výjimkami je jedním z aspektů čistého kódu.
Pokud nějakou chybu nechceš logovat, tak jí odchyť a zpracuj. Tím, že jí odchytíš v místě, kde k ní může dojít, dáváš najevo, že o ní víš a že je očekávaná. Pokud bys jí zpracovával o úroveň výš, tedy v debuggeru, tím, že ji zahodíš, si zaděláváš na velké problémy do budoucna.
Výjimka a její „zpracování“ bude dost nelogicky odděleno od sebe a jak pak poznáš, že je to skutečně ta výjimka, co tě zajímá? Podle typu? Co když k výjimce stejného typu dojde ještě někde jinde v kódu? Budeš analyzovat stack trace? Co když pak v budoucnu při refactoringu třídy přeorganizuješ a na místě původní výjimky se bude vyhazovat podobná výjimka, která je však už mnohem vážnější? A co když filtr omylem zachytí zachytí zásadní výjimku, která narušuje konzistenci dat? Jak se o ní pak dozvíš?
Oddělit výjimku a její zpracování do dvou různých úrovní, které jsou na sobě nezávislé, je prostě hloupost. Laděnka má sloužit jako poslední záchrana, aby se programátor dozvěděl o chybách, které neošetřil, ne jako nástroj, který mu je pomůže zahodit.
A nezapomínejte – i prázdný catch
může být korektní
zpracování výjimky:
try {
// some error-prone operations ...
} catch (SomeException $e) {
// this exception is OK, it's their fault, not mine
}
Editoval Panda (18. 1. 2013 9:00)
- mancze
- Člen | 58
Díky za vaše názory. Myslím, že LeonardoCA vyjádřil velice přesně podstatu problému. A má pravdu, CRON je jedním s případů, kde s tím bojuji. Já jsem nastínil případ výpadků databáze. Stává se mi to na dvou projektech. Jednou jde o lokální databázi a podruhé to je vzdálená databáze, nad kterou nemám kontrolu. Argumenty jako „změň hosting“ jsou sice hezké, ale v praxi to často nejde a tohle jeden z těch případů.
Filip Procházka napsal(a):
Od čeho je
try {} catch () {}
. Naučte se prosím „pořádně odchytávat chyby“…
Nenech se mýlit, try-catch
používám a chyby mám
ošetřené. Nevyřeší mi to ale problém s databází. Ta má lazy
připojení, takže try-catch
kolem connect()
to dát
nemůžu. I kdyby připojení nebylo líné, tak problém stejně zůstane –
spojení mi může spadnout kdykoliv, nejen během připojování. A obalovat
try-catch
každý blok, kde se pracuje s databází – ne,
děkuji, bohatě si vystačím s error 500, neřídím jadernou
elektrárnu.
Panda napsal(a):
Výjimka a její „zpracování“ bude dost nelogicky odděleno od sebe a jak pak poznáš, že je to skutečně ta výjimka, co tě zajímá? Podle typu? Co když k výjimce stejného typu dojde ještě někde jinde v kódu? Budeš analyzovat stack trace? Co když pak v budoucnu při refactoringu třídy přeorganizuješ a na místě původní výjimky se bude vyhazovat podobná výjimka, která je však už mnohem vážnější?
Vlastně používám textovou zprávu výjimky pro rozlišení. O systematičnosti nepotřebuji nic slyšet, už tak celý mechanismus je jen berlička kvůli problémům neideálního prostředí reálného světa (to nepopírám). Pokud změní se text výjimky, ano, začnou mi zase chodit e-maily. No tak filtr upravím. Ale objektivně – to se téměř jistě nestane.
Panda napsal(a):
A co když filtr omylem zachytí zachytí zásadní výjimku, která narušuje konzistenci dat? Jak se o ní pak dozvíš?
To je pak problém špatně napsaného detekčního algoritmu filtru, ne jeho mechanismu.
Co mne ale po přečtení diskuze nenapadlo (a mělo napadnout), je napsat si
try-catch
kolem $application->run()
. To by mi pak
vyřešilo i odchytávání výjimek výpadku databáze. Pak bych nemusel
obcházet exception handler Debuggeru. Poradí si to ale s případem, kdy má
$application
nastaveno catchExceptions = true
?
Editoval mancze (18. 1. 2013 11:36)
- Filip Procházka
- Moderator | 4668
Velice dobrý nápad. Ale jde to ještě lépe. Application, vždy, když se až do něj dostane výjimka, tak nad ní zavolá callback. Můžeš si jej tedy ošetřit zde:
$application->onError[] = function (\Exception $e) {
if ($e instanceof \DibiException) {
Debugger::log($e, 'db'); // samotné volání ::log() emaily neposílá
die("Sorry, aplikace má problémy, zkuste to za chvíli.");
}
// ostatní chyby
};
Co se týče ideálnosti světa: ještě nikdy jsem reálně nezažil, že by mi jen tak vypadla umřela databáze. Ani na hostingu, ani na VPS. Vybral jsi si špatný hosting?
A netvrď mi prosím, že nejde změnit. Vždycky to jde změnit, jenom se musí chtít a musí se to umět klientovi vysvětlit.
- brablc
- Člen | 6
Samotné odchytávání výjimek je určitě základ, ale to co chce mancze a výborně popsal LeonardoCA je naprosto logické a při správě většího počtu webů nutností. Výjimečné stavy se prostě stávají. Je nutné empiricky určit, kdy je lze zanedbat a kdy je nutné je reportovat a eskalovat (mail, SMS).
Nejsem si úplně jistý, jestli je správné takovýto monitoring dávat do aplikace. Myslím, že by bylo lepší poohlédnout se po nějakém řešení, které near real time analyzuje access log http serveru. Tím je možné odhalit i třeba chyby na statickém obsahu.
- David Grudl
- Nette Core | 8228
Trošku moc hysterie okolo korektní otázky, ne?
mancze napsal(a):
Moje otázka zní: Jde nějak pěkně napsat filtr pro Debugger, který mi tyto nezajímavé zprávy bude ignorovat?
Pokud používám Nette\Application, kde výjimky zpracovává nějaký ErrorPresenter, třídím výjimky v něm. Výpadky databáze tak loguju bez notifikace apod.
Nejspíš bych mohl použít vlastní proxy error handler a exception handler, který mi to bude filtrovat a zároveň napsat vlastní proxy Logger (protože bych to třeba rád v logu měl, ale ignoroval odeslání e-mailu a negeneroval bluescreen). Ale přijde mi to jako příliš nepěkné řešení, takové moc roztroušené. Proto se raději ptám, zda neexistuje lepší cesta. Případně zda to nezvážit jako předmět budoucího rozšíření.
Zmíněný ErrorPresenter fakticky takovým řešením je. Obdobou by bylo obalit celý kód do try & catch a pak se rozhodnout, jak kterou výjimku zpracovat. Samotný exception handler třídy Debugger uživatelské třídění výjimek nenabízí, jelikož představuje tu poslední instanci, ke které se výjimka může dostat.