File Coverage

blib/lib/Dancer2/Core/Role/Logger.pm
Criterion Covered Total %
statement 63 74 85.1
branch 20 34 58.8
condition 3 13 23.0
subroutine 26 32 81.2
pod 7 9 77.7
total 119 162 73.4


line stmt bran cond sub pod time code
1             package Dancer2::Core::Role::Logger;
2             # ABSTRACT: Role for logger engines
3             $Dancer2::Core::Role::Logger::VERSION = '2.1.0';
4 133     133   625634 use Dancer2::Core::Types;
  133         359  
  133         1760  
5              
6 133     133   2063318 use Moo::Role;
  133         20619  
  133         1617  
7 133     133   282023 use POSIX 'strftime';
  133         1088957  
  133         984  
8 133     133   253049 use Encode ();
  133         61172  
  133         4050  
9 133     133   9548 use Data::Dumper;
  133         115200  
  133         225264  
10              
11             with 'Dancer2::Core::Role::Engine';
12              
13 116     116 0 422 sub hook_aliases { +{} }
14             sub supported_hooks {
15 36     36 0 302 qw(
16             engine.logger.before
17             engine.logger.after
18             );
19             }
20              
21 0     0   0 sub _build_type {'Logger'}
22              
23             # This is the only method to implement by logger engines.
24             # It receives the following arguments:
25             # $msg_level, $msg_content, it gets called only if the configuration allows
26             # a message of the given level to be logged.
27             requires 'log';
28              
29             has auto_encoding_charset => (
30             is => 'ro',
31             isa => Str,
32             );
33              
34             has app_name => (
35             is => 'ro',
36             isa => Str,
37             default => sub {'-'},
38             );
39              
40             has log_format => (
41             is => 'rw',
42             isa => Str,
43             default => sub {'[%a:%P] %L @%T> %m in %f l. %l'},
44             );
45              
46             has caller_stack_size => (
47             is => 'rw',
48             isa => Int,
49             default => sub { 9; },
50             );
51              
52             my $_levels = {
53              
54             # levels < 0 are for core only
55             core => -10,
56              
57             # levels > 0 are for end-users only
58             debug => 1,
59             info => 2,
60             warn => 3,
61             warning => 3,
62             error => 4,
63             };
64              
65             has log_level => (
66             is => 'rw',
67             isa => Enum[keys %{$_levels}],
68             default => sub {'debug'},
69             );
70              
71             sub _should {
72 2841     2841   5992 my ( $self, $msg_level ) = @_;
73 2841         57466 my $conf_level = $self->log_level;
74 2841         30439 return $_levels->{$conf_level} <= $_levels->{$msg_level};
75             }
76              
77             sub format_message {
78 58     58 1 4648 my ( $self, $level, $message ) = @_;
79 58         173 chomp $message;
80              
81 58 50       338 $message = Encode::encode( $self->auto_encoding_charset, $message )
82             if $self->auto_encoding_charset;
83              
84 58         231 my $request = $self->request;
85 58         238 my $config = $self->config;
86              
87             my $block_handler = sub {
88 5     5   29 my ( $block, $type ) = @_;
89 5 50       22 if ( $type eq 't' ) {
    50          
90 0         0 return POSIX::strftime( $block, localtime(time) );
91             }
92             elsif ( $type eq 'h' ) {
93 5   50     50 return ( $request && $request->header($block) ) || '-';
94             }
95             else {
96 0         0 Carp::carp("{$block}$type not supported");
97 0         0 return "-";
98             }
99 58         445 };
100              
101 58         247 my $chars_mapping = $self->map_chars_to_subs($level, $message, );
102              
103             my $char_mapping = sub {
104 376     376   698 my $char = shift;
105              
106 376         598 my $cb = $chars_mapping->{$char};
107 376 50       796 if ( !$cb ) {
108 0         0 Carp::carp "%$char not supported.";
109 0         0 return "-";
110             }
111 376         779 $cb->($char);
112 58         340 };
113              
114 58         1806 my $fmt = $self->log_format;
115              
116 58         954 $fmt =~ s/
117             (?:
118             \%\{(.+?)\}([a-z])|
119             \%([a-zA-Z])
120             )
121 381 100       2084 / $1 ? $block_handler->($1, $2) : $char_mapping->($3) /egx;
122              
123 58         2999 return $fmt . "\n";
124             }
125              
126             sub map_chars_to_subs {
127 58     58 1 224 my ( $self, $level, $message, $caller_delta ) = @_;
128 58   50     1408 my @stack = caller($self->caller_stack_size + ($caller_delta // 0));
129 58         1176 my $request = $self->request;
130             return {
131 53     53   377 a => sub { $self->app_name },
132 0     0   0 t => sub { POSIX::strftime( "%d/%b/%Y %H:%M:%S", localtime(time) ) },
133 53     53   3007 T => sub { POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime(time) ) },
134 0     0   0 u => sub { POSIX::strftime( "%d/%b/%Y %H:%M:%S", gmtime(time) ) },
135 0     0   0 U => sub { POSIX::strftime( "%Y-%m-%d %H:%M:%S", gmtime(time) ) },
136 53     53   590 P => sub {$$},
137 53     53   251 L => sub {$level},
138 53     53   242 m => sub {$message},
139 0 0   0   0 p => sub { $stack[0] || '-' }, # package
140 53 100   53   327 f => sub { $stack[1] || '-' }, # filepath
141 53 100   53   305 l => sub { $stack[2] || '-' }, # line number
142             h => sub {
143 0 0 0 0   0 ( $request && ( $request->remote_host || $request->address ) ) || '-'
      0        
144             },
145 5 50 33 5   41 i => sub { ( $request && $request->id ) || '-' },
146 58         3833 };
147             };
148              
149             sub _serialize {
150 53     53   185 my @vars = @_;
151              
152 53 50       1708 return join q{}, map +(
    50          
153             ref $_
154             ? Data::Dumper->new( [$_] )->Terse(1)->Purity(1)->Indent(0)
155             ->Sortkeys(1)->Dump()
156             : ( defined($_) ? $_ : 'undef' )
157             ), @vars;
158             }
159              
160             around 'log' => sub {
161             my ($orig, $self, @args) = @_;
162              
163             $self->execute_hook( 'engine.logger.before', $self, @args );
164             $self->$orig( @args );
165             $self->execute_hook( 'engine.logger.after', $self, @args );
166             };
167              
168             sub core {
169 2790     2790 1 7996 my ( $self, @args ) = @_;
170 2790 100       8480 $self->_should('core') and $self->log( 'core', _serialize(@args) );
171             }
172              
173             sub debug {
174 12     12 1 4115 my ( $self, @args ) = @_;
175 12 100       81 $self->_should('debug') and $self->log( 'debug', _serialize(@args) );
176             }
177              
178             sub info {
179 2     2 1 1838 my ( $self, @args ) = @_;
180 2 50       10 $self->_should('info') and $self->log( 'info', _serialize(@args) );
181             }
182              
183             sub warning {
184 9     9 1 1999 my ( $self, @args ) = @_;
185 9 50       38 $self->_should('warning') and $self->log( 'warning', _serialize(@args) );
186             }
187              
188             sub error {
189 28     28 1 2085 my ( $self, @args ) = @_;
190 28 50       122 $self->_should('error') and $self->log( 'error', _serialize(@args) );
191             }
192              
193             1;
194              
195             __END__