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