Hi! I have no real experience using exceptions in my code. Neither I have not seen good guide how to use exceptions in software developent. I have lot of small pieces about it, but no big picture. So I have simple question: how and where to catch errors in Dancer app? I looked to Dancer::Error and it seems good, but still I don't get, where is the best place to catch and handle errors? Is it good idea to have one simple dispatcher (like in bin/app.pl), so every exception is falling to the base script and handled there? Or is better to catch them in place all over the code? Or? Is there some "best practice"? -- Wbr, Kõike hääd, Gunnar
You'll actually find a fair amount of disagreement over how to handle exceptions. In fact, some argue to never handle them and instead let them bubble up to the top on the theory that they'll show up in your error logs and you'll have no choice but to deal with them. The alternative is having exceptions get swallowed, hiding real errors, and having your app do strange things. Here's a classic anti-pattern from Java from a dev who's in a hurry: try { CopyFiles(); MakeRegistryEntries(); } catch (CException & e) { // I'll come back to this later } Because exceptions must often be trapped at compile-time in Java, you'll often find bad java code with empty catch blocks "swallowing" exceptions on the theory that they have a lot of work to do and will come back to it later. Later, of course, is always later. The app fails mysteriously when other devs are trying to why the copied files are intermittently not copied. In Perl, we don't have "checked exceptions" like we do in Java, but here's how it manifests: try { copy_files(); make_registry_entries(); } catch { my $error = $_; $self->log->error($error); }; Well, great. We've logged the damned error, but the software didn't see it and many people don't look at the logs. By never trapping errors, they become big IN YOUR FACE errors and are pretty hard to ignore. Moving away from that point of view, there are fundamentally two types of exceptions: those you can recover from and those you cannot. For many Web-based applications, failure to connect to the database is an unrecoverable error. Don't trap it: let it bubble up and make sure your user gets a really pretty 500 page (not the development 500 page with a stack trace. Major security hole!) For recoverable errors (particularly low-value ones), such as failing to fetch a funny quote from the fortune command, I would trap it at the source, log the error, and make sure my app is coded in such a way that it can handle the lack of "fortune" output (perhaps by providing a default "fortune"). Best,Ovid-- IT consulting, training, international recruiting http://www.allaroundtheworld.fr/. Buy my book! - http://bit.ly/beginning_perl Live and work overseas - http://www.overseas-exile.com/ On Thursday, 20 November 2014, 12:29, WK <wanradt@gmail.com> wrote: Hi! I have no real experience using exceptions in my code. Neither I have not seen good guide how to use exceptions in software developent. I have lot of small pieces about it, but no big picture. So I have simple question: how and where to catch errors in Dancer app? I looked to Dancer::Error and it seems good, but still I don't get, where is the best place to catch and handle errors? Is it good idea to have one simple dispatcher (like in bin/app.pl), so every exception is falling to the base script and handled there? Or is better to catch them in place all over the code? Or? Is there some "best practice"? -- Wbr,Kõike hääd, Gunnar _______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
On Thu, 2014-11-20 at 13:29 +0200, WK wrote:
So I have simple question: how and where to catch errors in Dancer app?
As Ovid says, there are many ways to approach the problem. This is mine. I use Log::Report, which has the advantage of doing translations and logging, as well as handling exceptions. For errors, it provides various levels ("error", "warn", "info" etc). Log::Report provides one or more dispatchers, which can be configured to filter on the severity of the error message. A dispatcher can be a callback, so I configure multiple dispatchers for different types of messages. If it's a user error then that will be displayed to the web page, and depending on the severity I'll either allow the route to continue or stop it there. Otherwise I'll log it to the system, which I plan to do with errbit in the future. Whenever I call a function in my controller, I wrap it in my own eval equivalent: process(sub {...}) This function does the work, and if it's fatal (including for a user error) it stops the route and displays the error. sub process { my $coderef = shift; try {&$coderef}; $@->reportFatal(to => 'error_handler', is_fatal => 0); $@ ? 0 : 1; # Return true on success } In terms of exceptions that I wasn't expecting (e.g. unable to connect to the database), I catch all of these using the init_error hook: hook init_error => sub { my $error = shift; panic $error->{exception}; }; This works, but it's a bit messy, so I'd like to find a better way of doing it. That's it in short. I hope to write it all up at some point and/or create a plugin, but feel free to ask questions in the meantime. Andy
Here's one module we're going to use in the future for all exceptions in Dancer2: Throwable::Error (and its role: Throwable). On Thu, Nov 20, 2014 at 7:16 PM, Andrew Beverley <andy@andybev.com> wrote:
On Thu, 2014-11-20 at 13:29 +0200, WK wrote:
So I have simple question: how and where to catch errors in Dancer app?
As Ovid says, there are many ways to approach the problem. This is mine.
I use Log::Report, which has the advantage of doing translations and logging, as well as handling exceptions. For errors, it provides various levels ("error", "warn", "info" etc).
Log::Report provides one or more dispatchers, which can be configured to filter on the severity of the error message. A dispatcher can be a callback, so I configure multiple dispatchers for different types of messages.
If it's a user error then that will be displayed to the web page, and depending on the severity I'll either allow the route to continue or stop it there. Otherwise I'll log it to the system, which I plan to do with errbit in the future.
Whenever I call a function in my controller, I wrap it in my own eval equivalent:
process(sub {...})
This function does the work, and if it's fatal (including for a user error) it stops the route and displays the error.
sub process { my $coderef = shift; try {&$coderef}; $@->reportFatal(to => 'error_handler', is_fatal => 0); $@ ? 0 : 1; # Return true on success }
In terms of exceptions that I wasn't expecting (e.g. unable to connect to the database), I catch all of these using the init_error hook:
hook init_error => sub { my $error = shift; panic $error->{exception}; };
This works, but it's a bit messy, so I'd like to find a better way of doing it.
That's it in short. I hope to write it all up at some point and/or create a plugin, but feel free to ask questions in the meantime.
Andy
_______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
participants (4)
-
Andrew Beverley -
Ovid -
Sawyer X -
WK