1Dancer2::Plugin::LogRepUosretr(3C)ontributed Perl DocumeDnatnacteiro2n::Plugin::LogReport(3)
2
3
4
6 Dancer2::Plugin::LogReport - logging and exceptions via Log::Report
7
9 Dancer2::Plugin::LogReport
10 is a Dancer2::Plugin
11
13 # Load the plugin into Dancer2
14 # see Log::Report::import() for %options
15 use Dancer2::Plugin::LogReport %options;
16
17 # Stop execution, redirect, and display an error to the user
18 $name or error "Please enter a name";
19
20 # Add debug information to logger
21 trace "We're here";
22
23 # Handling user errors cleanly
24 if (process( sub {MyApp::Model->create_user} )) {
25 # Success, redirect user elsewhere
26 } else {
27 # Failed, continue as if submit hadn't been made.
28 # Error message will be in session for display later.
29 }
30
31 # Send errors to template for display
32 hook before_template => sub {
33 my $tokens = shift;
34 $tokens->{messages} = session 'messages';
35 session 'messages' => [];
36 }
37
39 [The Dancer2 plugin was contributed by Andrew Beverley]
40
41 This module provides easy access to the extensive logging facilities
42 provided by Log::Report. Along with Dancer2::Logger::LogReport, this
43 brings together all the internal Dancer2 logging, handling for expected
44 and unexpected exceptions, translations and application logging.
45
46 Logging is extremely flexible using many of the available dispatchers.
47 Multiple dispatchers can be used, each configured separately to display
48 different messages in different formats. By default, messages are
49 logged to a session variable for display on a webpage, and to STDERR.
50
51 Messages within this plugin use the extended
52 Dancer2::Logger::LogReport::Message class rather than the standard
53 Log::Report::Message class.
54
55 Note that it is currently recommended to use the plugin in all apps
56 within a Dancer2 program, not only some. Therefore, wherever you "use
57 Dancer2" you should also "use Dancer2::Plugin::LogReport". This does
58 not apply if using the same app name ("use Dancer2 appname,
59 'Already::Exists'"). In all other modules, you can just "use
60 Log::Report".
61
62 Read the "DETAILS" in below in this manual-page.
63
65 $obj->fatal_handler()
66 "fatal_handler()" allows alternative handlers to be defined in
67 place of (or in addition to) the default redirect handler that is
68 called on a fatal error.
69
70 Calls should be made with 1 parameter: the subroutine to call in
71 the case of a fatal error. The subroutine is passed 3 parameters:
72 the DSL, the message in question, and the reason. The subroutine
73 should return true or false depending on whether it handled the
74 error. If it returns false, the next fatal handler is called, and
75 if there are no others then the default redirect fatal handler is
76 called.
77
78 example: Error handler based on URL (e.g. API)
79
80 fatal_handler sub {
81 my ($dsl, $msg, $reason) = @_;
82 return if $dsl->app->request->uri !~ m!^/api/!;
83 status $reason eq 'PANIC' ? 'Internal Server Error' : 'Bad Request';
84 $dsl->send_as(JSON => {
85 error => 1,
86 error_description => $msg->toString,
87 }, {
88 content_type => 'application/json; charset=UTF-8',
89 });
90 };
91
92 example: Return JSON responses for requests with content-type of
93 application/json
94
95 fatal_handler sub {
96 my ($dsl, $msg, $reason, $default) = @_;
97
98 (my $ctype = $dsl->request->header('content-type')) =~ s/;.*//;
99 return if $ctype ne 'application/json';
100 status $reason eq 'PANIC' ? 'Internal Server Error' : 'Bad Request';
101 $dsl->send_as(JSON => {
102 error => 1,
103 description => $msg->toString,
104 }, {
105 content_type => 'application/json; charset=UTF-8',
106 });
107 };
108
109 $obj->process()
110 "process()" is an eval, but one which expects and understands
111 exceptions generated by Log::Report. Any messages will be logged as
112 normal in accordance with the dispatchers, but any fatal exceptions
113 will be caught and handled gracefully. This allows much simpler
114 error handling, rather than needing to test for lots of different
115 scenarios.
116
117 In a module, it is enough to simply use the "error" keyword in the
118 event of a fatal error.
119
120 The return value will be 1 for success or 0 if a fatal exception
121 occurred.
122
123 See the "DETAILS" for an example of how this is expected to be
124 used.
125
126 This module is configured only once in your application. The other
127 modules which make your website do not need to require this plugin,
128 instead they can "use Log::Report" to get useful functions like
129 error and fault.
130
131 Handlers
132 All the standard Log::Report functions are available to use. Please see
133 the "The Reason for the report" in Log::Report for details of when each
134 one should be used.
135
136 Log::Report class functionality to class messages (which can then be
137 tested later):
138
139 notice __x"Class me up", _class => 'label';
140 ...
141 if ($msg->inClass('label')) ...
142
143 Dancer2::Plugin::LogReport has a special message class, "no_session",
144 which prevents the message from being saved to the messages session
145 variable. This is useful, for example, if you are writing messages
146 within the session hooks, in which case recursive loops can be
147 experienced.
148
149 $obj->alert()
150 $obj->assert()
151 $obj->error()
152 $obj->failure()
153 $obj->fault()
154 $obj->info()
155 $obj->mistake()
156 $obj->notice()
157 $obj->panic()
158 $obj->success()
159 This is a special additional type, equivalent to "notice". The
160 difference is that messages using this keyword will have the class
161 "success" added, which can be used to color the messages
162 differently to the end user. For example,
163 Dancer2::Plugin::LogReport::Message#bootstrap_color uses this to
164 display the message in green.
165
166 $obj->trace()
167 $obj->warning()
168
170 This chapter will guide you through the myriad of ways that you can use
171 Log::Report in your Dancer2 application.
172
173 We will set up our application to do the following:
174
175 Messages to the user
176 We'll look at an easy way to output messages to the user's web
177 page, whether they be informational messages, warnings or errors.
178
179 Debug information
180 We'll look at an easy way to log debug information, at different
181 levels.
182
183 Manage unexpected exceptions
184 We'll handle unexpected exceptions cleanly, in the unfortunate
185 event that they happen in your production application.
186
187 Email alerts of significant errors
188 If we do get unexpected errors then we want to be notified them.
189
190 Log DBIC information and errors
191 We'll specifically look at nice ways to log SQL queries and errors
192 when using DBIx::Class.
193
194 Larger example
195 In its simplest form, this module can be used for more flexible logging
196
197 get '/route' => sub {
198 # Stop execution, redirect, and display an error to the user
199 $name or error "Please enter a name";
200
201 # The same but translated
202 $name or error __"Please enter a name";
203
204 # The same but translated and with variables
205 $name or error __x"{name} is not valid", name => $name;
206
207 # Show the user a warning, but continue execution
208 mistake "Not sure that's what you wanted";
209
210 # Add debug information, can be caught in syslog by adding
211 # the (for instance) syslog dispatcher
212 trace "Hello world";
213 };
214
215 Setup and Configuration
216 To make full use of Log::Report, you'll need to use both
217 Dancer2::Logger::LogReport and Dancer2::Plugin::LogReport.
218
219 Dancer2::Logger::LogReport
220
221 Set up Dancer2::Logger::LogReport by adding it to your Dancer2
222 application configuration (see Dancer2::Config). By default, all
223 messages will go to STDERR.
224
225 To get all message out "the Perl way" (using print, warn and die) just
226 use
227
228 logger: "LogReport"
229
230 At start, these are handled by a Log::Report::Dispatcher::Perl object,
231 named 'default'. If you open a new dispatcher with the name 'default',
232 the output via the perl mechanisms will be stopped.
233
234 To also send messages to your syslog:
235
236 logger: "LogReport"
237
238 engines:
239 logger:
240 LogReport:
241 log_format: %a%i%m # See Dancer2::Logger::LogReport
242 app_name: MyApp
243 dispatchers:
244 default: # Name
245 type: SYSLOG # Log::Reporter::dispatcher() options
246 identity: myapp
247 facility: local0
248 flags: "pid ndelay nowait"
249 mode: DEBUG
250
251 To send messages to a file:
252
253 logger: "LogReport"
254
255 engines:
256 logger:
257 LogReport:
258 log_format: %a%i%m # See Dancer2::Logger::LogReport
259 app_name: MyApp
260 dispatchers:
261 logfile: # "default" dispatcher stays open as well
262 type: FILE
263 to: /var/log/myapp.log
264 charset: utf-8
265 mode: DEBUG
266
267 See Log::Report::Dispatcher for full details of options.
268
269 Finally: a Dancer2 script may run many applications. Each application
270 can have its own logger configuration. However, Log::Report
271 dispatchers are global, so will be shared between Dancer2 applications.
272 Any attempt to create a new Log::Report dispatcher by the same name (as
273 will happen when a new Dancer2 application is started with the same
274 configuration) will be ignored.
275
276 Dancer2::Plugin::LogReport
277
278 To use the plugin, you simply use it in your application:
279
280 package MyApp;
281 use Log::Report (); # use early and minimal once
282 use Dancer2;
283 use Dancer2::Plugin::LogReport %config;
284
285 Dancer2::Plugin::LogReport takes the same %config options as
286 Log::Report itself (see Log::Report::import()).
287
288 If you want to send messages from your modules/models, there is no need
289 to use this specific plugin. Instead, you should simply "use
290 Log::Report" to negate the need of loading all the Dancer2 specific
291 code.
292
293 In use
294 Logging debug information
295
296 In its simplest form, you can now use all the Log::Report logging
297 functions to send messages to your dispatchers (as configured in the
298 Logger configuration):
299
300 trace "I'm here";
301
302 warning "Something dodgy happened";
303
304 panic "I'm bailing out";
305
306 # Additional, special Dancer2 keyword
307 success "Settings saved successfully";
308
309 Exceptions
310
311 Log::Report is a combination of a logger and an exception system.
312 Messages to be logged are thrown to all listening dispatchers to be
313 handled.
314
315 This module will also catch any unexpected exceptions:
316
317 # This will be caught, the error will be logged (full stacktrace to STDOUT,
318 # short message to the session messages), and the user will be forwarded
319 # (default to /). This would also be sent to syslog with the appropriate
320 # dispatcher.
321 get 'route' => sub {
322 my $foo = 1;
323 my $bar = $foo->{x}; # whoops
324 }
325
326 For a production application ("show_errors: 1"), the message saved in
327 the session will be the generic text "An unexpected error has
328 occurred". This can be customised in the configuration file, and will
329 be translated.
330
331 Sending messages to the user
332
333 To make it easier to send messages to your users, messages at the
334 following levels are also stored in the user's session: "notice",
335 "warning", "mistake", "error", "fault", "alert", "failure" and "panic".
336
337 You can pass these to your template and display them at each page
338 render:
339
340 hook before_template => sub {
341 my $tokens = shift;
342 $tokens->{messages} = session 'messages';
343 session 'messages' => []; # Clear the message queue
344 }
345
346 Then in your template (for example the main layout):
347
348 [% FOR message IN messages %]
349 <div class="alert alert-[% message.bootstrap_color %]">
350 [% message.toString | html_entity %]
351 </div>
352 [% END %]
353
354 The "bootstrap_color" of the message is compatible with Bootstrap
355 contextual colors: "success", "info", "warning" or "danger".
356
357 Now, anywhere in your application that you have used Log::Report, you
358 can
359
360 warning "Hey user, you should now about this";
361
362 and the message will be sent to the next page the user sees.
363
364 Handling user errors
365
366 Sometimes we write a function in a model, and it would be nice to have
367 a nice easy way to return from the function with an error message. One
368 way of doing this is with a separate error message variable, but that
369 can be messy code. An alternative is to use exceptions, but these can
370 be a pain to deal with in terms of catching them. Here's how to do it
371 with Log::Report.
372
373 In this example, we do use exceptions, but in a neat, easier to use
374 manner.
375
376 First, your module/model:
377
378 package MyApp::CD;
379
380 sub update {
381 my ($self, %values) = @_;
382 $values{title} or error "Please enter a title";
383 $values{description} or warning "No description entered";
384 }
385
386 Then, in your controller:
387
388 package MyApp;
389 use Dancer2;
390
391 post '/cd' => sub {
392 my %values = (
393 title => param('title');
394 description => param('description');
395 );
396 if (process sub { MyApp::CD->update(%values) } ) {
397 success "CD updated successfully";
398 redirect '/cd';
399 }
400
401 template 'cd' => { values => \%values };
402 }
403
404 Now, when update() is called, any exceptions are caught. However, there
405 is no need to worry about any error messages. Both the error and
406 warning messages in the above code will have been stored in the
407 messages session variable, where they can be displayed using the code
408 in the previous section. The "error" will have caused the code to stop
409 running, and process() will have returned false. "warning" will have
410 simply logged the warning and not caused the function to stop running.
411
412 Logging DBIC database queries and errors
413
414 If you use DBIx::Class in your application, you can easily integrate
415 its logging and exceptions. To log SQL queries:
416
417 # Log all queries and execution time
418 $schema->storage->debugobj(new Log::Report::DBIC::Profiler);
419 $schema->storage->debug(1);
420
421 By default, exceptions from DBIC are classified at the level "error".
422 This is normally a user level error, and thus may be filtered as normal
423 program operation. If you do not expect to receive any DBIC exceptions,
424 then it is better to class them at the level "panic":
425
426 # panic() DBIC errors
427 $schema->exception_action(sub { panic @_ });
428 # Optionally get a stracktrace too
429 $schema->stacktrace(1);
430
431 If you are occasionally running queries where you expect to naturally
432 get exceptions (such as not inserting multiple values on a unique
433 constraint), then you can catch these separately:
434
435 try { $self->schema->resultset('Unique')->create() };
436 # Log any messages from try block, but only as trace
437 $@->reportAll(reason => 'TRACE');
438
439 Email alerts of exceptions
440
441 If you have an unexpected exception in your production application,
442 then you probably want to be notified about it. One way to do so is
443 configure rsyslog to send emails of messages at the panic level. Use
444 the following configuration to do so:
445
446 # Normal logging from LOCAL0
447 local0.* -/var/log/myapp.log
448
449 # Load the mail module
450 $ModLoad ommail
451 # Configure sender, receiver and mail server
452 $ActionMailSMTPServer localhost
453 $ActionMailFrom root
454 $ActionMailTo root
455 # Set up an email template
456 $template mailSubject,"Critical error on %hostname%"
457 $template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'\r\nseverity='%syslogseverity-text%'"
458 $ActionMailSubject mailSubject
459 # Send an email no more frequently than every minute
460 $ActionExecOnlyOnceEveryInterval 60
461 # Configure the level of message to notify via email
462 if $syslogfacility-text == 'local0' and $syslogseverity < 3 then :ommail:;mailBody
463 $ActionExecOnlyOnceEveryInterval 0
464
465 With the above configuration, you will only be emailed of severe
466 errors, but can view the full log information in /var/log/myapp.log
467
469 All configuration is optional. The example configuration file below
470 shows the configuration options and defaults.
471
472 plugins:
473 LogReport:
474 # Whether to handle Dancer HTTP errors such as 404s. Currently has
475 # no effect due to unresolved issues saving messages to the session
476 # and accessing the DSL at that time.
477 handle_http_errors: 1
478 # Where to forward users in the event of an uncaught fatal
479 # error within a GET request
480 forward_url: /
481 # Or you can specify a template instead [1.13]
482 forward_template: error_template_file # Defaults to empty
483 # For a production server (show_errors: 0), this is the text that
484 # will be displayed instead of unexpected exception errors
485 fatal_error_message: An unexpected error has occurred
486 # The levels of messages that will be saved to the session, and
487 # thus displayed to the end user
488 session_messages: [ NOTICE, WARNING, MISTAKE, ERROR, FAULT, ALERT, FAILURE, PANIC ]
489
491 This module is part of Log-Report distribution version 1.34, built on
492 September 15, 2022. Website: http://perl.overmeer.net/CPAN/
493
495 Copyrights 2007-2022 by [Mark Overmeer <markov@cpan.org>]. For other
496 contributors see ChangeLog.
497
498 This program is free software; you can redistribute it and/or modify it
499 under the same terms as Perl itself. See http://dev.perl.org/licenses/
500
501
502
503perl v5.36.0 2022-09-15 Dancer2::Plugin::LogReport(3)