File Coverage

blib/lib/MojoX/Log/Dispatch/Simple.pm
Criterion Covered Total %
statement 17 69 24.6
branch 0 8 0.0
condition 0 8 0.0
subroutine 6 42 14.2
pod 3 32 9.3
total 26 159 16.3


line stmt bran cond sub pod time code
1             package MojoX::Log::Dispatch::Simple;
2             # ABSTRACT: Simple Log::Dispatch replacement of Mojo::Log
3              
4 1     1   474981 use 5.010;
  1         11  
5 1     1   6 use strict;
  1         2  
  1         22  
6 1     1   5 use warnings;
  1         3  
  1         28  
7              
8 1     1   508 use Mojo::Base 'Mojo::EventEmitter';
  1         164853  
  1         7  
9 1     1   1632 use Mojo::Util 'encode';
  1         3  
  1         1805  
10              
11             our $VERSION = '1.11'; # VERSION
12              
13             has history => sub { [] };
14             has level => 'debug';
15             has max_history_size => 10;
16             has dispatch => undef;
17             has format_cb => undef;
18             has parent => undef;
19              
20             has 'path';
21             has color => sub { $ENV{MOJO_LOG_COLOR} };
22             has short => sub { $ENV{MOJO_LOG_SHORT} };
23             has handle => sub {
24             return \*STDERR unless my $path = shift->path;
25             return Mojo::File->new($path)->open('>>');
26             };
27              
28             sub new {
29 1     1 1 171 my $self = shift->SUPER::new(@_);
30             $self->on( message => sub {
31 0     0   0 my ( $self, $level ) = ( shift, shift );
32 0 0       0 return unless ( $self->_active_level($level) );
33              
34 0         0 push( @{ $self->history }, [ time, $level, @_ ] );
  0         0  
35 0         0 shift @{ $self->history } while ( @{ $self->history } > $self->max_history_size );
  0         0  
  0         0  
36              
37             ( $self->parent // $self )->dispatch->log(
38             level => $level,
39             message => encode( 'UTF-8', $_ ),
40 0   0     0 ) for (@_);
41 1         22 } );
42 1         14 return $self;
43             }
44              
45             sub _log {
46 0     0     my ( $self, $level, @input ) = @_;
47 0 0         $self->emit( 'message', $level, ref $input[0] eq 'CODE' ? $input[0]() : @input );
48             }
49              
50             {
51             my $levels = {
52             debug => 1,
53             info => 2,
54             warn => 3,
55             error => 4,
56             fatal => 5,
57              
58             notice => 2,
59             warning => 3,
60             critical => 4,
61             alert => 5,
62             emergency => 5,
63             emerg => 5,
64              
65             err => 4,
66             crit => 4,
67             };
68              
69             sub _active_level {
70 0     0     my ( $self, $level ) = @_;
71 0 0 0       return ( $levels->{$level} >= $levels->{ $ENV{MOJO_LOG_LEVEL} || $self->level } ) ? 1 : 0;
72             }
73              
74             sub helpers {
75 0     0 1   my ( $self, $c ) = ( shift, shift );
76              
77 0 0         for my $level ( (@_) ? @_ : keys %$levels ) {
78             $c->helper( $level => sub {
79 0     0     my ($self) = shift;
80 0           $self->app->log->$level($_) for (@_);
81 0           return;
82 0           } );
83             }
84              
85 0           return $self;
86             }
87             }
88              
89             sub context {
90 0     0 0   my ( $self, $str ) = @_;
91 0           return $self->new( parent => $self, context => $str, level => $self->level );
92             }
93              
94             sub format {
95 0     0 0   my ($self) = @_;
96 0   0 0     return $self->format_cb || sub { localtime(shift) . ' [' . shift() . '] ' . join( "\n", @_, '' ) };
  0            
97             }
98              
99 0     0 0   sub debug { shift->_log( 'debug', @_ ) }
100 0     0 0   sub info { shift->_log( 'info', @_ ) }
101 0     0 0   sub warn { shift->_log( 'warning', @_ ) }
102 0     0 1   sub error { shift->_log( 'error', @_ ) }
103 0     0 0   sub fatal { shift->_log( 'emergency', @_ ) }
104              
105 0     0 0   sub notice { shift->_log( 'notice', @_ ) }
106 0     0 0   sub warning { shift->_log( 'warning', @_ ) }
107 0     0 0   sub critical { shift->_log( 'critical', @_ ) }
108 0     0 0   sub alert { shift->_log( 'alert', @_ ) }
109 0     0 0   sub emergency { shift->_log( 'emergency', @_ ) }
110 0     0 0   sub emerg { shift->_log( 'emergency', @_ ) }
111              
112 0     0 0   sub err { shift->_log( 'error', @_ ) }
113 0     0 0   sub crit { shift->_log( 'critical', @_ ) }
114              
115 0     0 0   sub is_debug { shift->_active_level('debug') }
116 0     0 0   sub is_info { shift->_active_level('info') }
117 0     0 0   sub is_warn { shift->_active_level('warn') }
118 0     0 0   sub is_error { shift->_active_level('error') }
119 0     0 0   sub is_fatal { shift->_active_level('fatal') }
120              
121 0     0 0   sub is_notice { shift->_active_level('notice') }
122 0     0 0   sub is_warning { shift->_active_level('warning') }
123 0     0 0   sub is_critical { shift->_active_level('critical') }
124 0     0 0   sub is_alert { shift->_active_level('alert') }
125 0     0 0   sub is_emergency { shift->_active_level('emergency') }
126 0     0 0   sub is_emerg { shift->_active_level('emergency') }
127              
128 0     0 0   sub is_err { shift->_active_level('err') }
129 0     0 0   sub is_crit { shift->_active_level('crit') }
130              
131 0     0 0   sub trace { shift->_log( 'debug', @_ ) }
132 0     0 0   sub is_level { shift->_active_level(pop) }
133              
134             1;
135              
136             =pod
137              
138             =encoding UTF-8
139              
140             =head1 NAME
141              
142             MojoX::Log::Dispatch::Simple - Simple Log::Dispatch replacement of Mojo::Log
143              
144             =head1 VERSION
145              
146             version 1.11
147              
148             =for markdown [![test](https://github.com/gryphonshafer/MojoX-Log-Dispatch-Simple/workflows/test/badge.svg)](https://github.com/gryphonshafer/MojoX-Log-Dispatch-Simple/actions?query=workflow%3Atest)
149             [![codecov](https://codecov.io/gh/gryphonshafer/MojoX-Log-Dispatch-Simple/graph/badge.svg)](https://codecov.io/gh/gryphonshafer/MojoX-Log-Dispatch-Simple)
150              
151             =head1 SYNOPSIS
152              
153             # from inside your startup() most likely...
154              
155             use Log::Dispatch;
156             use MojoX::Log::Dispatch::Simple;
157              
158             my $mojo_logger = MojoX::Log::Dispatch::Simple->new(
159             dispatch => Log::Dispatch->new,
160             level => 'debug'
161             );
162              
163             my ($self) = @_; # Mojolicious object from inside startup()
164             $self->log($mojo_logger);
165              
166             # ...then later inside a controller...
167              
168             $self->app->log->debug('Debug-level message');
169             $self->app->log->info('Info-level message');
170              
171             # ...or back to your startup() to setup some helpers...
172              
173             $mojo_logger->helpers($self);
174             $mojo_logger->helpers( $self, qw( debug info warn error ) );
175              
176             # ...so that in your controllers you can...
177              
178             $self->debug('Debug-level message');
179             $self->info('Info-level message');
180              
181             # ...or do it all at once, in the startup() most likely...
182              
183             $self->log( MojoX::Log::Dispatch::Simple->new(
184             dispatch => Log::Dispatch->new,
185             level => 'debug'
186             )->helpers($self) );
187              
188             =head1 DESCRIPTION
189              
190             This module provides a really simple way to replace the built-in L<Mojo::Log>
191             with a L<Log::Dispatch> object, and yet still support all the L<Mojo::Log>
192             log levels and other functionality L<Mojolicious> assumes exists. To make it
193             even easier, you can install helpers to all the log levels, all from the same
194             single line of code.
195              
196             $self->log( MojoX::Log::Dispatch::Simple->new(
197             dispatch => Log::Dispatch->new,
198             level => 'debug'
199             )->helpers($self) );
200              
201             The module tries not to make any assumptions about how you want to use
202             L<Log::Dispatch>. In fact, you can if desired use an alternate L<Log::Dispatch>
203             library so long as it offers a similar interface.
204              
205             =head1 PRIMARY METHODS
206              
207             These are methods that you would likely use from within your L<Mojolicious>
208             C<startup()> subroutine.
209              
210             =head2 new
211              
212             This method instantiates an object. It requires a "dispatch" parameter, which
213             should be a L<Log::Dispatch> object (or an object with a similar signature).
214             The method allow accepts an optional "level" parameter, which is used to set
215             the log level for your L<Mojolicious> application.
216              
217             my $mojo_logger = MojoX::Log::Dispatch::Simple->new(
218             dispatch => Log::Dispatch->new,
219             level => 'debug'
220             );
221              
222             Optionally, you can also provide a "format_cb" value, which should be a
223             reference to a subroutine that will be used to provide custom formatting to
224             entries that appear on the L<Mojolicious> error reporting web page. This
225             formatting will have nothing at all to do with whatever your L<Log::Dispatch>
226             does; it only formats log entries that appear on the L<Mojolicious> error
227             reporting web page.
228              
229             my $mojo_logger = MojoX::Log::Dispatch::Simple->new(
230             dispatch => Log::Dispatch->new,
231             level => 'debug',
232             format_cb => sub {
233             localtime(shift) . ' [' . shift() . '] ' . join( "\n", @_, '' )
234             },
235             );
236              
237             By default, when you're looking at one of these L<Mojolicious> error reporting
238             web pages, you'll see the past 10 log entries listed. You can change that
239             by passing in a "max_history_size" value.
240              
241             my $mojo_logger = MojoX::Log::Dispatch::Simple->new(
242             dispatch => Log::Dispatch->new,
243             max_history_size => 20,
244             );
245              
246             =head2 helpers
247              
248             You can optionally tell this library to create helpers to each of the log
249             levels, or to a selection of them. This method requires that you pass in
250             a reference to the L<Mojolicious> object. If that's all you pass in, the
251             method will create a helper for every log level.
252              
253             # from inside your startup()...
254             $mojo_logger->helpers($mojo_obj);
255              
256             # now later from inside a controller...
257             $c->debug('Debug message');
258              
259             $c->app->log->debug("This is what you'd have to type without the helper");
260              
261             You can optionally pass in the names of the log levels you want helpers created
262             for, and the method will only create methods for those levels.
263              
264             $mojo_logger->helpers( $mojo_obj, qw( debug info warn ) );
265              
266             =head1 LOG LEVELS
267              
268             Unfortunately, L<Mojolicious> and L<Log::Dispatch> have somewhat different
269             ideas as to what log levels should exist. Since this module is a bridge between
270             them, it attempts to support all levels from both sides. That being said, when
271             calling log levels in your application, you will probably want to only use
272             the log levels from L<Log::Dispatch> if you use your L<Log::Dispatch> code
273             in non-Mojo-app areas of your ecosystem, thus keeping things uniform everywhere.
274              
275             For the purposes of understanding log levels relative to each other, all log
276             levels are assigned a "rank" value. Since L<Mojolicious> has fewer levels than
277             L<Log::Dispatch> and there are 5 of them, a level's "rank" is an integer
278             between 1 and 5.
279              
280             =head2 Log::Dispatch Log Levels
281              
282             The following are L<Log::Dispatch> log levels along with their corresponding
283             "rank" integer and any supported aliases:
284              
285             =over 4
286              
287             =item *
288              
289             debug (1)
290              
291             =item *
292              
293             info (2)
294              
295             =item *
296              
297             notice (2)
298              
299             =item *
300              
301             warning, warn (3)
302              
303             =item *
304              
305             error, err (4)
306              
307             =item *
308              
309             critical, crit (4)
310              
311             =item *
312              
313             alert (5)
314              
315             =item *
316              
317             emergency, emerg (5)
318              
319             =back
320              
321             =head2 Mojolicious Log Levels
322              
323             The following are L<Mojolicious> log levels along with their corresponding
324             "rank" integer and any supported aliases:
325              
326             =over 4
327              
328             =item *
329              
330             debug (1)
331              
332             =item *
333              
334             info (2)
335              
336             =item *
337              
338             warn (3)
339              
340             =item *
341              
342             error (4)
343              
344             =item *
345              
346             fatal (5)
347              
348             =back
349              
350             You can check what log level you're set at by either just reading C<$obj->level>
351             or by running an "is_*" method. For every log level, there's a corresponding
352             "is_*" method.
353              
354             my $log_level_at_or_above_notice = $obj->is_notice;
355              
356             Note that this gets somewhat confusing when dealing with L<Log::Dispatch> log
357             levels because from the perspective of L<Log::Dispatch>, the "notice" level is
358             a unique level that's lower than a "warning" and higher than the "info" level.
359             However, from the perspective of L<Mojolicious>, there's no such log level.
360             It will assume you're set at the "info" log level. Ergo, if you call
361             C<is_notice()> or C<is_info()>, you'll get the same result.
362              
363             =head1 POST-INSTANTIATION MEDDLING
364              
365             Following the creation of the object from this library, you can still
366             manipulate various attributes, which are:
367              
368             =over 4
369              
370             =item *
371              
372             dispatch (a L<Log::Dispatch> object)
373              
374             =item *
375              
376             level
377              
378             =item *
379              
380             max_history_size
381              
382             =item *
383              
384             format_cb (a subref)
385              
386             =item *
387              
388             history (an arrayref)
389              
390             =back
391              
392             So you can do things like:
393              
394             $obj->dispatch->remove('debug');
395              
396             This also means you can manipulate the log history. Why you'd ever want to do
397             that, I can't say; but you can. Freedom is messy.
398              
399             =head1 SEE ALSO
400              
401             L<Mojolicious>, L<Log::Dispatch>.
402              
403             You can also look for additional information at:
404              
405             =over 4
406              
407             =item *
408              
409             L<GitHub|https://github.com/gryphonshafer/MojoX-Log-Dispatch-Simple>
410              
411             =item *
412              
413             L<MetaCPAN|https://metacpan.org/pod/MojoX::Log::Dispatch::Simple>
414              
415             =item *
416              
417             L<GitHub Actions|https://github.com/gryphonshafer/MojoX-Log-Dispatch-Simple/actions>
418              
419             =item *
420              
421             L<Codecov|https://codecov.io/gh/gryphonshafer/MojoX-Log-Dispatch-Simple>
422              
423             =item *
424              
425             L<CPANTS|http://cpants.cpanauthors.org/dist/MojoX-Log-Dispatch-Simple>
426              
427             =item *
428              
429             L<CPAN Testers|http://www.cpantesters.org/distro/M/MojoX-Log-Dispatch-Simple.html>
430              
431             =back
432              
433             =for Pod::Coverage alert crit critical debug emerg emergency err fatal format info is_alert is_crit is_critical is_debug is_emerg is_emergency is_err is_error is_fatal is_info is_notice is_warn is_warning notice warn warning context is_level trace
434              
435             =head1 GRATITUDE
436              
437             Special thanks to the following for contributing to this module:
438              
439             =over 4
440              
441             =item *
442              
443             Tomohiro Hosaka
444              
445             =back
446              
447             =head1 AUTHOR
448              
449             Gryphon Shafer <gryphon@cpan.org>
450              
451             =head1 COPYRIGHT AND LICENSE
452              
453             This software is Copyright (c) 2015-2021 by Gryphon Shafer.
454              
455             This is free software, licensed under:
456              
457             The Artistic License 2.0 (GPL Compatible)
458              
459             =cut
460              
461             __END__ MojoX::Log::Dispatch::Simple MojoX-Log-Dispatch-Simple
462