distribution Log-Report-1.06.tar.gz
Mark Overmeer authored 8 years ago
Mark Overmeer committed 6 years ago
5 | 5 | |
6 | 6 | TODO: |
7 | 7 | . connect to Message::Passing framework |
8 | ||
9 | version 1.06: Mon Jun 15 17:30:33 CEST 2015 | |
10 | ||
11 | Fixes: | |
12 | - t/60mojo.t will not run on old mojo's: requires 2.16 (2011) | |
13 | [cpantesters] | |
14 | - ::Dispatcher::File do not use %F/%T in strfime, which is not | |
15 | supported by Windows. | |
16 | - make ::Die understand multiline 'die()' messages. | |
17 | rt.cpan.org#101389 [Ken Neighbors] | |
18 | ||
19 | Improvements: | |
20 | - add Dancer::Log::Report and examples/dancer/ | |
21 | - add Dancer2::*, contributed by [Andrew Beverly] | |
8 | 22 | |
9 | 23 | version 1.05: Tue Jun 24 09:38:15 CEST 2014 |
10 | 24 |
1 | 1 | MANIFEST |
2 | 2 | Makefile.PL |
3 | 3 | README |
4 | examples/dancer/dancer1.pl | |
5 | lib/Dancer/Logger/LogReport.pm | |
6 | lib/Dancer2/Logger/LogReport.pm | |
7 | lib/Dancer2/Plugin/LogReport.pm | |
4 | 8 | lib/Log/Report.pm |
5 | 9 | lib/Log/Report/Die.pm |
6 | 10 | lib/Log/Report/Dispatcher.pm |
1 | 1 | |
2 | 2 | use 5.008; |
3 | 3 | |
4 | my $version = '1.05'; | |
4 | my $version = '1.06'; | |
5 | 5 | |
6 | 6 | my %prereq = |
7 | 7 | ( Test::More => '0.86' |
47 | 47 | RAWDIR = ../public_html/log-report/raw |
48 | 48 | DISTDIR = ../public_html/log-report/source |
49 | 49 | LICENSE = artistic |
50 | SKIP_LINKS = XML::LibXML | |
50 | SKIP_LINKS = Dancer2::Config | |
51 | 51 | |
52 | 52 | # for OODoc's oodist, POD |
53 | 53 | FIRST_YEAR = 2007 |
0 | #!/usr/bin/env perl | |
1 | # Daemon at localhost:3000 | |
2 | ||
3 | use Dancer; | |
4 | use Dancer::Logger::LogReport; | |
5 | use Log::Report import => 'dispatcher'; | |
6 | ||
7 | dispatcher FILE => 'logfile' # open additional log destination | |
8 | # , mode => 'DEBUG' # extended information | |
9 | , to => '/tmp/dancer-demo.log'; | |
10 | ||
11 | dispatcher close => 'default'; # closes warn/die default dispatcher | |
12 | ||
13 | set logger => 'log_report'; | |
14 | set log => 'debug'; | |
15 | set logger_format => 'LOG: %i%m'; | |
16 | ||
17 | get '/' => sub { | |
18 | error "we reached the log"; # use Dancer's error() syntax! | |
19 | notice "one more"; # additional levels, same syntax | |
20 | return "Hello World!\n"; | |
21 | }; | |
22 | ||
23 | start; |
0 | package Dancer::Logger::LogReport; | |
1 | use base 'Dancer::Logger::Abstract', 'Exporter'; | |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use Scalar::Util qw/blessed/; | |
7 | use Log::Report 'logreport', import => 'report'; | |
8 | use Log::Report::Dispatcher (); | |
9 | ||
10 | our $AUTHORITY = 'cpan:MARKOV'; | |
11 | ||
12 | our @EXPORT = qw/ | |
13 | trace | |
14 | assert | |
15 | notice | |
16 | alert | |
17 | panic | |
18 | /; | |
19 | ||
20 | my %level_dancer2lr = | |
21 | ( core => 'TRACE' | |
22 | , debug => 'TRACE' | |
23 | ); | |
24 | ||
25 | =chapter NAME | |
26 | ||
27 | Dancer::Logger::LogReport - reroute Dancer logs into Log::Report | |
28 | ||
29 | =chapter SYNOPSIS | |
30 | ||
31 | # When your main program is not a Dancer object | |
32 | use My::Dancer::App; | |
33 | use Log::Report; | |
34 | ... start dispatcher ... | |
35 | error "something is wrong"; # Log::Report::error() | |
36 | ||
37 | # When your main program is a Dancer object | |
38 | use Dancer; | |
39 | use Dancer::Logger::LogReport; | |
40 | use Log::Report import => 'dispatcher'; | |
41 | ... start dispatcher ... | |
42 | error "something is wrong"; # Dancer::error() | |
43 | ||
44 | # In any case, your main program needs to start log dispatcers | |
45 | # Both Dancer and other Log::Report based modules will send | |
46 | # their messages here: | |
47 | dispatcher FILE => 'default', ...; | |
48 | ||
49 | # In your config | |
50 | logger: log_report | |
51 | logger_format: %i%m # keep it simple | |
52 | log: debug # filtered by dispatchers | |
53 | ||
54 | =chapter DESCRIPTION | |
55 | ||
56 | The M<Log::Report> exception/translation framework defines a large | |
57 | number of logging back-ends. The same log messages can be sent to | |
58 | multiple destinations at the same time via flexible dispatchers. | |
59 | When you use this logger in your Dancer application, it will nicely | |
60 | integrate with non-Dancer modules which need logging. | |
61 | ||
62 | Many log back-ends, like syslog, have more levels of system messages. | |
63 | Modules who explicitly load this module can use the missing C<assert>, | |
64 | C<notice>, C<panic>, and C<alert> log levels. The C<trace> name is | |
65 | provided as well: when you are debugging, you add a 'trace' to your | |
66 | program... its just a better name than 'debug'. | |
67 | ||
68 | You probably want to set a very simple C<logger_format>, because the | |
69 | dispatchers do already add some of the fields that the default | |
70 | C<simple> format adds. For instance, to get the filename/line-number | |
71 | in messages depends on the dispatcher 'mode' (f.i. 'DEBUG'). | |
72 | ||
73 | You also want to set the log level to C<debug>, because level filtering | |
74 | is controled per dispatcher (as well) | |
75 | ||
76 | =cut | |
77 | ||
78 | # Add some extra 'levels' | |
79 | sub trace { goto &Dancer::Logger::debug } | |
80 | sub assert { goto &Dancer::Logger::assert } | |
81 | sub notice { goto &Dancer::Logger::notice } | |
82 | sub panic { goto &Dancer::Logger::panic } | |
83 | sub alert { goto &Dancer::Logger::alert } | |
84 | ||
85 | { use Dancer::Logger; | |
86 | package Dancer::Logger; | |
87 | sub assert { my $l = logger(); $l && $l->_log(assert => _serialize(@_)) } | |
88 | sub notice { my $l = logger(); $l && $l->_log(notice => _serialize(@_)) } | |
89 | sub alert { my $l = logger(); $l && $l->_log(alert => _serialize(@_)) } | |
90 | sub panic { my $l = logger(); $l && $l->_log(panic => _serialize(@_)) } | |
91 | } | |
92 | ||
93 | #sub init(@) | |
94 | #{ my $self = shift; | |
95 | # $self->SUPER::init(@_); | |
96 | #} | |
97 | ||
98 | sub _log { | |
99 | my ($self, $level, $params) = @_; | |
100 | ||
101 | # all dancer levels are the same as L::R levels, except: | |
102 | my $msg; | |
103 | if(blessed $params && $params->isa('Log::Report::Message')) | |
104 | { $msg = $params; | |
105 | } | |
106 | else | |
107 | { $msg = $self->format_message($level => $params); | |
108 | $msg =~ s/\n+$//; | |
109 | } | |
110 | ||
111 | # The levels are nearly the same. | |
112 | my $reason = $level_dancer2lr{$level} // uc $level; | |
113 | ||
114 | # Gladly, report() does not get confused between Dancer's use of | |
115 | # Try::Tiny and Log::Report's try() which starts a new dispatcher. | |
116 | report {is_fatal => 0}, $reason => $msg; | |
117 | ||
118 | undef; | |
119 | } | |
120 | ||
121 | 1; |
0 | package Dancer2::Logger::LogReport; | |
1 | # ABSTRACT: Dancer2 logger engine for Log::Report | |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use Moo; | |
7 | use Dancer2::Core::Types; | |
8 | use Scalar::Util qw/blessed/; | |
9 | use Log::Report 'logreport', syntax => 'REPORT', mode => 'DEBUG'; | |
10 | ||
11 | our $AUTHORITY = 'cpan:MARKOV'; | |
12 | ||
13 | my %level_dancer2lr = | |
14 | ( core => 'TRACE' | |
15 | , debug => 'TRACE' | |
16 | ); | |
17 | ||
18 | with 'Dancer2::Core::Role::Logger'; | |
19 | ||
20 | # Set by calling function | |
21 | has dispatchers => | |
22 | ( is => 'ro' | |
23 | , isa => Maybe[HashRef] | |
24 | , lazy => 1 | |
25 | ); | |
26 | ||
27 | sub BUILD | |
28 | { my $self = shift; | |
29 | my $dispatchers = $self->dispatchers; | |
30 | ||
31 | foreach my $name (keys %$dispatchers) | |
32 | { my %dispatcher = %{$dispatchers->{$name}}; | |
33 | my $type = delete $dispatcher{type}; | |
34 | dispatcher $type => $name, %dispatcher; | |
35 | } | |
36 | } | |
37 | ||
38 | =chapter NAME | |
39 | ||
40 | Dancer2::Logger::LogReport - reroute Dancer2 logs into Log::Report | |
41 | ||
42 | =chapter SYNOPSIS | |
43 | ||
44 | # This module is loaded when configured. It does not provide | |
45 | # end-user functions or methods. | |
46 | ||
47 | =chapter DESCRIPTION | |
48 | ||
49 | This logger allows the use of the many logging backends available | |
50 | in M<Log::Report>. It will process all of the Dancer2 log messages, | |
51 | and also allow any other module to use the same logging facilities. The | |
52 | same log messages can be sent to multiple destinations at the same time | |
53 | via flexible dispatchers. | |
54 | ||
55 | If using this logger, you may also want to use | |
56 | M<Dancer2::Plugin::LogReport> | |
57 | ||
58 | Many log back-ends, like syslog, have more levels of system messages. | |
59 | Modules who explicitly load this module can use the missing C<assert>, | |
60 | C<notice>, C<panic>, and C<alert> log levels. The C<trace> name is | |
61 | provided as well: when you are debugging, you add a 'trace' to your | |
62 | program... it's just a better name than 'debug'. | |
63 | ||
64 | You probably want to set a very simple C<logger_format>, because the | |
65 | dispatchers do already add some of the fields that the default C<simple> | |
66 | format adds. For instance, to get the filename/line-number in messages | |
67 | depends on the dispatcher 'mode' (f.i. 'DEBUG'). | |
68 | ||
69 | You also want to set the log level to C<debug>, because level filtering is | |
70 | controlled per dispatcher (as well). | |
71 | ||
72 | =chapter METHODS | |
73 | ||
74 | =method log $level, $params | |
75 | ||
76 | =cut | |
77 | ||
78 | sub log($$$) | |
79 | { my ($self, $level, $params) = @_; | |
80 | ||
81 | # all dancer levels are the same as L::R levels, except: | |
82 | my $msg; | |
83 | if(blessed $params && $params->isa('Log::Report::Message')) | |
84 | { $msg = $params; | |
85 | } | |
86 | else | |
87 | { $msg = $self->format_message($level => $params); | |
88 | $msg =~ s/\n+$//; | |
89 | } | |
90 | ||
91 | # The levels are nearly the same. | |
92 | my $reason = $level_dancer2lr{$level} // uc $level; | |
93 | ||
94 | report {is_fatal => 0}, $reason => $msg; | |
95 | ||
96 | undef; | |
97 | } | |
98 | ||
99 | #-------------- | |
100 | =chapter DETAILS | |
101 | ||
102 | =section Configuration | |
103 | ||
104 | The setting B<logger> should be set to C<LogReport> in order to use | |
105 | this logging engine in a Dancer application. See M<Dancer2::Config> | |
106 | about ways to include these settings in your program. | |
107 | ||
108 | There is only one optional configuration parameter: C<dispatchers>. This | |
109 | defines the M<Log::Report> dispatchers to use. Any number of dispatchers | |
110 | may be configured. | |
111 | ||
112 | # instruct Dancer2 to load this module | |
113 | logger: LogReport | |
114 | ||
115 | # use default Log::Report dispatchers | |
116 | engines: | |
117 | logger: | |
118 | LogReport: | |
119 | ||
120 | # syslog and file dispatcher | |
121 | engines: | |
122 | logger: | |
123 | LogReport: | |
124 | logger_format: %i%m # keep it simple | |
125 | dispatchers: | |
126 | syslog: # Name | |
127 | type: SYSLOG # Log::Report dispatcher type | |
128 | identity: gads # Dispatcher options | |
129 | facility: local0 | |
130 | flags: "pid ndelay nowait" | |
131 | mode: DEBUG | |
132 | default: # will replace default dispatcher | |
133 | type: FILE | |
134 | to: /var/log/mylog | |
135 | charset: utf-8 | |
136 | accept: NOTICE- # Only accept NOTICE and above | |
137 | ||
138 | =cut | |
139 | ||
140 | 1; |
0 | package Dancer2::Plugin::LogReport; | |
1 | ||
2 | use warnings; | |
3 | use strict; | |
4 | ||
5 | use Dancer2::Plugin; | |
6 | use Log::Report 'log-report', syntax => 'REPORT'; | |
7 | ||
8 | use Scalar::Util qw/blessed/; | |
9 | ||
10 | my $_dsl; # XXX How to avoid the global? | |
11 | my $_settings; | |
12 | ||
13 | =chapter NAME | |
14 | ||
15 | Dancer2::Plugin::LogReport - logging and exceptions via Log::Report | |
16 | ||
17 | =chapter SYNOPSIS | |
18 | ||
19 | # Load the plugin into Dancer2 | |
20 | # see Log::Report::import() for %options | |
21 | use Dancer2::Plugin::LogReport %options; | |
22 | ||
23 | # Stop execution, redirect, and display an error to the user | |
24 | $name or error "Please enter a name"; | |
25 | ||
26 | # Add debug information to logger | |
27 | trace "We're here"; | |
28 | ||
29 | # Handling user errors cleanly | |
30 | if (process( sub {MyApp::Model->create_user} )) { | |
31 | # Success, redirect user elsewhere | |
32 | } else { | |
33 | # Failed, continue as if submit hadn't been made. | |
34 | # Error message will be in session for display later. | |
35 | } | |
36 | ||
37 | # Send errors to template for display | |
38 | hook before_template => sub { | |
39 | my $tokens = shift; | |
40 | $tokens->{messages} = session 'messages'; | |
41 | session 'messages' => []; | |
42 | } | |
43 | ||
44 | =chapter DESCRIPTION | |
45 | ||
46 | This module provides easy access to the extensive logging facilities | |
47 | provided by M<Log::Report>. Along with M<Dancer2::Logger::LogReport>, | |
48 | this brings together all the internal Dancer2 logging, handling for | |
49 | expected and unexpected exceptions, translations and application logging. | |
50 | ||
51 | Logging is extremely flexible using many of the available | |
52 | L<dispatchers|Log::Report::Dispatcher/DETAILS>. Multiple dispatchers can be | |
53 | used, each configured separately to display different messages in different | |
54 | formats. By default, messages are logged to a session variable for display on | |
55 | a webpage, and to STDERR. | |
56 | ||
57 | =chapter METHODS | |
58 | ||
59 | =cut | |
60 | ||
61 | # "use" import | |
62 | sub import | |
63 | { my $class = shift; | |
64 | Log::Report->import('+2', @_, syntax => 'LONG'); | |
65 | } | |
66 | ||
67 | # Dancer2 import | |
68 | on_plugin_import | |
69 | { my $dsl = $_dsl = shift; # capture global singleton | |
70 | my $settings = $_settings = plugin_setting; | |
71 | ||
72 | # Need init_error for exceptions and other errors | |
73 | $dsl->hook(init_error => sub { | |
74 | my $error = shift; | |
75 | # Catch other exceptions. This hook is called for all errors | |
76 | # not just exceptions (including for example 404s), so check first. | |
77 | # If it's an exception then panic it to get Log::Report | |
78 | # to handle it nicely. If it's another error such as a 404 | |
79 | # then exception will not be set. | |
80 | report 'PANIC' => $error->{exception} | |
81 | if $error->{exception}; | |
82 | }); | |
83 | ||
84 | if ($settings->{handle_http_errors}) | |
85 | { # Need after_error for HTTP errors (eg 404) so as to | |
86 | # be able to change the forwarding location | |
87 | $dsl->hook(after_error => sub { | |
88 | use Data::Dumper; say STDERR Dumper \@_; | |
89 | my $error = shift; | |
90 | my $msg = $error->status . ": " | |
91 | . Dancer2::Core::HTTP->status_message($error->status); | |
92 | ||
93 | # XXX How to write messages to the session? request() is not | |
94 | # in the DSL at this point. At least log it. | |
95 | report 'TRACE' => $msg; | |
96 | _forward_home( $error, danger => $msg ); # $error is the request | |
97 | }); | |
98 | } | |
99 | ||
100 | # This is so that all messages go into the session, to be displayed | |
101 | # on the web page (if required) | |
102 | dispatcher CALLBACK => 'error_handler' | |
103 | , callback => \&_error_handler | |
104 | , mode => 'DEBUG'; | |
105 | }; # ";" required! | |
106 | ||
107 | =method process | |
108 | ||
109 | C<process()> is an eval, but one which expects and handles exceptions | |
110 | generated by M<Log::Report>. Any messages will be logged as normal in | |
111 | accordance with the dispatchers, but any fatal exceptions will be caught | |
112 | and handled gracefully. This allows much simpler error handling, rather | |
113 | than needing to test for lots of different scenarios. | |
114 | ||
115 | In a module, it is enough to simply use the C<error> keyword in the event of a | |
116 | fatal error. | |
117 | ||
118 | The return value will be 1 for success or 0 if a fatal exception occurred. | |
119 | ||
120 | See the L</DETAILS> for an example of how this is expected to be used. | |
121 | ||
122 | Modules do not need to use this plugin, instead they can C<use Log::Report>. | |
123 | ||
124 | =cut | |
125 | ||
126 | sub process($$) | |
127 | { my ($dsl, $coderef) = @_; | |
128 | try { $coderef->() }; | |
129 | ||
130 | # Return true on success | |
131 | if (my $exception = $@->wasFatal) | |
132 | { $exception->throw(is_fatal => 0); | |
133 | return 0; | |
134 | } | |
135 | $@->reportAll; | |
136 | 1; | |
137 | } | |
138 | ||
139 | register process => \&process; | |
140 | ||
141 | sub _message_add($$) | |
142 | { my ($type, $text) = @_; | |
143 | $text && $type or return; | |
144 | unless ($_dsl->app->request) | |
145 | { # This happens for HTTP errors | |
146 | # XXX the session is not available in the DSL | |
147 | report 'ASSERT' => "Unable to write message to session: unable to write cookie"; | |
148 | return; | |
149 | } | |
150 | my $messages_variable = $_settings->{messages_key} || 'messages'; | |
151 | my $session = $_dsl->app->session; | |
152 | my $msgs = $session->read($messages_variable); | |
153 | push @$msgs, { text => $text, type => $type }; | |
154 | $session->write($messages_variable => $msgs); | |
155 | } | |
156 | ||
157 | #------ | |
158 | =section Handlers | |
159 | ||
160 | All the standard M<Log::Report> functions are available to use. Please see the | |
161 | L<Log::Report/"The Reason for the report"> for details | |
162 | of when each one should be used. | |
163 | ||
164 | =method trace | |
165 | =method assert | |
166 | =method info | |
167 | =method notice | |
168 | =method warning | |
169 | =method mistake | |
170 | =method error | |
171 | =method fault | |
172 | =method alert | |
173 | =method failure | |
174 | =method panic | |
175 | =cut | |
176 | ||
177 | sub _forward_home($$$) | |
178 | { my $dsl = shift; | |
179 | _message_add(shift, shift); | |
180 | my $page = $_settings->{forward_url} || '/'; | |
181 | $dsl->redirect($page); | |
182 | } | |
183 | ||
184 | sub _error_handler($$$$) | |
185 | { my ($disp, $options, $reason, $message) = @_; | |
186 | ||
187 | my $fatal_handler = sub { | |
188 | _forward_home( $_dsl, danger => $_[0] ) | |
189 | unless $_dsl->request->uri eq '/' && $_dsl->request->is_get; | |
190 | }; | |
191 | ||
192 | my %handler = | |
193 | ( # Default do nothing for the moment (TRACE|ASSERT|INFO) | |
194 | default => sub {} | |
195 | ||
196 | # Notice that something has happened. Not an error. | |
197 | , NOTICE => sub {_message_add info => $_[0]} | |
198 | ||
199 | # Non-fatal problem. Show warning. | |
200 | , WARNING => sub {_message_add warning => $_[0]} | |
201 | ||
202 | # Non-fatal problem. Show warning. | |
203 | , MISTAKE => sub {_message_add warning => $_[0]} | |
204 | ||
205 | # A user-created error condition that is not recoverable. | |
206 | # This could have already been caught by the process | |
207 | # subroutine, in which case we should continue running | |
208 | # of the program. In all other cases, we should bail | |
209 | # out. With the former, the exception will have been | |
210 | # re-thrown as a non-fatal exception, so check that. | |
211 | , ERROR => sub { | |
212 | exists $options->{is_fatal} && !$options->{is_fatal} | |
213 | ? _message_add( danger => $_[0] ) | |
214 | : _forward_home( $_dsl, danger => $_[0] ); | |
215 | } | |
216 | ||
217 | # 'FAULT', 'ALERT', 'FAILURE', 'PANIC' | |
218 | # All these are fatal errors. Display error to user, but | |
219 | # forward home so that we can reload. However, don't if | |
220 | # it's a GET request to the home, as it will cause a recursive | |
221 | # loop. In this case, do nothing, and let dancer handle it. | |
222 | , FAULT => $fatal_handler | |
223 | , ALERT => $fatal_handler | |
224 | , FAILURE => $fatal_handler | |
225 | , PANIC => $fatal_handler | |
226 | ); | |
227 | ||
228 | my $call = $handler{$reason} || $handler{default}; | |
229 | $call->("$message"); | |
230 | } | |
231 | ||
232 | sub _report($@) { | |
233 | my ($reason, $dsl) = (shift, shift); | |
234 | ||
235 | my $msg = (blessed($_[0]) && $_[0]->isa('Log::Report::Message')) | |
236 | ? $_[0] : Dancer2::Core::Role::Logger::_serialize(@_); | |
237 | ||
238 | report uc($reason) => $msg; | |
239 | } | |
240 | ||
241 | register trace => sub { _report(TRACE => @_) }; | |
242 | register assert => sub { _report(ASSERT => @_) }; | |
243 | register notice => sub { _report(NOTICE => @_) }; | |
244 | register mistake => sub { _report(MISTAKE => @_) }; | |
245 | register panic => sub { _report(PANIC => @_) }; | |
246 | register alert => sub { _report(ALERT => @_) }; | |
247 | ||
248 | register_plugin for_versions => ['2']; | |
249 | ||
250 | #---------- | |
251 | =chapter DETAILS | |
252 | ||
253 | =section Larger example | |
254 | ||
255 | In its simplest form, this module can be used for more flexible logging | |
256 | ||
257 | get '/route' => sub { | |
258 | # Stop execution, redirect, and display an error to the user | |
259 | $name or error "Please enter a name"; | |
260 | ||
261 | # The same but translated | |
262 | $name or error __"Please enter a name"; | |
263 | ||
264 | # The same but translated and with variables | |
265 | $name or error __x"{name} is not valid", name => $name; | |
266 | ||
267 | # Show the user a warning, but continue exection | |
268 | mistake "Not sure that's what you wanted"; | |
269 | ||
270 | # Add debug information, can be caught in syslog by adding the syslog | |
271 | # dispatcher | |
272 | trace "Hello world"; }; | |
273 | ||
274 | The module can also be used in models to test for user input and act | |
275 | accordingly, without needing to set up complicated error handling: | |
276 | ||
277 | # In a module | |
278 | package MyApp::MyModel sub create_user { | |
279 | ... | |
280 | $surname or error "Please enter a surname"; # Execution stops here | |
281 | ... | |
282 | $telephone or mistake "Tel not entered"; # Execution continues | |
283 | ... | |
284 | } | |
285 | ||
286 | # In the main app | |
287 | get '/user' => sub { | |
288 | ... | |
289 | if (param 'submit') { | |
290 | if (process( sub { MyApp::Model->create_user() })) { | |
291 | # Success, redirect user elsewhere | |
292 | } | |
293 | } | |
294 | # Failed, continue as if submit hadn't been made. Error will have been | |
295 | # logged in session to be displayed later. | |
296 | }; | |
297 | ||
298 | This module will also catch any unexpected exceptions: | |
299 | ||
300 | # This will be caught, the error will be logged (full stacktrace to STDOUT, | |
301 | # short message to the session messages), and the user will be forwarded | |
302 | # (default to /). This would also be sent to syslog with the appropriate | |
303 | # dispatcher. | |
304 | get 'route' => sub { | |
305 | my $foo = 1; | |
306 | my $bar = $foo->{x}; # whoops | |
307 | } | |
308 | ||
309 | Errors are all logged to the session. These need to be cleared once they have | |
310 | been displayed. | |
311 | ||
312 | hook before_template => sub { | |
313 | my $tokens = shift; | |
314 | # Pass messages to template and clear session | |
315 | $tokens->{messages} = session 'messages'; | |
316 | session 'messages' => []; | |
317 | } | |
318 | ||
319 | In the template. This example prints them in Bootstrap colors: | |
320 | ||
321 | [% FOR message IN messages %] | |
322 | [% IF message.type %] | |
323 | [% msgtype = message.type %] | |
324 | [% ELSE %] | |
325 | [% msgtype = "info" %] | |
326 | [% END %] | |
327 | <div class="alert alert-[% msgtype %]"> | |
328 | [% message.text | html_entity %] | |
329 | </div> | |
330 | [% END %] | |
331 | ||
332 | =section Configuration | |
333 | ||
334 | =subsection Dancer2 configuration | |
335 | ||
336 | In your application's configuration file (values shown are defaults): | |
337 | ||
338 | plugins: LogReport | |
339 | # Set the forward URL on fatal error that isn't caught | |
340 | forward_url: / | |
341 | ||
342 | # Set to 1 if you want the module to also catch Dancer HTTP errors | |
343 | # (such as 404s) | |
344 | handle_http_errors: 0 | |
345 | ||
346 | # Configure session variable for messages | |
347 | messages_key = messages | |
348 | ||
349 | =subsection Log::Report configuration | |
350 | ||
351 | Any L<Log::Report configuration options|Log::Report/Configuration> can also be | |
352 | used with this plugin. | |
353 | ||
354 | =subsection Dancer2::Logger::LogReport | |
355 | ||
356 | You probably want to also use and configure M<Dancer2::Logger::LogReport>. | |
357 | See its documentation for full details. | |
358 | ||
359 | =cut | |
360 | ||
361 | 1; | |
362 |
14 | 14 | |
15 | 15 | =chapter DESCRIPTION |
16 | 16 | |
17 | =chapter OVERLOADING | |
17 | This module is used internally, to translate output of 'die' and Carp | |
18 | functions into M<Log::Report::Message> objects. | |
18 | 19 | |
19 | 20 | =chapter Functions |
20 | 21 | |
68 | 69 | $text[0] =~ s/\s*[.:;]?\s*$err\s*$// # the $err is translation sensitive |
69 | 70 | or delete $opt{errno}; |
70 | 71 | |
71 | my $msg = shift @text; | |
72 | length $msg or $msg = 'stopped'; | |
72 | my @msg = shift @text; | |
73 | length $msg[0] or $msg[0] = 'stopped'; | |
73 | 74 | |
74 | 75 | my @stack; |
75 | 76 | foreach (@text) |
76 | { push @stack, [ $1, $2, $3 ] | |
77 | if m/^\s*(.*?)\s+called at (.*?) line (\d+)\s*$/; | |
77 | { if(m/^\s*(.*?)\s+called at (.*?) line (\d+)\s*$/) | |
78 | { push @stack, [ $1, $2, $3 ] } | |
79 | else { push @msg, $_ } | |
78 | 80 | } |
79 | 81 | $opt{stack} = \@stack; |
80 | 82 | $opt{classes} = [ 'perl', (@stack ? 'confess' : 'die') ]; |
83 | 85 | = @{$opt{stack}} ? ($opt{errno} ? 'ALERT' : 'PANIC') |
84 | 86 | : ($opt{errno} ? 'FAULT' : 'ERROR'); |
85 | 87 | |
86 | ($dietxt, \%opt, $reason, $msg); | |
88 | ($dietxt, \%opt, $reason, join("\n",@msg)); | |
87 | 89 | } |
88 | 90 | |
89 | 91 | "to die or not to die, that's the question"; |
128 | 128 | : $format eq 'LONG' |
129 | 129 | ? sub { my $msg = shift; |
130 | 130 | my $domain = shift || '-'; |
131 | my $stamp = strftime "%FT%T", gmtime; | |
131 | my $stamp = strftime "%Y-%m-%dT%H:%M:%S", gmtime; | |
132 | 132 | "[$stamp $$] $domain $msg" |
133 | 133 | } |
134 | 134 | : error __x"unknown format parameter `{what}'" |
12 | 12 | use File::Basename qw/basename/; |
13 | 13 | |
14 | 14 | my %default_reasonToPrio = |
15 | ( TRACE => LOG_DEBUG | |
16 | , ASSERT => LOG_DEBUG | |
17 | , INFO => LOG_INFO | |
18 | , NOTICE => LOG_NOTICE | |
19 | , WARNING => LOG_WARNING | |
20 | , MISTAKE => LOG_WARNING | |
21 | , ERROR => LOG_ERR | |
22 | , FAULT => LOG_ERR | |
23 | , ALERT => LOG_ALERT | |
24 | , FAILURE => LOG_EMERG | |
25 | , PANIC => LOG_CRIT | |
26 | ); | |
15 | ( TRACE => LOG_DEBUG | |
16 | , ASSERT => LOG_DEBUG | |
17 | , INFO => LOG_INFO | |
18 | , NOTICE => LOG_NOTICE | |
19 | , WARNING => LOG_WARNING | |
20 | , MISTAKE => LOG_WARNING | |
21 | , ERROR => LOG_ERR | |
22 | , FAULT => LOG_ERR | |
23 | , ALERT => LOG_ALERT | |
24 | , FAILURE => LOG_EMERG | |
25 | , PANIC => LOG_CRIT | |
26 | ); | |
27 | 27 | |
28 | 28 | @reasons==keys %default_reasonToPrio |
29 | 29 | or panic __"not all reasons have a default translation"; |
148 | 148 | $self->SUPER::close; |
149 | 149 | } |
150 | 150 | |
151 | #-------------- | |
151 | 152 | =section Accessors |
153 | =cut | |
152 | 154 | |
155 | #-------------- | |
153 | 156 | =section Logging |
154 | 157 | =cut |
155 | 158 |
35 | 35 | if($@->success) { # no errors # } |
36 | 36 | |
37 | 37 | try { # something causes an error report, which is caught |
38 | report {to => 'stderr'}, FAILURE => 'no network'; | |
38 | failure 'no network'; | |
39 | 39 | }; |
40 | 40 | $@->reportFatal(to => 'syslog'); # overrule destination |
41 | 41 |
343 | 343 | do { $sub = (caller $nest++)[3] } |
344 | 344 | while(defined $sub && $sub ne 'Log::Report::report'); |
345 | 345 | defined $sub or $nest = 1; # not found |
346 | ||
347 | # skip syntax==SHORT routine entries | |
348 | # $nest++ if defined $sub && $sub =~ m/^Log\:\:Report\:\:/; | |
349 | 346 | |
350 | 347 | # special trick by Perl for Carp::Heavy: adds @DB::args |
351 | 348 | { package DB; # non-blank before package to avoid problem with OODoc |
5 | 5 | use Log::Report 'log-report'; |
6 | 6 | use Log::Report::Util qw/is_fatal/; |
7 | 7 | use POSIX qw/locale_h/; |
8 | use Scalar::Util qw/blessed/; | |
8 | 9 | |
9 | 10 | =chapter NAME |
10 | 11 | Log::Report::Exception - a collected report |
100 | 101 | { my $self = shift; |
101 | 102 | @_ or return $self->{message}; |
102 | 103 | my $msg = shift; |
103 | UNIVERSAL::isa($msg, 'Log::Report::Message') | |
104 | or panic __x"message() of exception expects Log::Report::Message"; | |
104 | blessed $msg && $msg->isa('Log::Report::Message') | |
105 | or panic "message() of exception expects Log::Report::Message"; | |
105 | 106 | $self->{message} = $msg; |
106 | 107 | } |
107 | 108 |
337 | 337 | |
338 | 338 | sub valueOf($) { $_[0]->{$_[1]} } |
339 | 339 | |
340 | #-------------- | |
340 | 341 | =section Processing |
341 | 342 | |
342 | 343 | =method inClass $class|Regexp |
10 | 10 | use Data::Dumper; |
11 | 11 | |
12 | 12 | BEGIN |
13 | { eval "require Mojo::Base"; | |
14 | plan skip_all => 'Mojo is not installed' | |
13 | { eval "require Mojolicious"; | |
14 | plan skip_all => 'Mojolicious is not installed' | |
15 | 15 | if $@; |
16 | 16 | |
17 | plan skip_all => 'installed Mojolicious too old (requires 2.16)' | |
18 | if $Mojolicious::VERSION < 2.16; | |
17 | 19 | plan tests => 7; |
18 | 20 | } |
19 | 21 |