File Coverage

blib/lib/Dancer2/Core/Role/Logger.pm
Criterion Covered Total %
statement 60 70 85.7
branch 20 32 62.5
condition 2 11 18.1
subroutine 25 30 83.3
pod 6 8 75.0
total 113 151 74.8


line stmt bran cond sub pod time code
1             # ABSTRACT: Role for logger engines
2             $Dancer2::Core::Role::Logger::VERSION = '0.400000';
3             use Dancer2::Core::Types;
4 139     139   95292  
  139         315  
  139         1299  
5             use Moo::Role;
6 139     139   1141946 use POSIX 'strftime';
  139         379  
  139         1269  
7 139     139   212971 use Encode ();
  139         803711  
  139         1001  
8 139     139   180640 use Data::Dumper;
  139         63433  
  139         2791  
9 139     139   3470  
  139         26683  
  139         160967  
10             with 'Dancer2::Core::Role::Engine';
11              
12             qw(
13 102     102 0 297 engine.logger.before
14             engine.logger.after
15 35     35 0 303 );
16             }
17              
18              
19             # This is the only method to implement by logger engines.
20             # It receives the following arguments:
21 0     0   0 # $msg_level, $msg_content, it gets called only if the configuration allows
22             # a message of the given level to be logged.
23             requires 'log';
24              
25             has auto_encoding_charset => (
26             is => 'ro',
27             isa => Str,
28             );
29              
30             has app_name => (
31             is => 'ro',
32             isa => Str,
33             default => sub {'-'},
34             );
35              
36             has log_format => (
37             is => 'rw',
38             isa => Str,
39             default => sub {'[%a:%P] %L @%T> %m in %f l. %l'},
40             );
41              
42             my $_levels = {
43              
44             # levels < 0 are for core only
45             core => -10,
46              
47             # levels > 0 are for end-users only
48             debug => 1,
49             info => 2,
50             warn => 3,
51             warning => 3,
52             error => 4,
53             };
54              
55             has log_level => (
56             is => 'rw',
57             isa => Enum[keys %{$_levels}],
58             default => sub {'debug'},
59             );
60              
61             my ( $self, $msg_level ) = @_;
62             my $conf_level = $self->log_level;
63             return $_levels->{$conf_level} <= $_levels->{$msg_level};
64             }
65              
66 2318     2318   4255 my ( $self, $level, $message ) = @_;
67 2318         32493 chomp $message;
68 2318         23506  
69             $message = Encode::encode( $self->auto_encoding_charset, $message )
70             if $self->auto_encoding_charset;
71              
72 52     52 1 1783 my @stack = caller(8);
73 52         152 my $request = $self->request;
74             my $config = $self->config;
75 52 50       224  
76             my $block_handler = sub {
77             my ( $block, $type ) = @_;
78 52         619 if ( $type eq 't' ) {
79 52         170 return POSIX::strftime( $block, localtime(time) );
80 52         161 }
81             elsif ( $type eq 'h' ) {
82             return ( $request && $request->header($block) ) || '-';
83 4     4   10 }
84 4 50       13 else {
    50          
85 0         0 Carp::carp("{$block}$type not supported");
86             return "-";
87             }
88 4   50     20 };
89              
90             my $chars_mapping = {
91 0         0 a => sub { $self->app_name },
92 0         0 t => sub { POSIX::strftime( "%d/%b/%Y %H:%M:%S", localtime(time) ) },
93             T => sub { POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime(time) ) },
94 52         357 u => sub { POSIX::strftime( "%d/%b/%Y %H:%M:%S", gmtime(time) ) },
95             U => sub { POSIX::strftime( "%Y-%m-%d %H:%M:%S", gmtime(time) ) },
96             P => sub {$$},
97 48     48   305 L => sub {$level},
98 0     0   0 m => sub {$message},
99 48     48   3138 f => sub { $stack[1] || '-' },
100 0     0   0 l => sub { $stack[2] || '-' },
101 0     0   0 h => sub {
102 48     48   244 ( $request && ( $request->remote_host || $request->address ) ) || '-'
103 48     48   175 },
104 48     48   232 i => sub { ( $request && $request->id ) || '-' },
105 48 100   48   330 };
106 48 100   48   290  
107             my $char_mapping = sub {
108 0 0 0 0   0 my $char = shift;
      0        
109              
110 4 50 33 4   24 my $cb = $chars_mapping->{$char};
111 52         1403 if ( !$cb ) {
112             Carp::carp "%$char not supported.";
113             return "-";
114 340     340   589 }
115             $cb->($char);
116 340         471 };
117 340 50       614  
118 0         0 my $fmt = $self->log_format;
119 0         0  
120             $fmt =~ s/
121 340         601 (?:
122 52         220 \%\{(.+?)\}([a-z])|
123             \%([a-zA-Z])
124 52         1055 )
125             / $1 ? $block_handler->($1, $2) : $char_mapping->($3) /egx;
126 52         655  
127             return $fmt . "\n";
128             }
129              
130             my @vars = @_;
131 344 100       1633  
132             return join q{}, map +(
133 52         1824 ref $_
134             ? Data::Dumper->new( [$_] )->Terse(1)->Purity(1)->Indent(0)
135             ->Sortkeys(1)->Dump()
136             : ( defined($_) ? $_ : 'undef' )
137 46     46   129 ), @vars;
138             }
139 46 50       1080  
    50          
140             around 'log' => sub {
141             my ($orig, $self, @args) = @_;
142              
143             $self->execute_hook( 'engine.logger.before', $self, @args );
144             $self->$orig( @args );
145             $self->execute_hook( 'engine.logger.after', $self, @args );
146             };
147              
148             my ( $self, @args ) = @_;
149             $self->_should('core') and $self->log( 'core', _serialize(@args) );
150             }
151              
152             my ( $self, @args ) = @_;
153             $self->_should('debug') and $self->log( 'debug', _serialize(@args) );
154             }
155              
156 2268     2268 1 6236 my ( $self, @args ) = @_;
157 2268 100       6051 $self->_should('info') and $self->log( 'info', _serialize(@args) );
158             }
159              
160             my ( $self, @args ) = @_;
161 12     12 1 3581 $self->_should('warning') and $self->log( 'warning', _serialize(@args) );
162 12 100       32 }
163              
164             my ( $self, @args ) = @_;
165             $self->_should('error') and $self->log( 'error', _serialize(@args) );
166 2     2 1 1571 }
167 2 50       8  
168             1;
169              
170              
171 9     9 1 1573 =pod
172 9 50       46  
173             =encoding UTF-8
174              
175             =head1 NAME
176 27     27 1 1665  
177 27 50       83 Dancer2::Core::Role::Logger - Role for logger engines
178              
179             =head1 VERSION
180              
181             version 0.400000
182              
183             =head1 DESCRIPTION
184              
185             Any class that consumes this role will be able to implement to write log messages.
186              
187             In order to implement this role, the consumer B<must> implement the C<log>
188             method. This method will receives as argument the C<level> and the C<message>.
189              
190             =head1 ATTRIBUTES
191              
192             =head2 auto_encoding_charset
193              
194             Charset to use when writing a message.
195              
196             =head2 app_name
197              
198             Name of the application. Can be used in the message.
199              
200             =head2 log_format
201              
202             This is a format string (or a preset name) to specify the log format.
203              
204             The possible values are:
205              
206             =over 4
207              
208             =item %h
209              
210             host emitting the request
211              
212             =item %t
213              
214             date (local timezone, formatted like %d/%b/%Y %H:%M:%S)
215              
216             =item %T
217              
218             date (local timezone, formatted like %Y-%m-%d %H:%M:%S)
219              
220             =item %u
221              
222             date (UTC timezone, formatted like %d/%b/%Y %H:%M:%S)
223              
224             =item %U
225              
226             date (UTC timezone, formatted like %Y-%m-%d %H:%M:%S)
227              
228             =item %P
229              
230             PID
231              
232             =item %L
233              
234             log level
235              
236             =item %D
237              
238             timer
239              
240             =item %m
241              
242             message
243              
244             =item %f
245              
246             file name that emit the message
247              
248             =item %l
249              
250             line from the file
251              
252             =item %i
253              
254             request ID
255              
256             =item %{$fmt}t
257              
258             timer formatted with a valid time format
259              
260             =item %{header}h
261              
262             header value
263              
264             =back
265              
266             =head2 log_level
267              
268             Level to use by default.
269              
270             =head1 METHODS
271              
272             =head2 core
273              
274             Log messages as B<core>.
275              
276             =head2 debug
277              
278             Log messages as B<debug>.
279              
280             =head2 info
281              
282             Log messages as B<info>.
283              
284             =head2 warning
285              
286             Log messages as B<warning>.
287              
288             =head2 error
289              
290             Log messages as B<error>.
291              
292             =head2 format_message
293              
294             Provides a common message formatting.
295              
296             =head1 CONFIGURATION
297              
298             The B<logger> configuration variable tells Dancer2 which engine to use.
299              
300             You can change it either in your config.yml file:
301              
302             # logging to console
303             logger: "console"
304              
305             The log format can also be configured,
306             please see L<Dancer2::Core::Role::Logger/"log_format"> for details.
307              
308             =head1 AUTHOR
309              
310             Dancer Core Developers
311              
312             =head1 COPYRIGHT AND LICENSE
313              
314             This software is copyright (c) 2022 by Alexis Sukrieh.
315              
316             This is free software; you can redistribute it and/or modify it under
317             the same terms as the Perl 5 programming language system itself.
318              
319             =cut