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 = '2021-01-31'; # DATE
5             our $DIST = 'Log-ger'; # DIST
6             our $VERSION = '0.038'; # 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   57 no warnings ($warnings::Bits{'redundant'} ? 'redundant' : 'redefine');
  8         16  
  8         18129  
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   141 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 1760     1760 0 3171 my ($phase, $hook_args, $flow_control,
157             $target_type, $target_name) = @_;
158             #print "D: running hooks for phase $phase\n";
159              
160 1760 50       3444 $Global_Hooks{$phase} or die "Unknown phase '$phase'";
161 1760         2087 my @hooks = @{ $Global_Hooks{$phase} };
  1760         3079  
162              
163 1760 100       3712 if ($target_type eq 'package') {
    100          
    50          
164 944 100       1159 unshift @hooks, @{ $Per_Package_Hooks{$target_name}{$phase} || [] };
  944         2797  
165             } elsif ($target_type eq 'hash') {
166 216         1190 my ($addr) = "$target_name" =~ $re_addr;
167 216 50       376 unshift @hooks, @{ $Per_Hash_Hooks{$addr}{$phase} || [] };
  216         749  
168             } elsif ($target_type eq 'object') {
169 600         3668 my ($addr) = "$target_name" =~ $re_addr;
170 600 50       959 unshift @hooks, @{ $Per_Object_Hooks{$addr}{$phase} || [] };
  600         1972  
171             }
172              
173 1760         2520 my $res;
174 1760         3296 for my $hook (sort {$a->[1] <=> $b->[1]} @hooks) {
  395         1002  
175 1270         3515 my $hook_res = $hook->[2]->(%$hook_args);
176 1270 100       2970 if (defined $hook_res->[0]) {
177 1035         1402 $res = $hook_res->[0];
178             #print "D: got result from hook $hook->[0]: $res\n";
179 1035 100       1882 if (ref $flow_control eq 'CODE') {
180 157 100       332 last if $flow_control->($hook, $hook_res);
181             } else {
182 878 50       1848 last if $flow_control;
183             }
184             }
185 318 50       728 last if $hook_res->[1];
186             }
187 1760         16278 return $res;
188             }
189              
190             sub init_target {
191 74     74 0 245 my ($target_type, $target_name, $per_target_conf) = @_;
192              
193             #print "D:init_target($target_type, $target_name, ...)\n";
194 74         287 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 74         112 my %filters;
202             run_hooks(
203             'create_filter', \%hook_args,
204             # collect filters, until a hook instructs to stop
205             sub {
206 2     2   6 my ($hook, $hook_res) = @_;
207 2         5 my ($filter, $flow_control, $fltname) = @$hook_res;
208 2 50       16 $fltname = 'default' if !defined($fltname);
209 2   33     14 $filters{$fltname} ||= $filter;
210 2         5 $flow_control;
211             },
212 74         393 $target_type, $target_name);
213              
214 74         292 my %formatters;
215             run_hooks(
216             'create_formatter', \%hook_args,
217             # collect formatters, until a hook instructs to stop
218             sub {
219 79     79   142 my ($hook, $hook_res) = @_;
220 79         143 my ($formatter, $flow_control, $fmtname) = @$hook_res;
221 79 100       187 $fmtname = 'default' if !defined($fmtname);
222 79   66     368 $formatters{$fmtname} ||= $formatter;
223 79         186 $flow_control;
224             },
225 74         330 $target_type, $target_name);
226              
227             # collect only a single layouter
228 74         272 my $layouter =
229             run_hooks(
230             'create_layouter', \%hook_args, 1, $target_type, $target_name);
231              
232 74         130 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 76     76   139 my ($hook, $hook_res) = @_;
238 76         144 my ($routine_name_rec, $flow_control) = @$hook_res;
239 76 50       170 $routine_name_rec or return;
240 76         224 for (keys %$routine_name_rec) {
241 300         377 push @{ $routine_names->{$_} }, @{ $routine_name_rec->{$_} };
  300         495  
  300         732  
242             }
243 76         254 $flow_control;
244             },
245 74         373 $target_type, $target_name);
246              
247 74         296 my @routines;
248 74         145 my $is_object = $target_type eq 'object';
249              
250             CREATE_LOGGER_ROUTINES:
251             {
252 74         99 my @routine_name_recs;
  74         94  
253 74 100       170 if ($target_type eq 'package') {
254 40 50       57 push @routine_name_recs, @{ $routine_names->{log_subs} || [] }; # old name, will be removed
  40         164  
255 40 50       62 push @routine_name_recs, @{ $routine_names->{logger_subs} || [] };
  40         117  
256             } else {
257 34 50       50 push @routine_name_recs, @{ $routine_names->{log_methods} || [] }; # old name, will be removed
  34         120  
258 34 50       53 push @routine_name_recs, @{ $routine_names->{logger_methods} || [] };
  34         84  
259             }
260             NAME:
261 74         156 for my $routine_name_rec (@routine_name_recs) {
262 440         948 my ($rname, $lname, $fmtname, $rper_target_conf, $fltname)
263             = @$routine_name_rec;
264 440 100       539 my $lnum; $lnum = $Levels{$lname} if defined $lname;
  440         868  
265 440 100       793 $fmtname = 'default' if !defined($fmtname);
266              
267 440         576 my ($output_routine, $logger);
268 440         637 $_outputter_is_null = 0;
269 440         871 local $hook_args{name} = $rname; # compat, deprecated
270 440         654 local $hook_args{routine_name} = $rname;
271 440         704 local $hook_args{level} = $lnum;
272 440         630 local $hook_args{str_level} = $lname;
273 440         537 my $outputter;
274             {
275 440 50       539 $outputter = run_hooks("create_outputter" , \%hook_args, 1, $target_type, $target_name) and last;
  440         822  
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 440 50       790 die "BUG in configuration: No outputter is produced for routine name $rname" unless $outputter;
279              
280             { # enclosing block
281 440 100       561 if ($_outputter_is_null) {
  440         720  
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 211         271 $logger = $outputter;
289 211         265 last;
290             }
291              
292 229         348 my $formatter = $formatters{$fmtname};
293 229 100       424 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 229 0 0 0   355 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       7  
  2 50       5  
  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 2 0 0 0   6 } 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 2 100 33 2   20 } 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         127  
  1         10  
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 227 0 0 0   386 } 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       327  
  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 227 100 33 21   362 } 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   275  
  21     21   98  
  21     21   61  
        21      
        21      
        21      
        21      
        21      
        21      
        21      
        21      
        21      
308 138   66 28   497 } else { $logger = sub { $outputter->($rper_target_conf || $per_target_conf, $formatter->(@_) ) }; } } # no-filter has-formatter no-layouter not-oo
  28     15   5014  
        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 440 100       807 my $rtype = $is_object ? 'logger_method' : 'logger_sub';
314 440   66     2348 push @routines, [$logger, $rname, $lnum, $rtype, $rper_target_conf||$per_target_conf];
315             }
316             }
317              
318             CREATE_LEVEL_CHECKER_ROUTINES:
319             {
320 74         114 my @routine_name_recs;
321             my $type;
322 74 100       156 if ($target_type eq 'package') {
323 40 50       57 push @routine_name_recs, @{ $routine_names->{is_subs} || [] }; # old name, will be removed
  40         175  
324 40 50       66 push @routine_name_recs, @{ $routine_names->{level_checker_subs} || [] };
  40         145  
325 40         70 $type = 'level_checker_sub';
326             } else {
327 34 50       53 push @routine_name_recs, @{ $routine_names->{is_methods} || [] }; # old name, will be removed
  34         110  
328 34 50       52 push @routine_name_recs, @{ $routine_names->{level_checker_methods} || [] };
  34         84  
329 34         57 $type = 'level_checker_method';
330             }
331 74         126 for my $routine_name_rec (@routine_name_recs) {
332 438         837 my ($rname, $lname) = @$routine_name_rec;
333 438         634 my $lnum = $Levels{$lname};
334              
335 438         716 local $hook_args{name} = $rname;
336 438         633 local $hook_args{level} = $lnum;
337 438         608 local $hook_args{str_level} = $lname;
338              
339 438         531 my $code_is;
340             {
341 438 50       528 $code_is = run_hooks('create_is_routine' , \%hook_args, 1, $target_type, $target_name) and last; # old name, will be removed
  438         770  
342 438         796 $code_is = run_hooks('create_level_checker', \%hook_args, 1, $target_type, $target_name);
343             }
344 438 50       813 die "BUG in configuration: No level_checker routine is produced for routine name $rname" unless $code_is;
345              
346 438         1494 push @routines, [$code_is, $rname, $lnum, $type, $per_target_conf];
347             }
348             }
349              
350             {
351 74         102 local $hook_args{routines} = \@routines;
  74         105  
  74         158  
352 74         135 local $hook_args{filters} = \%filters;
353 74         134 local $hook_args{formatters} = \%formatters;
354 74         140 local $hook_args{layouter} = $layouter;
355 74         268 run_hooks('before_install_routines', \%hook_args, 0,
356             $target_type, $target_name);
357             }
358              
359 74         284 install_routines($target_type, $target_name, \@routines, 1);
360              
361             {
362 74         179 local $hook_args{routines} = \@routines;
  74         171  
363 74         213 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__