File Coverage

blib/lib/Log/ger/Heavy.pm
Criterion Covered Total %
statement 142 177 80.2
branch 60 104 57.6
condition 9 45 20.0
subroutine 43 81 53.0
pod 0 2 0.0
total 254 409 62.1


line stmt bran cond sub pod time code
1             package Log::ger::Heavy;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-03-11'; # DATE
5             our $DIST = 'Log-ger'; # DIST
6             our $VERSION = '0.037'; # VERSION
7              
8             #IFUNBUILT
9             # use strict;
10             # use warnings;
11             #END IFUNBUILT
12              
13             package
14             Log::ger;
15              
16             #IFUNBUILT
17             # use vars qw(
18             # $re_addr
19             # %Levels
20             # %Level_Aliases
21             # $Current_Level
22             # $_outputter_is_null
23             # $_dumper
24             # %Global_Hooks
25             # %Package_Targets
26             # %Per_Package_Hooks
27             # %Hash_Targets
28             # %Per_Hash_Hooks
29             # %Object_Targets
30             # %Per_Object_Hooks
31             # );
32             #END IFUNBUILT
33              
34             # key = phase, value = [ [key, prio, coderef], ... ]
35             our %Default_Hooks = (
36             create_filter => [],
37              
38             create_formatter => [
39             [__PACKAGE__, 90,
40             sub {
41             my %args = @_;
42              
43             # BEGIN_BLOCK: default_formatter
44              
45             my $formatter =
46              
47             # the default formatter is sprintf-style that dumps data
48             # structures arguments as well as undef as ''.
49             sub {
50             return $_[0] if @_ < 2;
51             my $fmt = shift;
52             my @args;
53             for (@_) {
54             if (!defined($_)) {
55             push @args, '';
56             } elsif (ref $_) {
57             require Log::ger::Util unless $Log::ger::_dumper;
58             push @args, Log::ger::Util::_dump($_);
59             } else {
60             push @args, $_;
61             }
62             }
63             # redefine is just a dummy category for perls < 5.22 which
64             # don't have 'redundant' yet
65 8 50   20   48 no warnings ($warnings::Bits{'redundant'} ? 'redundant' : 'redefine');
  8         13  
  8         14424  
66             sprintf $fmt, @args;
67             };
68              
69             [$formatter];
70              
71             # END_BLOCK: default_formatter
72              
73             }],
74             ],
75              
76             create_layouter => [],
77              
78             create_routine_names => [
79             [__PACKAGE__, 90,
80             # the default names are log_LEVEL() and log_is_LEVEL() for subroutine
81             # names, or LEVEL() and is_LEVEL() for method names
82             sub {
83             my %args = @_;
84              
85             my $levels = [keys %Levels];
86              
87             return [{
88             logger_subs => [map { ["log_$_", $_] } @$levels],
89             level_checker_subs => [map { ["log_is_$_", $_] } @$levels],
90             # used when installing to hash or object
91             logger_methods => [map { ["$_", $_] } @$levels],
92             level_checker_methods => [map { ["is_$_", $_] } @$levels],
93             }, 1];
94             }],
95             ],
96              
97             # old name for create_outputter, deprecated and will be removed in the
98             # future
99             create_log_routine => [],
100              
101             create_outputter => [
102             [__PACKAGE__, 10,
103             # the default behavior is to create a null routine for levels that are
104             # too high than the global level ($Current_Level). since we run at high
105             # priority (10), we block typical output plugins at normal priority
106             # (50). this is a convenience so normally a plugin does not have to
107             # deal with level checking. plugins that want to do its own level
108             # checking can use a higher priority.
109             sub {
110             my %args = @_;
111             my $level = $args{level};
112             my $num_outputs = 0;
113             $num_outputs += @{ $Global_Hooks{create_log_routine} }; # old name, will be removed
114             $num_outputs += @{ $Global_Hooks{create_outputter} };
115             if ( # level indicates routine should be a null logger
116             (defined $level && $Current_Level < $level) ||
117             # there's only us that produces log routines (e.g. no outputs)
118             $num_outputs == 1
119             ) {
120             $_outputter_is_null = 1;
121 38     38   106 return [sub {0}];
  2     38   6  
        62      
        4      
        2      
        2      
        2      
        2      
        2      
        2      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
122             }
123             [undef]; # decline, let output plugin supply logger routines
124             }],
125             ],
126              
127             # old name for create_level_checker, deprecated and will be removed in the
128             # future
129             create_is_routine => [],
130              
131             create_level_checker => [
132             [__PACKAGE__, 90,
133             # the default behavior is to compare to global level. normally this
134             # behavior suffices. we run at low priority (90) so normal plugins
135             # which typically use priority 50 can override us.
136             sub {
137             my %args = @_;
138             my $level = $args{level};
139 0     12   0 [sub { $Current_Level >= $level }];
  0     12      
        0      
        12      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
140             }],
141             ],
142              
143             before_install_routines => [],
144              
145             after_install_routines => [],
146             );
147              
148             for my $phase (keys %Default_Hooks) {
149             $Global_Hooks{$phase} = [@{ $Default_Hooks{$phase} }];
150             }
151              
152             # if flow_control is 1, stops after the first hook that gives non-undef result.
153             # flow_control can also be a coderef that will be called after each hook with
154             # ($hook, $hook_res) and can return 1 to mean stop.
155             sub run_hooks {
156 1735     1735 0 2401 my ($phase, $hook_args, $flow_control,
157             $target_type, $target_name) = @_;
158             #print "D: running hooks for phase $phase\n";
159              
160 1735 50       2703 $Global_Hooks{$phase} or die "Unknown phase '$phase'";
161 1735         1702 my @hooks = @{ $Global_Hooks{$phase} };
  1735         2522  
162              
163 1735 100       2938 if ($target_type eq 'package') {
    100          
    50          
164 919 100       892 unshift @hooks, @{ $Per_Package_Hooks{$target_name}{$phase} || [] };
  919         2193  
165             } elsif ($target_type eq 'hash') {
166 216         987 my ($addr) = "$target_name" =~ $re_addr;
167 216 50       277 unshift @hooks, @{ $Per_Hash_Hooks{$addr}{$phase} || [] };
  216         617  
168             } elsif ($target_type eq 'object') {
169 600         2798 my ($addr) = "$target_name" =~ $re_addr;
170 600 50       833 unshift @hooks, @{ $Per_Object_Hooks{$addr}{$phase} || [] };
  600         1632  
171             }
172              
173 1735         1986 my $res;
174 1735         2628 for my $hook (sort {$a->[1] <=> $b->[1]} @hooks) {
  386         855  
175 1248         2869 my $hook_res = $hook->[2]->(%$hook_args);
176 1248 100       2403 if (defined $hook_res->[0]) {
177 1017         1128 $res = $hook_res->[0];
178             #print "D: got result from hook $hook->[0]: $res\n";
179 1017 100       1554 if (ref $flow_control eq 'CODE') {
180 152 100       245 last if $flow_control->($hook, $hook_res);
181             } else {
182 865 50       1437 last if $flow_control;
183             }
184             }
185 310 50       600 last if $hook_res->[1];
186             }
187 1735         12888 return $res;
188             }
189              
190             sub init_target {
191 73     73 0 183 my ($target_type, $target_name, $per_target_conf) = @_;
192              
193             #print "D:init_target($target_type, $target_name, ...)\n";
194 73         226 my %hook_args = (
195             target_type => $target_type,
196             target_name => $target_name,
197             per_target_conf => $per_target_conf,
198             );
199              
200             # collect only a single filter
201 73         89 my %filters;
202             run_hooks(
203             'create_filter', \%hook_args,
204             # collect filters, until a hook instructs to stop
205             sub {
206 1     1   2 my ($hook, $hook_res) = @_;
207 1         2 my ($filter, $flow_control, $fltname) = @$hook_res;
208 1 50       2 $fltname = 'default' if !defined($fltname);
209 1   33     7 $filters{$fltname} ||= $filter;
210 1         2 $flow_control;
211             },
212 73         322 $target_type, $target_name);
213              
214 73         235 my %formatters;
215             run_hooks(
216             'create_formatter', \%hook_args,
217             # collect formatters, until a hook instructs to stop
218             sub {
219 77     77   113 my ($hook, $hook_res) = @_;
220 77         116 my ($formatter, $flow_control, $fmtname) = @$hook_res;
221 77 100       135 $fmtname = 'default' if !defined($fmtname);
222 77   66     305 $formatters{$fmtname} ||= $formatter;
223 77         142 $flow_control;
224             },
225 73         253 $target_type, $target_name);
226              
227             # collect only a single layouter
228 73         214 my $layouter =
229             run_hooks(
230             'create_layouter', \%hook_args, 1, $target_type, $target_name);
231              
232 73         111 my $routine_names = {};
233             run_hooks(
234             'create_routine_names', \%hook_args,
235             # collect routine names, until a hook instructs to stop.
236             sub {
237 74     74   130 my ($hook, $hook_res) = @_;
238 74         408 my ($routine_name_rec, $flow_control) = @$hook_res;
239 74 50       123 $routine_name_rec or return;
240 74         189 for (keys %$routine_name_rec) {
241 294         336 push @{ $routine_names->{$_} }, @{ $routine_name_rec->{$_} };
  294         383  
  294         564  
242             }
243 74         204 $flow_control;
244             },
245 73         300 $target_type, $target_name);
246              
247 73         227 my @routines;
248 73         126 my $is_object = $target_type eq 'object';
249              
250             CREATE_LOGGER_ROUTINES:
251             {
252 73         88 my @routine_name_recs;
  73         84  
253 73 100       110 if ($target_type eq 'package') {
254 39 50       47 push @routine_name_recs, @{ $routine_names->{log_subs} || [] }; # old name, will be removed
  39         127  
255 39 50       59 push @routine_name_recs, @{ $routine_names->{logger_subs} || [] };
  39         87  
256             } else {
257 34 50       41 push @routine_name_recs, @{ $routine_names->{log_methods} || [] }; # old name, will be removed
  34         102  
258 34 50       45 push @routine_name_recs, @{ $routine_names->{logger_methods} || [] };
  34         64  
259             }
260             NAME:
261 73         120 for my $routine_name_rec (@routine_name_recs) {
262 433         732 my ($rname, $lname, $fmtname, $rper_target_conf, $fltname)
263             = @$routine_name_rec;
264 433 100       467 my $lnum; $lnum = $Levels{$lname} if defined $lname;
  433         719  
265 433 100       610 $fmtname = 'default' if !defined($fmtname);
266              
267 433         494 my ($output_routine, $logger);
268 433         485 $_outputter_is_null = 0;
269 433         713 local $hook_args{name} = $rname; # compat, deprecated
270 433         590 local $hook_args{routine_name} = $rname;
271 433         526 local $hook_args{level} = $lnum;
272 433         552 local $hook_args{str_level} = $lname;
273 433         436 my $outputter;
274             {
275 433 50       429 $outputter = run_hooks("create_outputter" , \%hook_args, 1, $target_type, $target_name) and last;
  433         671  
276 0         0 $outputter = run_hooks("create_log_routine", \%hook_args, 1, $target_type, $target_name); # old name, will be removed in the future
277             }
278 433 50       647 die "BUG in configuration: No outputter is produced for routine name $rname" unless $outputter;
279              
280             { # enclosing block
281 433 100       435 if ($_outputter_is_null) {
  433         625  
282              
283             # if outputter is a null outputter (sub {0}) we don't need
284             # to format message, layout message, or care about the
285             # logger routine being a subroutine/object. shortcut here
286             # for faster init.
287              
288 208         215 $logger = $outputter;
289 208         219 last;
290             }
291              
292 225         275 my $formatter = $formatters{$fmtname};
293 225 100       368 my $filter = defined($fltname) ? $filters{$fltname} : undef;
294              
295             # zoom out to see vertical alignments... we have filter(x2) x
296             # formatter+layouter(x3) x OO/non-OO (x2) = 12 permutations. we
297             # create specialized subroutines for each case, for performance
298             # reason.
299 225 0 0 0   271 if ($filter) { if ($formatter) { if ($layouter) { if ($is_object) { $logger = sub { shift; return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, $layouter->($formatter->(@_), $per_target_conf, $lnum, $lname, $per_msg_conf), $per_msg_conf) }; # has-filter has-formatter has-layouter with-oo
  1 0       2  
  1 50       2  
  0 50       0  
  0 100       0  
  0         0  
  0         0  
  0         0  
300 0 0 0 0   0 } else { $logger = sub { return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, $layouter->($formatter->(@_), $per_target_conf, $lnum, $lname, $per_msg_conf), $per_msg_conf) }; } # has-filter has-formatter has-layouter not-oo
  0         0  
  0         0  
301 1 0 0 0   1 } else { if ($is_object) { $logger = sub { shift; return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, $formatter->(@_), $per_msg_conf) }; # has-filter has-formatter no-layouter with-oo
  0 50       0  
  0         0  
  0         0  
  0         0  
302 1 100 33 2   4 } else { $logger = sub { return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, $formatter->(@_), $per_msg_conf) }; } } # has-filter has-formatter no-layouter not-oo
  2         75  
  1         12  
303 0 0 0 0   0 } else { if ($is_object) { $logger = sub { shift; return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, \@_, $per_msg_conf) }; # has-filter no-formatter no-layouter with-oo
  0 0       0  
  0         0  
  0         0  
  0         0  
304 0 0 0 0   0 } else { $logger = sub { return 0 unless my $per_msg_conf = $filter->(@_); $outputter->($rper_target_conf || $per_target_conf, \@_, $per_msg_conf) }; } } # has-filter no-formatter no-layouter not-oo
  0         0  
  0         0  
305 224 0 0 0   296 } else { if ($formatter) { if ($layouter) { if ($is_object) { $logger = sub { shift; $outputter->($rper_target_conf || $per_target_conf, $layouter->($formatter->(@_), $per_target_conf, $lnum, $lname ) ) }; # no-filter has-formatter has-layouter with-oo
  224 50       255  
  0 50       0  
  0         0  
  0         0  
  0         0  
306 0   0 0   0 } else { $logger = sub { $outputter->($rper_target_conf || $per_target_conf, $layouter->($formatter->(@_), $per_target_conf, $lnum, $lname ) ) }; } # no-filter has-formatter has-layouter not-oo
  0         0  
307 224 100 33 21   279 } else { if ($is_object) { $logger = sub { shift; $outputter->($rper_target_conf || $per_target_conf, $formatter->(@_) ) }; # no-filter has-formatter no-layouter with-oo
  89     21   221  
  21     21   65  
  21     21   54  
        21      
        21      
        21      
        21      
        21      
        21      
        21      
        21      
        21      
308 135   66 28   375 } else { $logger = sub { $outputter->($rper_target_conf || $per_target_conf, $formatter->(@_) ) }; } } # no-filter has-formatter no-layouter not-oo
  28     15   3945  
        24      
        15      
        24      
        15      
        11      
        11      
        24      
        15      
309 0 0 0 0   0 } else { if ($is_object) { $logger = sub { shift; $outputter->($rper_target_conf || $per_target_conf, \@_ ) }; # no-filter no-formatter no-layouter with-oo
  0         0  
  0         0  
  0         0  
310 0   0 0   0 } else { $logger = sub { $outputter->($rper_target_conf || $per_target_conf, \@_ ) }; } } } # no-filter no-formatter no-layouter not-oo
  0         0  
311             } # enclosing block
312             L1:
313 433 100       665 my $rtype = $is_object ? 'logger_method' : 'logger_sub';
314 433   66     1831 push @routines, [$logger, $rname, $lnum, $rtype, $rper_target_conf||$per_target_conf];
315             }
316             }
317              
318             CREATE_LEVEL_CHECKER_ROUTINES:
319             {
320 73         93 my @routine_name_recs;
321             my $type;
322 73 100       116 if ($target_type eq 'package') {
323 39 50       53 push @routine_name_recs, @{ $routine_names->{is_subs} || [] }; # old name, will be removed
  39         124  
324 39 50       52 push @routine_name_recs, @{ $routine_names->{level_checker_subs} || [] };
  39         116  
325 39         59 $type = 'level_checker_sub';
326             } else {
327 34 50       37 push @routine_name_recs, @{ $routine_names->{is_methods} || [] }; # old name, will be removed
  34         98  
328 34 50       43 push @routine_name_recs, @{ $routine_names->{level_checker_methods} || [] };
  34         69  
329 34         43 $type = 'level_checker_method';
330             }
331 73         115 for my $routine_name_rec (@routine_name_recs) {
332 432         670 my ($rname, $lname) = @$routine_name_rec;
333 432         512 my $lnum = $Levels{$lname};
334              
335 432         622 local $hook_args{name} = $rname;
336 432         528 local $hook_args{level} = $lnum;
337 432         506 local $hook_args{str_level} = $lname;
338              
339 432         414 my $code_is;
340             {
341 432 50       410 $code_is = run_hooks('create_is_routine' , \%hook_args, 1, $target_type, $target_name) and last; # old name, will be removed
  432         624  
342 432         626 $code_is = run_hooks('create_level_checker', \%hook_args, 1, $target_type, $target_name);
343             }
344 432 50       658 die "BUG in configuration: No level_checker routine is produced for routine name $rname" unless $code_is;
345              
346 432         1164 push @routines, [$code_is, $rname, $lnum, $type, $per_target_conf];
347             }
348             }
349              
350             {
351 73         88 local $hook_args{routines} = \@routines;
  73         82  
  73         123  
352 73         128 local $hook_args{filters} = \%filters;
353 73         124 local $hook_args{formatters} = \%formatters;
354 73         104 local $hook_args{layouter} = $layouter;
355 73         225 run_hooks('before_install_routines', \%hook_args, 0,
356             $target_type, $target_name);
357             }
358              
359 73         236 install_routines($target_type, $target_name, \@routines, 1);
360              
361             {
362 73         107 local $hook_args{routines} = \@routines;
  73         137  
363 73         140 run_hooks('after_install_routines', \%hook_args, 0,
364             $target_type, $target_name);
365             }
366             }
367              
368             1;
369             # ABSTRACT: The bulk of the implementation of Log::ger
370              
371             __END__