File Coverage

blib/lib/Log/Any/App.pm
Criterion Covered Total %
statement 488 604 80.7
branch 156 252 61.9
condition 26 47 55.3
subroutine 44 50 88.0
pod 1 1 100.0
total 715 954 74.9


line stmt bran cond sub pod time code
1             package Log::Any::App;
2              
3             our $DATE = '2019-01-09'; # DATE
4             our $VERSION = '0.540'; # VERSION
5              
6             # i need this to run on centos 5.x. otherwise all my other servers are debian
7             # 5.x and 6.x+ (perl 5.010).
8 2     2   44026 use 5.008000;
  2         14  
9 2     2   10 use strict;
  2         3  
  2         52  
10 2     2   10 use warnings;
  2         2  
  2         83  
11              
12 2     2   11 use File::Path qw(make_path);
  2         4  
  2         138  
13 2     2   13 use File::Spec;
  2         6  
  2         60  
14 2     2   1681 use Log::Any::IfLOG;
  2         37  
  2         10  
15 2     2   876 use Log::Any::Adapter;
  2         19888  
  2         11  
16              
17 2     2   81 use vars qw($dbg_ctx);
  2         4  
  2         16658  
18              
19             our %PATTERN_STYLES = (
20             plain => '%m',
21             plain_nl => '%m%n',
22             script_short => '[%r] %m%n',
23             script_long => '[%d] %m%n',
24             daemon => '[pid %P] [%d] %m%n',
25             syslog => '[pid %p] %m',
26             );
27             for (keys %PATTERN_STYLES) {
28             $PATTERN_STYLES{"cat_$_"} = "[cat %c]$PATTERN_STYLES{$_}";
29             $PATTERN_STYLES{"loc_$_"} = "[loc %l]$PATTERN_STYLES{$_}";
30             }
31              
32             my $init_args;
33             our $init_called;
34             my $is_daemon;
35              
36             # poor man's version of 5.10's //
37             sub _ifdef {
38 3482     3482   5027 my $def = pop @_;
39 3482         5033 for (@_) {
40 3482 100       7181 return $_ if defined($_);
41             }
42 3378         8423 $def;
43             }
44              
45             # j=as json (except the last default)
46             sub _ifdefj {
47 144     144   714 require JSON::MaybeXS;
48              
49 144         294 my $def = pop @_;
50 144         251 for (@_) {
51 239 50       551 return JSON::MaybeXS::decode_json($_) if defined($_);
52             }
53 144         870 $def;
54             }
55              
56             sub init {
57 49 50   49 1 72376 return if $init_called++;
58              
59 49         85 $is_daemon = undef;
60              
61 49         96 my ($args, $caller) = @_;
62 49   66     248 $caller ||= caller();
63              
64 49         116 my $spec = _parse_opts($args, $caller);
65 49 100 66     198 if ($spec->{log} && $spec->{init}) {
66 1         5 _init_log4perl($spec);
67 1 50       2710 if ($ENV{LOG_ENV}) {
68 0         0 my $log_main = Log::Any->get_logger(category => 'main');
69 0         0 $log_main->tracef("Environment variables: %s", \%ENV);
70             }
71             }
72 49         152 $spec;
73             }
74              
75             sub _gen_appender_config {
76 6     6   12 my ($ospec, $apd_name, $filter) = @_;
77              
78 6         11 my $name = $ospec->{name};
79 6         8 my $class;
80 6         9 my $params = {};
81 6 50       27 if ($name =~ /^dir/i) {
    50          
    0          
    0          
    0          
    0          
82 0         0 $class = "Log::Dispatch::Dir";
83 0         0 $params->{dirname} = $ospec->{path};
84 0         0 $params->{filename_pattern} = $ospec->{filename_pattern};
85 0 0       0 $params->{max_size} = $ospec->{max_size} if $ospec->{max_size};
86 0 0       0 $params->{max_files} = $ospec->{histories}+1 if $ospec->{histories};
87 0 0       0 $params->{max_age} = $ospec->{max_age} if $ospec->{max_age};
88             } elsif ($name =~ /^file/i) {
89 6         10 $class = "Log::Dispatch::FileWriteRotate";
90 6         27 my ($dir, $prefix) = $ospec->{path} =~ m!(.+)/(.+)!;
91 6   50     15 $dir ||= "."; $prefix ||= $ospec->{path};
  6   33     16  
92 6         14 $params->{dir} = $dir;
93 6         10 $params->{prefix} = $prefix;
94 6         8 $params->{suffix} = $ospec->{suffix};
95 6         12 $params->{size} = $ospec->{max_size};
96 6         7 $params->{period} = $ospec->{period};
97 6         9 $params->{histories} = $ospec->{histories};
98 6         11 $params->{buffer_size} = $ospec->{buffer_size};
99             } elsif ($name =~ /^screen/i) {
100             $class = "Log::Log4perl::Appender::" .
101 0 0       0 ($ospec->{color} ? "ScreenColoredLevels" : "Screen");
102 0 0       0 $params->{stderr} = $ospec->{stderr} ? 1:0;
103 0         0 $params->{"color.WARN"} = "bold blue"; # blue on black is so unreadable
104             } elsif ($name =~ /^syslog/i) {
105 0         0 $class = "Log::Dispatch::Syslog";
106 0         0 $params->{mode} = 'append';
107 0         0 $params->{ident} = $ospec->{ident};
108 0         0 $params->{facility} = $ospec->{facility};
109             } elsif ($name =~ /^unixsock/i) {
110 0         0 $class = "Log::Log4perl::Appender::Socket::UNIX";
111 0         0 $params->{Socket} = $ospec->{path};
112             } elsif ($name =~ /^array/i) {
113 0         0 $class = "Log::Dispatch::ArrayWithLimits";
114 0         0 $params->{array} = $ospec->{array};
115 0         0 $params->{max_elems} = $ospec->{max_elems};
116             } else {
117 0         0 die "BUG: Unknown appender type: $name";
118             }
119              
120             join(
121             "",
122             "log4perl.appender.$apd_name = $class\n",
123 12         75 (map { "log4perl.appender.$apd_name.$_ = $params->{$_}\n" }
124 6 50       26 grep {defined $params->{$_}} keys %$params),
  42         69  
125             "log4perl.appender.$apd_name.layout = PatternLayout\n",
126             "log4perl.appender.$apd_name.layout.ConversionPattern = $ospec->{pattern}\n",
127             ($filter ? "log4perl.appender.$apd_name.Filter = $filter\n" : ""),
128             );
129             }
130              
131             sub _lit {
132 0     0   0 require Data::Dump;
133 0         0 Data::Dump::dump(shift);
134             }
135              
136             sub _gen_l4p_config {
137 1     1   4 my ($spec) = @_;
138              
139 1         5 my @otypes = qw(file dir screen syslog unixsock array);
140              
141             # we use a custom perl code to implement filter_* specs.
142 1         2 my @fccode;
143 1         2 push @fccode, 'my %p = @_';
144 1         2 push @fccode, 'my $str';
145 1         3 for my $ospec (map { @{ $spec->{$_} } } @otypes) {
  6         7  
  6         15  
146 2 50       7 if (defined $ospec->{filter_text}) {
147 0         0 push @fccode, '$str = '._lit($ospec->{filter_text});
148 0         0 push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
149             ' && index($_, $str) == -1';
150             }
151 2 50       6 if (defined $ospec->{filter_no_text}) {
152 0         0 push @fccode, '$str = '._lit($ospec->{filter_no_text});
153 0         0 push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
154             ' && index($_, $str) > -1';
155             }
156 2 50       6 if (defined $ospec->{filter_citext}) {
157 0         0 push @fccode, '$str = '._lit($ospec->{filter_citext});
158 0         0 push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
159             ' && !/\Q$str/io';
160             }
161 2 50       4 if (defined $ospec->{filter_no_citext}) {
162 0         0 push @fccode, '$str = '._lit($ospec->{filter_no_citext});
163 0         0 push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
164             ' && /\Q$str/io';
165             }
166 2 50       6 if (defined $ospec->{filter_re}) {
167 0         0 push @fccode, '$str = '._lit($ospec->{filter_re});
168             push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
169 0 0       0 ' && $_ !~ ' . (ref($ospec->{filter_re}) eq 'Regexp' ? '$str' : 'qr/$str/o');
170             }
171 2 50       5 if (defined $ospec->{filter_no_re}) {
172 0         0 push @fccode, '$str = '._lit($ospec->{filter_no_re});
173             push @fccode, 'return 0 if $p{name} eq '._lit($ospec->{name}).
174 0 0       0 ' && $_ =~ ' . (ref($ospec->{filter_re}) eq 'Regexp' ? '$str' : 'qr/$str/o');
175             }
176             }
177 1         3 push @fccode, "1";
178 1         5 my $fccode = join "; ", @fccode;
179              
180             my $filters_str = join(
181             "",
182             "log4perl.filter.FilterCustom = sub { $fccode }\n",
183             "\n",
184             "log4perl.filter.FilterOFF0 = Log::Log4perl::Filter::LevelRange\n",
185             "log4perl.filter.FilterOFF0.LevelMin = TRACE\n",
186             "log4perl.filter.FilterOFF0.LevelMax = FATAL\n",
187             "log4perl.filter.FilterOFF0.AcceptOnMatch = false\n",
188             "\n",
189             "log4perl.filter.FilterOFF = Log::Log4perl::Filter::Boolean\n",
190             "log4perl.filter.FilterOFF.logic = FilterOFF0 && FilterCustom\n",
191 1         6 map {join(
  5         29  
192             "",
193             "log4perl.filter.Filter${_}0 = Log::Log4perl::Filter::LevelRange\n",
194             "log4perl.filter.Filter${_}0.LevelMin = $_\n",
195             "log4perl.filter.Filter${_}0.LevelMax = FATAL\n",
196             "log4perl.filter.Filter${_}0.AcceptOnMatch = true\n",
197             "\n",
198             "log4perl.filter.Filter$_ = Log::Log4perl::Filter::Boolean\n",
199             "log4perl.filter.Filter$_.logic = Filter${_}0 && FilterCustom\n",
200             "\n",
201             )} qw(FATAL ERROR WARN INFO DEBUG), # TRACE
202             );
203              
204 1         4 my %levels; # key = output name; value = { cat => level, ... }
205             my %cats; # list of categories
206 1         0 my %ospecs; # key = oname; this is just a shortcut to get ospec
207              
208             # 1. list all levels for each category and output
209 1         4 for my $ospec (map { @{ $spec->{$_} } } @otypes) {
  6         7  
  6         12  
210 2         5 my $oname = $ospec->{name};
211 2         4 $ospecs{$oname} = $ospec;
212 2         5 $levels{$oname} = {};
213 2         3 my %seen_cats;
214 2 50       7 if ($ospec->{category_level}) {
215 2         3 while (my ($cat0, $level) = each %{ $ospec->{category_level} }) {
  6         75  
216 4         12 my @cat = _extract_category($ospec, $cat0);
217 4         8 for my $cat (@cat) {
218 4 50       12 next if $seen_cats{$cat}++;
219 4         6 $cats{$cat}++;
220 4         11 $levels{$oname}{$cat} = $level;
221             }
222             }
223             }
224 2 50       6 if ($spec->{category_level}) {
225 2         3 while (my ($cat0, $level) = each %{ $spec->{category_level} }) {
  4         14  
226 2         5 my @cat = _extract_category($ospec, $cat0);
227 2         3 for my $cat (@cat) {
228 4 50       12 next if $seen_cats{$cat}++;
229 4         6 $cats{$cat}++;
230 4         10 $levels{$oname}{$cat} = $level;
231             }
232             }
233             }
234 2         5 my @cat = _extract_category($ospec);
235 2         4 for my $cat (@cat) {
236 2 50       6 next if $seen_cats{$cat}++;
237 2         2 $cats{$cat}++;
238 2         9 $levels{$oname}{$cat} = $ospec->{level};
239             }
240             }
241             #print Dumper \%levels; exit;
242              
243             my $find_olevel = sub {
244 40     40   73 my ($oname, $cat) = @_;
245 40         59 my $olevel = $levels{$oname}{''};
246 40         91 my @c = split /\./, $cat;
247 40         85 for (my $i=0; $i<@c; $i++) {
248 64         118 my $c = join(".", @c[0..$i]);
249 64 100       133 if ($levels{$oname}{$c}) {
250 42         89 $olevel = $levels{$oname}{$c};
251             }
252             }
253 40         83 $olevel;
254 1         18 };
255              
256             # 2. determine level for each category (which is the minimum level of all
257             # appenders for that category)
258 1         3 my %cat_configs; # key = cat, value = [catlevel, apdname, ...]
259 1         2 my $add_str = '';
260 1         2 my $apd_str = '';
261 1         8 for my $cat0 (sort {$a cmp $b} keys %cats) {
  13         21  
262 7 100       25 $add_str .= "log4perl.additivity.$cat0 = 0\n" unless $cat0 eq '';
263 7         14 my @cats = ($cat0);
264             # since we don't use additivity, we need to add supercategories ourselves
265 7         32 while ($cat0 =~ s/\.[^.]+$//) { push @cats, $cat0 }
  6         19  
266 7         13 for my $cat (@cats) {
267 13         16 my $cat_level;
268 13         25 for my $oname (keys %levels) {
269 26         42 my $olevel = $find_olevel->($oname, $cat);
270 26 50       395 next unless $olevel;
271 26         43 $cat_level = _ifdef($cat_level, $olevel);
272 26         39 $cat_level = _min_level($cat_level, $olevel);
273             }
274 13         54 $cat_configs{$cat} = [uc($cat_level)];
275             #next if $cat_level eq 'off';
276             }
277             }
278             #print Dumper \%cat_configs; exit;
279              
280             # 3. add appenders for each category
281 1         3 my %generated_appenders; # key = apdname, just a memory hash
282 1         5 for my $cat (keys %cat_configs) {
283 7         12 my $cat_level = $cat_configs{$cat}[0];
284 7         14 for my $oname (keys %levels) {
285 14         18 my $ospec = $ospecs{$oname};
286 14         23 my $olevel = $find_olevel->($oname, $cat);
287             #print "D:oname=$oname, cat=$cat, olevel=$olevel, cat_level=$cat_level\n";
288 14         21 my $apd_name;
289             my $filter;
290 14 100 66     33 if ($olevel ne $cat_level &&
291             _min_level($olevel, $cat_level) eq $cat_level) {
292             # we need to filter the appender, since the category level is
293             # lower than the output level
294 5         12 $apd_name = $oname . "_" . uc($olevel);
295 5         7 $filter = "Filter".uc($olevel);
296             } else {
297 9         12 $apd_name = $oname;
298 9         14 $filter = "FilterCustom";
299             }
300 14 100       44 unless ($generated_appenders{$apd_name}++) {
301 6         16 $apd_str .= _gen_appender_config($ospec, $apd_name, $filter).
302             "\n";
303             }
304 14         23 push @{ $cat_configs{$cat} }, $apd_name;
  14         38  
305             }
306             }
307             #print Dumper \%cat_configs; exit;
308              
309             # 4. write out log4perl category line
310 1         4 my $cat_str = '';
311 1         49 for my $cat (sort {$a cmp $b} keys %cat_configs) {
  13         19  
312 7 100       45 my $l = $cat eq '' ? '' : ".$cat";
313 7         13 $cat_str .= "log4perl.logger$l = ".join(", ", @{ $cat_configs{$cat} })."\n";
  7         21  
314             }
315              
316             join(
317 1         20 "",
318             "# filters\n", $filters_str,
319             "# categories\n", $cat_str, $add_str, "\n",
320             "# appenders\n", $apd_str,
321             );
322             }
323              
324             sub _init_log4perl {
325 1     1   844 require Log::Log4perl;
326              
327 1         45918 my ($spec) = @_;
328              
329             # create intermediate directories for dir
330 1         3 for (@{ $spec->{dir} }) {
  1         5  
331 0         0 my $dir = _dirname($_->{path});
332 0 0 0     0 make_path($dir) if length($dir) && !(-d $dir);
333             }
334              
335             # create intermediate directories for file
336 1         2 for (@{ $spec->{file} }) {
  1         4  
337 2         9 my $dir = _dirname($_->{path});
338 2 50 33     38 make_path($dir) if length($dir) && !(-d $dir);
339             }
340              
341 1         6 my $config_str = _gen_l4p_config($spec);
342 1 50       5 if ($spec->{dump}) {
343 0         0 require Data::Dump;
344 0         0 print "Log::Any::App configuration:\n",
345             Data::Dump::dump($spec);
346 0         0 print "Log4perl configuration: <<EOC\n", $config_str, "EOC\n";
347             }
348              
349 1         7 Log::Log4perl->init(\$config_str);
350 1         347507 Log::Any::Adapter->set('Log4perl');
351             }
352              
353             sub _basename {
354 49     49   79 my $path = shift;
355 49         544 my ($vol, $dir, $file) = File::Spec->splitpath($path);
356 49         142 $file;
357             }
358              
359             sub _dirname {
360 2     2   4 my $path = shift;
361 2         30 my ($vol, $dir, $file) = File::Spec->splitpath($path);
362 2         6 $dir;
363             }
364              
365             # we separate args and opts, because we need to export logger early
366             # (BEGIN), but configure logger in INIT (to be able to detect
367             # existence of other modules).
368              
369             sub _parse_args {
370 2     2   4 my ($args, $caller) = @_;
371 2         7 $args = _ifdef($args, []); # if we don't import(), we never get args
372              
373 2         4 my $i = 0;
374 2         9 while ($i < @$args) {
375 10         17 my $arg = $args->[$i];
376 10 100       35 do { $i+=2; next } if $arg =~ /^-(\w+)$/;
  9         12  
  9         30  
377 1 50       3 if ($arg eq '$log') {
378 1         2 _export_logger($caller);
379             } else {
380 0         0 die "Unknown arg '$arg', valid arg is '\$log' or -OPTS";
381             }
382 1         4 $i++;
383             }
384             }
385              
386             sub _parse_opts {
387 49     49   1162 require File::HomeDir;
388              
389 49         10651 my ($args, $caller) = @_;
390 49         123 $args = _ifdef($args, []); # if we don't import(), we never get args
391 49         259 _debug("parse_opts: args = [".join(", ", @$args)."]");
392              
393 49         80 my $i = 0;
394 49         69 my %opts;
395 49         110 while ($i < @$args) {
396 73         117 my $arg = $args->[$i];
397 73 100       382 do { $i++; next } unless $arg =~ /^-(\w+)$/;
  1         2  
  1         3  
398 72         177 my $opt = $1;
399 72 50       156 die "Missing argument for option $opt" unless $i++ < @$args-1;
400 72         114 $arg = $args->[$i];
401 72         137 $opts{$opt} = $arg;
402 72         151 $i++;
403             }
404              
405 49         79 my $spec = {};
406              
407 49         149 $spec->{log} = _ifdef($ENV{LOG}, 1);
408 49 50       127 if (defined $opts{log}) {
409 0         0 $spec->{log} = $opts{log};
410 0         0 delete $opts{log};
411             }
412             # exit as early as possible if we are not doing any logging
413 49 50       107 goto END_PARSE_OPTS unless $spec->{log};
414              
415 49         96 $spec->{name} = _basename($0);
416 49 100       114 if (defined $opts{name}) {
417 6         12 $spec->{name} = $opts{name};
418 6         12 delete $opts{name};
419             }
420              
421 49         160 $spec->{level_flag_paths} = [File::HomeDir->my_home, "/etc"];
422 49 100       1786 if (defined $opts{level_flag_paths}) {
423 4         9 $spec->{level_flag_paths} = $opts{level_flag_paths};
424 4         9 delete $opts{level_flag_paths};
425             }
426              
427 49         129 $spec->{level} = _set_level("", "", $spec);
428 49 50 66     230 if (!$spec->{level} && defined($opts{level})) {
    100          
429 0         0 $spec->{level} = _check_level($opts{level}, "-level");
430 0         0 _debug("Set general level to $spec->{level} (from -level)");
431             } elsif (!$spec->{level}) {
432 31         59 $spec->{level} = "warn";
433 31         74 _debug("Set general level to $spec->{level} (default)");
434             }
435 49         79 delete $opts{level};
436              
437 49         150 $spec->{category_alias} = _ifdefj($ENV{LOG_CATEGORY_ALIAS}, {});
438 49 100       124 if (defined $opts{category_alias}) {
439             die "category_alias must be a hashref"
440 1 50       5 unless ref($opts{category_alias}) eq 'HASH';
441 1         3 $spec->{category_alias} = $opts{category_alias};
442 1         3 delete $opts{category_alias};
443             }
444              
445 49 100       98 if (defined $opts{category_level}) {
446             die "category_level must be a hashref"
447 1 50       4 unless ref($opts{category_level}) eq 'HASH';
448 1         24 $spec->{category_level} = {};
449 1         17 for (keys %{ $opts{category_level} }) {
  1         7  
450             $spec->{category_level}{$_} =
451 1         7 _check_level($opts{category_level}{$_}, "-category_level{$_}");
452             }
453 1         3 delete $opts{category_level};
454             }
455              
456 49         73 $spec->{init} = 1;
457 49 100       122 if (defined $opts{init}) {
458 48         96 $spec->{init} = $opts{init};
459 48         84 delete $opts{init};
460             }
461              
462 49         77 $spec->{daemon} = 0;
463 49 100       99 if (defined $opts{daemon}) {
464 2         4 $spec->{daemon} = $opts{daemon};
465 2         7 _debug("setting is_daemon=$opts{daemon} (from daemon option)");
466 2         4 $is_daemon = $opts{daemon};
467 2         5 delete $opts{daemon};
468             }
469              
470 49         138 $spec->{dump} = $ENV{LOGANYAPP_DEBUG};
471 49 50       98 if (defined $opts{dump}) {
472 0         0 $spec->{dump} = 1;
473 0         0 delete $opts{dump};
474             }
475              
476 49         81 $spec->{filter_text} = $ENV{LOG_FILTER_TEXT};
477 49 50       90 if (defined $opts{filter_text}) {
478 0         0 $spec->{filter_text} = $opts{filter_text};
479 0         0 delete $opts{filter_text};
480             }
481 49         85 $spec->{filter_no_text} = $ENV{LOG_FILTER_NO_TEXT};
482 49 50       88 if (defined $opts{filter_no_text}) {
483 0         0 $spec->{filter_no_text} = $opts{filter_no_text};
484 0         0 delete $opts{filter_no_text};
485             }
486 49         79 $spec->{filter_citext} = $ENV{LOG_FILTER_CITEXT};
487 49 50       93 if (defined $opts{filter_citext}) {
488 0         0 $spec->{filter_citext} = $opts{filter_citext};
489 0         0 delete $opts{filter_citext};
490             }
491 49         103 $spec->{filter_no_citext} = $ENV{LOG_FILTER_NO_CITEXT};
492 49 50       97 if (defined $opts{filter_no_citext}) {
493 0         0 $spec->{filter_no_citext} = $opts{filter_no_citext};
494 0         0 delete $opts{filter_no_citext};
495             }
496 49         84 $spec->{filter_re} = $ENV{LOG_FILTER_RE};
497 49 50       85 if (defined $opts{filter_re}) {
498 0         0 $spec->{filter_re} = $opts{filter_re};
499 0         0 delete $opts{filter_re};
500             }
501 49         84 $spec->{filter_no_re} = $ENV{LOG_FILTER_NO_RE};
502 49 50       85 if (defined $opts{filter_no_re}) {
503 0         0 $spec->{filter_no_re} = $opts{filter_no_re};
504 0         0 delete $opts{filter_no_re};
505             }
506              
507 49         102 $spec->{file} = [];
508 49 100       196 _parse_opt_file($spec, _ifdef($opts{file}, ($0 ne '-e' ? 1:0)));
509 49         293 delete $opts{file};
510              
511 49         164 $spec->{dir} = [];
512 49         128 _parse_opt_dir($spec, _ifdef($opts{dir}, 0));
513 49         92 delete $opts{dir};
514              
515 49         99 $spec->{screen} = [];
516 49         115 _parse_opt_screen($spec, _ifdef($opts{screen}, !_is_daemon()));
517 49         98 delete $opts{screen};
518              
519 49         97 $spec->{syslog} = [];
520 49         170 _parse_opt_syslog($spec, _ifdef($opts{syslog}, _is_daemon()));
521 49         90 delete $opts{syslog};
522              
523 49         93 $spec->{unixsock} = [];
524 49         113 _parse_opt_unixsock($spec, _ifdef($opts{unixsock}, 0));
525 49         255 delete $opts{unixsock};
526              
527 49         94 $spec->{array} = [];
528 49         125 _parse_opt_array($spec, _ifdef($opts{array}, 0));
529 49         88 delete $opts{array};
530              
531 49 50       128 if (keys %opts) {
532 0         0 die "Unknown option(s) ".join(", ", keys %opts)." Known opts are: ".
533             "log, name, level, category_level, category_alias, dump, init, ".
534             "filter_{,no_}{text,citext,re}, file, dir, screen, syslog, ".
535             "unixsock, array";
536             }
537              
538             END_PARSE_OPTS:
539             #use Data::Dump; dd $spec;
540 49         124 $spec;
541             }
542              
543             sub _is_daemon {
544 98 100   98   192 if (defined $is_daemon) { return $is_daemon }
  51         102  
545 47 100       81 if (defined $main::IS_DAEMON) {
546 1         2 $is_daemon = $main::IS_DAEMON;
547 1         6 _debug("Setting is_daemon=$main::IS_DAEMON (from \$main::IS_DAEMON)");
548 1         3 return $main::IS_DAEMON;
549             }
550              
551 46         85 for (
552             "App/Daemon.pm",
553             "Daemon/Easy.pm",
554             "Daemon/Daemonize.pm",
555             "Daemon/Generic.pm",
556             "Daemonise.pm",
557             "Daemon/Simple.pm",
558             "HTTP/Daemon.pm",
559             "IO/Socket/INET/Daemon.pm",
560             #"Mojo/Server/Daemon.pm", # simply loading Mojo::UserAgent will load this too
561             "MooseX/Daemonize.pm",
562             "Net/Daemon.pm",
563             "Net/Server.pm",
564             "Proc/Daemon.pm",
565             "Proc/PID/File.pm",
566             "Win32/Daemon/Simple.pm") {
567 636 100       1174 if ($INC{$_}) {
568 2         8 _debug("setting is_daemon=1 (from existence of module $_)");
569 2         4 $is_daemon = 1;
570 2         5 return 1;
571             }
572             }
573 44         98 _debug("setting is_daemon=0 (no indication that we are a daemon)");
574 44         65 $is_daemon = 0;
575 44         86 0;
576             }
577              
578             sub _parse_opt_OUTPUT {
579 296     296   887 my (%args) = @_;
580 296         498 my $kind = $args{kind};
581 296         404 my $default_sub = $args{default_sub};
582 296         390 my $postprocess = $args{postprocess};
583 296         370 my $spec = $args{spec};
584 296         388 my $arg = $args{arg};
585              
586 296 100       674 return unless $arg;
587              
588 96 100 100     266 if (!ref($arg) || ref($arg) eq 'HASH') {
    50          
589 95         150 my $name = uc($kind).(@{ $spec->{$kind} }+0);
  95         246  
590 95         180 local $dbg_ctx = $name;
591 95         116 push @{ $spec->{$kind} }, $default_sub->($spec);
  95         195  
592 95         419 $spec->{$kind}[-1]{name} = $name;
593 95 100       211 if (!ref($arg)) {
594             # leave every output parameter as is
595             } else {
596 6         23 for my $k (keys %$arg) {
597 12         21 for ($spec->{$kind}[-1]) {
598 12 50       27 exists($_->{$k}) or die "Invalid $kind argument: $k, please".
599             " only specify one of: " . join(", ", sort keys %$_);
600             $_->{$k} = $k eq 'level' ?
601 12 100       35 _check_level($arg->{$k}, "-$kind") : $arg->{$k};
602 12 100       33 _debug("Set level of $kind to $_->{$k} (spec)")
603             if $k eq 'level';
604             }
605             }
606             }
607 95         181 $spec->{$kind}[-1]{main_spec} = $spec;
608 95         220 _set_pattern($spec->{$kind}[-1], $kind);
609 95 100       341 $postprocess->(spec => $spec, ospec => $spec->{$kind}[-1])
610             if $postprocess;
611             } elsif (ref($arg) eq 'ARRAY') {
612 1         4 for (@$arg) {
613 2         19 _parse_opt_OUTPUT(%args, arg => $_);
614             }
615             } else {
616 0         0 die "Invalid argument for -$kind, ".
617             "must be a boolean or hashref or arrayref";
618             }
619             }
620              
621             sub _set_pattern_style {
622 95     95   239 my ($x) = @_;
623             ($ENV{LOG_SHOW_LOCATION} ? 'loc_':
624 95 50       473 $ENV{LOG_SHOW_CATEGORY} ? 'cat_':'') . $x;
    50          
625             }
626              
627             sub _default_file {
628 48     48   190 require File::HomeDir;
629              
630 48         89 my ($spec) = @_;
631 48         91 my $level = _set_level("file", "file", $spec);
632 48 100       114 if (!$level) {
633 39         73 $level = $spec->{level};
634 39         88 _debug("Set level of file to $level (general level)");
635             }
636             return {
637             level => $level,
638             category_level => _ifdefj($ENV{FILE_LOG_CATEGORY_LEVEL},
639             $ENV{LOG_CATEGORY_LEVEL},
640             $spec->{category_level}),
641             path => $> ? File::Spec->catfile(File::HomeDir->my_home, "$spec->{name}.log") :
642             "/var/log/$spec->{name}.log", # XXX and on Windows?
643             max_size => undef,
644             histories => undef,
645             period => undef,
646             buffer_size => undef,
647             category => '',
648             pattern_style => _set_pattern_style('daemon'),
649             pattern => undef,
650              
651             filter_text => _ifdef($ENV{FILE_LOG_FILTER_TEXT}, $spec->{filter_text}),
652             filter_no_text => _ifdef($ENV{FILE_LOG_FILTER_NO_TEXT}, $spec->{filter_no_text}),
653             filter_citext => _ifdef($ENV{FILE_LOG_FILTER_CITEXT}, $spec->{filter_citext}),
654             filter_no_citext => _ifdef($ENV{FILE_LOG_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
655             filter_re => _ifdef($ENV{FILE_LOG_FILTER_RE}, $spec->{filter_re}),
656 48 50       197 filter_no_re => _ifdef($ENV{FILE_LOG_FILTER_NO_RE}, $spec->{filter_no_re}),
657             };
658             }
659              
660             sub _parse_opt_file {
661 49     49   97 my ($spec, $arg) = @_;
662              
663 49 100 100     373 if (!ref($arg) && $arg && $arg !~ /^(1|yes|true)$/i) {
      100        
664 2         7 $arg = {path => $arg};
665             }
666              
667             _parse_opt_OUTPUT(
668             kind => 'file', default_sub => \&_default_file,
669             spec => $spec, arg => $arg,
670             postprocess => sub {
671 48     48   138 my (%args) = @_;
672 48         80 my $spec = $args{spec};
673 48         77 my $ospec = $args{ospec};
674 48 100       270 if ($ospec->{path} =~ m!/$!) {
675 2         3 my $p = $ospec->{path};
676 2         6 $p .= "$spec->{name}.log";
677 2         8 _debug("File path ends with /, assumed to be dir, ".
678             "final path becomes $p");
679 2         9 $ospec->{path} = $p;
680             }
681             },
682 49         299 );
683             }
684              
685             sub _default_dir {
686 0     0   0 require File::HomeDir;
687              
688 0         0 my ($spec) = @_;
689 0         0 my $level = _set_level("dir", "dir", $spec);
690 0 0       0 if (!$level) {
691 0         0 $level = $spec->{level};
692 0         0 _debug("Set level of dir to $level (general level)");
693             }
694             return {
695             level => $level,
696             category_level => _ifdefj($ENV{DIR_LOG_CATEGORY_LEVEL},
697             $ENV{LOG_CATEGORY_LEVEL},
698             $spec->{category_level}),
699             path => $> ? File::Spec->catfile(File::HomeDir->my_home, "log", $spec->{name}) :
700             "/var/log/$spec->{name}", # XXX and on Windows?
701             max_size => undef,
702             max_age => undef,
703             histories => undef,
704             category => '',
705             pattern_style => _set_pattern_style('plain'),
706             pattern => undef,
707             filename_pattern => undef,
708              
709             filter_text => _ifdef($ENV{DIR_LOG_FILTER_TEXT}, $spec->{filter_text}),
710             filter_no_text => _ifdef($ENV{DIR_LOG_FILTER_NO_TEXT}, $spec->{filter_no_text}),
711             filter_citext => _ifdef($ENV{DIR_LOG_FILTER_CITEXT}, $spec->{filter_citext}),
712             filter_no_citext => _ifdef($ENV{DIR_LOG_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
713             filter_re => _ifdef($ENV{DIR_LOG_FILTER_RE}, $spec->{filter_re}),
714 0 0       0 filter_no_re => _ifdef($ENV{DIR_LOG_FILTER_NO_RE}, $spec->{filter_no_re}),
715             };
716             }
717              
718             sub _parse_opt_dir {
719 49     49   115 my ($spec, $arg) = @_;
720              
721 49 50 33     204 if (!ref($arg) && $arg && $arg !~ /^(1|yes|true)$/i) {
      33        
722 0         0 $arg = {path => $arg};
723             }
724              
725             _parse_opt_OUTPUT(
726 49         125 kind => 'dir', default_sub => \&_default_dir,
727             spec => $spec, arg => $arg,
728             );
729             }
730              
731             sub _default_screen {
732 43     43   79 my ($spec) = @_;
733 43         88 my $level = _set_level("screen", "screen", $spec);
734 43 100       94 if (!$level) {
735 34         68 $level = $spec->{level};
736 34         78 _debug("Set level of screen to $level (general level)");
737             }
738             return {
739             color => _ifdef($ENV{COLOR}, (-t STDOUT)),
740             stderr => 1,
741             level => $level,
742             category_level => _ifdefj($ENV{SCREEN_LOG_CATEGORY_LEVEL},
743             $ENV{LOG_CATEGORY_LEVEL},
744             $spec->{category_level}),
745             category => '',
746             pattern_style => _set_pattern_style(
747             $ENV{LOG_ELAPSED_TIME_IN_SCREEN} ? 'script_short' : 'plain_nl'),
748             pattern => undef,
749              
750             filter_text => _ifdef($ENV{SCREEN_LOG_FILTER_TEXT}, $spec->{filter_text}),
751             filter_no_text => _ifdef($ENV{SCREEN_FILTER_NO_TEXT}, $spec->{filter_no_text}),
752             filter_citext => _ifdef($ENV{SCREEN_FILTER_CITEXT}, $spec->{filter_citext}),
753             filter_no_citext => _ifdef($ENV{SCREEN_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
754             filter_re => _ifdef($ENV{SCREEN_FILTER_RE}, $spec->{filter_re}),
755 43 50       360 filter_no_re => _ifdef($ENV{SCREEN_FILTER_NO_RE}, $spec->{filter_no_re}),
756             };
757             }
758              
759             sub _parse_opt_screen {
760 49     49   95 my ($spec, $arg) = @_;
761 49         101 _parse_opt_OUTPUT(
762             kind => 'screen', default_sub => \&_default_screen,
763             spec => $spec, arg => $arg,
764             );
765             }
766              
767             sub _default_syslog {
768 4     4   7 my ($spec) = @_;
769 4         10 my $level = _set_level("syslog", "syslog", $spec);
770 4 50       10 if (!$level) {
771 4         9 $level = $spec->{level};
772 4         11 _debug("Set level of syslog to $level (general level)");
773             }
774             return {
775             level => $level,
776             category_level => _ifdefj($ENV{SYSLOG_LOG_CATEGORY_LEVEL},
777             $ENV{LOG_CATEGORY_LEVEL},
778             $spec->{category_level}),
779             ident => $spec->{name},
780             facility => 'daemon',
781             pattern_style => _set_pattern_style('syslog'),
782             pattern => undef,
783             category => '',
784              
785             filter_text => _ifdef($ENV{SYSLOG_LOG_FILTER_TEXT}, $spec->{filter_text}),
786             filter_no_text => _ifdef($ENV{SYSLOG_FILTER_NO_TEXT}, $spec->{filter_no_text}),
787             filter_citext => _ifdef($ENV{SYSLOG_FILTER_CITEXT}, $spec->{filter_citext}),
788             filter_no_citext => _ifdef($ENV{SYSLOG_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
789             filter_re => _ifdef($ENV{SYSLOG_FILTER_RE}, $spec->{filter_re}),
790 4         19 filter_no_re => _ifdef($ENV{SYSLOG_FILTER_NO_RE}, $spec->{filter_no_re}),
791             };
792             }
793              
794             sub _parse_opt_syslog {
795 49     49   93 my ($spec, $arg) = @_;
796 49         106 _parse_opt_OUTPUT(
797             kind => 'syslog', default_sub => \&_default_syslog,
798             spec => $spec, arg => $arg,
799             );
800             }
801              
802             sub _default_unixsock {
803 0     0   0 require File::HomeDir;
804              
805 0         0 my ($spec) = @_;
806 0         0 my $level = _set_level("unixsock", "unixsock", $spec);
807 0 0       0 if (!$level) {
808 0         0 $level = $spec->{level};
809 0         0 _debug("Set level of unixsock to $level (general level)");
810             }
811             return {
812             level => $level,
813             category_level => _ifdefj($ENV{UNIXSOCK_LOG_CATEGORY_LEVEL},
814             $ENV{LOG_CATEGORY_LEVEL},
815             $spec->{category_level}),
816             path => $> ? File::Spec->catfile(File::HomeDir->my_home, "$spec->{name}-log.sock") :
817             "/var/run/$spec->{name}-log.sock", # XXX and on Windows?
818             category => '',
819             pattern_style => _set_pattern_style('daemon'),
820             pattern => undef,
821              
822             filter_text => _ifdef($ENV{UNIXSOCK_LOG_FILTER_TEXT}, $spec->{filter_text}),
823             filter_no_text => _ifdef($ENV{UNIXSOCK_LOG_FILTER_NO_TEXT}, $spec->{filter_no_text}),
824             filter_citext => _ifdef($ENV{UNIXSOCK_LOG_FILTER_CITEXT}, $spec->{filter_citext}),
825             filter_no_citext => _ifdef($ENV{UNIXSOCK_LOG_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
826             filter_re => _ifdef($ENV{UNIXSOCK_LOG_FILTER_RE}, $spec->{filter_re}),
827 0 0       0 filter_no_re => _ifdef($ENV{UNIXSOCK_LOG_FILTER_NO_RE}, $spec->{filter_no_re}),
828             };
829             }
830              
831             sub _parse_opt_unixsock {
832 49     49   114 my ($spec, $arg) = @_;
833              
834 49 50 33     201 if (!ref($arg) && $arg && $arg !~ /^(1|yes|true)$/i) {
      33        
835 0         0 $arg = {path => $arg};
836             }
837              
838             _parse_opt_OUTPUT(
839             kind => 'unixsock', default_sub => \&_default_unixsock,
840             spec => $spec, arg => $arg,
841             postprocess => sub {
842 0     0   0 my (%args) = @_;
843 0         0 my $spec = $args{spec};
844 0         0 my $ospec = $args{ospec};
845 0 0       0 if ($ospec->{path} =~ m!/$!) {
846 0         0 my $p = $ospec->{path};
847 0         0 $p .= "$spec->{name}-log.sock";
848 0         0 _debug("Unix socket path ends with /, assumed to be dir, ".
849             "final path becomes $p");
850 0         0 $ospec->{path} = $p;
851             }
852              
853             # currently Log::Log4perl::Appender::Socket::UNIX *connects to an
854             # existing and listening* Unix socket and prints log to it. we are
855             # *not* creating a listening unix socket where clients can connect
856             # and see logs. to do that, we'll need a separate thread/process
857             # that listens to unix socket and stores (some) log entries and
858             # display it to users when they connect and request them.
859             #
860             #if ($ospec->{create} && !(-e $ospec->{path})) {
861             # _debug("Creating Unix socket $ospec->{path} ...");
862             # require IO::Socket::UNIX::Util;
863             # IO::Socket::UNIX::Util::create_unix_socket(
864             # $ospec->{path});
865             #}
866             },
867 49         251 );
868             }
869              
870             sub _default_array {
871 0     0   0 my ($spec) = @_;
872 0         0 my $level = _set_level("array", "array", $spec);
873 0 0       0 if (!$level) {
874 0         0 $level = $spec->{level};
875 0         0 _debug("Set level of array to $level (general level)");
876             }
877             return {
878             level => $level,
879             category_level => _ifdefj($ENV{ARRAY_LOG_CATEGORY_LEVEL},
880             $ENV{LOG_CATEGORY_LEVEL},
881             $spec->{category_level}),
882             array => [],
883             max_elems => undef,
884             category => '',
885             pattern_style => _set_pattern_style('script_long'),
886             pattern => undef,
887              
888             filter_text => _ifdef($ENV{ARRAY_LOG_FILTER_TEXT}, $spec->{filter_text}),
889             filter_no_text => _ifdef($ENV{ARRAY_LOG_FILTER_NO_TEXT}, $spec->{filter_no_text}),
890             filter_citext => _ifdef($ENV{ARRAY_LOG_FILTER_CITEXT}, $spec->{filter_citext}),
891             filter_no_citext => _ifdef($ENV{ARRAY_LOG_FILTER_NO_CITEXT}, $spec->{filter_no_citext}),
892             filter_re => _ifdef($ENV{ARRAY_LOG_FILTER_RE}, $spec->{filter_re}),
893 0         0 filter_no_re => _ifdef($ENV{ARRAY_LOG_FILTER_NO_RE}, $spec->{filter_no_re}),
894             };
895             }
896              
897             sub _parse_opt_array {
898 49     49   79 my ($spec, $arg) = @_;
899              
900 49         122 _parse_opt_OUTPUT(
901             kind => 'array', default_sub => \&_default_array,
902             spec => $spec, arg => $arg,
903             );
904             }
905              
906             sub _set_pattern {
907 95     95   191 my ($s, $name) = @_;
908 95         261 _debug("Setting $name pattern ...");
909 95 50       232 unless (defined($s->{pattern})) {
910             die "BUG: neither pattern nor pattern_style is defined ($name)"
911 95 50       186 unless defined($s->{pattern_style});
912             die "Unknown pattern style for $name `$s->{pattern_style}`, ".
913             "use one of: ".join(", ", keys %PATTERN_STYLES)
914 95 50       233 unless defined($PATTERN_STYLES{ $s->{pattern_style} });
915 95         167 $s->{pattern} = $PATTERN_STYLES{ $s->{pattern_style} };
916 95         300 _debug("Set $name pattern to `$s->{pattern}` ".
917             "(from style `$s->{pattern_style}`)");
918             }
919             }
920              
921             sub _extract_category {
922 8     8   13 my ($ospec, $c) = @_;
923 8         16 my $c0 = _ifdef($c, $ospec->{category});
924 8         12 my @res;
925 8 50       17 if (ref($c0) eq 'ARRAY') { @res = @$c0 } else { @res = ($c0) }
  0         0  
  8         15  
926             # replace alias with real value
927 8         17 for (my $i=0; $i<@res; $i++) {
928 8         14 my $c1 = $res[$i];
929 8         12 my $a = $ospec->{main_spec}{category_alias}{$c1};
930 8 100       22 next unless defined($a);
931 2 50       6 if (ref($a) eq 'ARRAY') {
932 2         7 splice @res, $i, 1, @$a;
933 2         6 $i += (@$a-1);
934             } else {
935 0         0 $res[$i] = $a;
936             }
937             }
938 8         14 for (@res) {
939 10         27 s/::/./g;
940             # $_ = lc; # XXX do we need this?
941             }
942 8         22 @res;
943             }
944              
945             sub _cat2apd {
946 0     0   0 my $cat = shift;
947 0         0 $cat =~ s/[^A-Za-z0-9_]+/_/g;
948 0         0 $cat;
949             }
950              
951             sub _check_level {
952 25     25   60 my ($level, $from) = @_;
953 25 50       149 $level =~ /^(off|fatal|error|warn|info|debug|trace)$/i
954             or die "Unknown level (from $from): $level";
955 25         87 lc($1);
956             }
957              
958             sub _set_level {
959 144     144   269 my ($prefix, $which, $spec) = @_;
960             #use Data::Dump; dd $spec;
961 144 100       302 my $p_ = $prefix ? "${prefix}_" : "";
962 144 100       279 my $P_ = $prefix ? uc("${prefix}_") : "";
963 144 100       284 my $F_ = $prefix ? ucfirst("${prefix}_") : "";
964 144 100       258 my $pd = $prefix ? "${prefix}-" : "";
965 144 100       2261 my $pr = $prefix ? qr/$prefix(_|-)/ : qr//;
966 144         311 my ($level, $from);
967              
968 144         496 my @label2level =([trace=>"trace"], [debug=>"debug"],
969             [verbose=>"info"], [quiet=>"error"]);
970              
971 144 100       468 _debug("Setting ", ($which ? "level of $which" : "general level"), " ...");
972             SET:
973             {
974 144 50       217 if ($INC{"App/Options.pm"}) {
  144         338  
975 0         0 my $key;
976 0         0 for (qw/log_level loglevel/) {
977 0         0 $key = $p_ . $_;
978 0         0 _debug("Checking \$App::options{$key}: ", _ifdef($App::options{$key}, "(undef)"));
979 0 0       0 if ($App::options{$key}) {
980 0         0 $level = _check_level($App::options{$key}, "\$App::options{$key}");
981 0         0 $from = "\$App::options{$key}";
982 0         0 last SET;
983             }
984             }
985 0         0 for (@label2level) {
986 0         0 $key = $p_ . $_->[0];
987 0         0 _debug("Checking \$App::options{$key}: ", _ifdef($App::options{$key}, "(undef)"));
988 0 0       0 if ($App::options{$key}) {
989 0         0 $level = $_->[1];
990 0         0 $from = "\$App::options{$key}";
991 0         0 last SET;
992             }
993             }
994             }
995              
996 144         224 my $i = 0;
997 144         300 _debug("Checking \@ARGV ...");
998 144         305 while ($i < @ARGV) {
999 36         61 my $arg = $ARGV[$i];
1000 36         59 $from = "cmdline arg $arg";
1001 36 50       585 if ($arg =~ /^--${pr}log[_-]?level=(.+)/) {
1002 0         0 _debug("\$ARGV[$i] looks like an option to specify level: $arg");
1003 0         0 $level = _check_level($1, "ARGV $arg");
1004 0         0 last SET;
1005             }
1006 36 100 66     468 if ($arg =~ /^--${pr}log[_-]?level$/ and $i < @ARGV-1) {
1007 6         20 _debug("\$ARGV[$i] and \$ARGV[${\($i+1)}] looks like an option to specify level: $arg ", $ARGV[$i+1]);
  6         28  
1008 6         22 $level = _check_level($ARGV[$i+1], "ARGV $arg ".$ARGV[$i+1]);
1009 6         17 last SET;
1010             }
1011 30         70 for (@label2level) {
1012 120 100       2581 if ($arg =~ /^--${pr}$_->[0](=(1|yes|true))?$/i) {
1013 2         12 _debug("\$ARGV[$i] looks like an option to specify level: $arg");
1014 2         4 $level = $_->[1];
1015 2         6 last SET;
1016             }
1017             }
1018 28         91 $i++;
1019             }
1020              
1021 136         243 for (qw/LOG_LEVEL LOGLEVEL/) {
1022 270         480 my $key = $P_ . $_;
1023 270         774 _debug("Checking environment variable $key: ", _ifdef($ENV{$key}, "(undef)"));
1024 270 100       693 if ($ENV{$key}) {
1025 2         7 $level = _check_level($ENV{$key}, "ENV $key");
1026 2         7 $from = "\$ENV{$key}";
1027 2         5 last SET;
1028             }
1029             }
1030 134         255 for (@label2level) {
1031 533         968 my $key = $P_ . uc($_->[0]);
1032 533         1215 _debug("Checking environment variable $key: ", _ifdef($ENV{$key}, "(undef)"));
1033 533 100       1220 if ($ENV{$key}) {
1034 2         4 $level = $_->[1];
1035 2         4 $from = "\$ENV{$key}";
1036 2         5 last SET;
1037             }
1038             }
1039              
1040 132         170 for my $dir (@{$spec->{level_flag_paths}}) {
  132         278  
1041 252         416 for (@label2level) {
1042 999         2100 my $filename = "$dir/$spec->{name}." . $P_ . "log_level";
1043 999         7311 my $exists = -f $filename;
1044 999         2049 my $content;
1045 999 100       1714 if ($exists) {
1046 2         64 open my($f), $filename;
1047 2         41 $content = <$f>;
1048 2 50       11 chomp($content) if defined($content);
1049 2         21 close $f;
1050             }
1051 999 100       3354 _debug("Checking level flag file content $filename: ",
1052             (defined($content) ? $content : "(undef)"));
1053 999 100       1819 if (defined $content) {
1054 2         10 $level = _check_level($content,
1055             "level flag file $filename");
1056 2         5 $from = $filename;
1057 2         6 last SET;
1058             }
1059              
1060 997         2323 $filename = "$dir/$spec->{name}." . $P_ . uc($_->[0]);
1061 997         7534 $exists = -e $filename;
1062 997 100       3927 _debug("Checking level flag file $filename: ",
1063             ($exists ? "EXISTS" : 0));
1064 997 100       2504 if ($exists) {
1065 2         4 $level = $_->[1];
1066 2         4 $from = $filename;
1067 2         7 last SET;
1068             }
1069             }
1070             }
1071              
1072 2     2   20 no strict 'refs';
  2         13  
  2         934  
1073 128         452 for ("${F_}Log_Level", "${P_}LOG_LEVEL", "${p_}log_level",
1074             "${F_}LogLevel", "${P_}LOGLEVEL", "${p_}loglevel") {
1075 738         1258 my $varname = "main::$_";
1076 738         1785 _debug("Checking variable \$$varname: ", _ifdef($$varname, "(undef)"));
1077 738 100       1914 if ($$varname) {
1078 12         24 $from = "\$$varname";
1079 12         39 $level = _check_level($$varname, "\$$varname");
1080 12         34 last SET;
1081             }
1082             }
1083 116         198 for (@label2level) {
1084 452         1258 for my $varname (
1085             "main::$F_" . ucfirst($_->[0]),
1086             "main::$P_" . uc($_->[0])) {
1087 900         1990 _debug("Checking variable \$$varname: ", _ifdef($$varname, "(undef)"));
1088 900 100       2335 if ($$varname) {
1089 8         11 $from = "\$$varname";
1090 8         16 $level = $_->[1];
1091 8         16 last SET;
1092             }
1093             }
1094             }
1095             }
1096              
1097 144 100       389 _debug("Set ", ($which ? "level of $which" : "general level"), " to $level (from $from)") if $level;
    100          
1098 144         632 return $level;
1099             }
1100              
1101             # return the lower level (e.g. _min_level("debug", "INFO") -> INFO
1102             sub _min_level {
1103 40     40   74 my ($l1, $l2) = @_;
1104 40         107 my %vals = (OFF=>99,
1105             FATAL=>6, ERROR=>5, WARN=>4, INFO=>3, DEBUG=>2, TRACE=>1);
1106 40 100       149 $vals{uc($l1)} > $vals{uc($l2)} ? $l2 : $l1;
1107             }
1108              
1109             sub _export_logger {
1110 1     1   3 my ($caller) = @_;
1111 1         3 my $log_for_caller = Log::Any->get_logger(category => $caller);
1112 1         2053 my $varname = "$caller\::log";
1113 2     2   16 no strict 'refs';
  2         4  
  2         308  
1114 1         4 *$varname = \$log_for_caller;
1115             }
1116              
1117             sub _debug {
1118 5169 50   5169   9628 return unless $ENV{LOGANYAPP_DEBUG};
1119 0 0       0 print $dbg_ctx, ": " if $dbg_ctx;
1120 0         0 print @_, "\n";
1121             }
1122              
1123             sub import {
1124 2     2   31 my ($self, @args) = @_;
1125 2         4 my $caller = caller();
1126 2         7 _parse_args(\@args, $caller);
1127 2         73 $init_args = \@args;
1128             }
1129              
1130             {
1131 2     2   14 no warnings;
  2         4  
  2         208  
1132             # if we are loaded at run-time, it's too late to run INIT blocks, so user
1133             # must call init() manually. but sometimes this is what the user wants. so
1134             # shut up perl warning.
1135             INIT {
1136 2     2   11 my $caller = caller();
1137 2         7 init($init_args, $caller);
1138             }
1139             }
1140              
1141             1;
1142             # ABSTRACT: An easy way to use Log::Any in applications
1143              
1144             __END__
1145              
1146             =pod
1147              
1148             =encoding UTF-8
1149              
1150             =head1 NAME
1151              
1152             Log::Any::App - An easy way to use Log::Any in applications
1153              
1154             =head1 VERSION
1155              
1156             This document describes version 0.540 of Log::Any::App (from Perl distribution Log-Any-App), released on 2019-01-09.
1157              
1158             =head1 SYNOPSIS
1159              
1160             Most of the time you only need to do this:
1161              
1162             # in your script.pl
1163             use Log::Any::App '$log';
1164             $log->warn("blah ...");
1165             if ($log->is_debug) { ... }
1166              
1167             # or, in command line
1168             % perl -MLog::Any::App -MModuleThatUsesLogAny -e'...'
1169              
1170             Here's the default logging that Log::Any::App sets up for you:
1171              
1172             Condition screen file syslog dir
1173             --------------------------------+-------+------------------+-------------+---
1174             -e (one-liners) y - - -
1175             Scripts running as normal user y ~/NAME.log - -
1176             Scripts running as root y /var/log/NAME.log - -
1177             Daemons - y y -
1178              
1179             You can customize level from outside the script, using environment variables or
1180             command-line options (won't interfere with command-line processing modules like
1181             Getopt::Long etc):
1182              
1183             % DEBUG=1 script.pl
1184             % LOG_LEVEL=trace script.pl
1185             % script.pl --verbose
1186              
1187             And to customize other stuffs:
1188              
1189             use Log::Any::App '$log',
1190             -syslog => 1, # turn on syslog logging explicitly
1191             -screen => 0, # turn off screen logging explicitly
1192             -file => {path=>'/foo/bar', max_size=>'10M', histories=>10};
1193             # customize file logging
1194              
1195             For more customization like categories, per-category level, per-output level,
1196             multiple outputs, string patterns, etc see L</USING AND EXAMPLES> and init().
1197              
1198             =head1 DESCRIPTION
1199              
1200             IMPORTANT: Please read L</"ROAD TO 1.0"> on some incompatibilities in the near
1201             future, before 1.0 is released.
1202              
1203             Log::Any::App is a convenient combo for L<Log::Any> and L<Log::Log4perl>
1204             (although alternative backends beside Log4perl might be considered in the
1205             future). To use Log::Any::App you need to be sold on the idea of Log::Any first,
1206             so please do a read up on that first.
1207              
1208             The goal of Log::Any::App is to provide developers an easy and concise way to
1209             add logging to their I<*applications*>. That is, instead of modules; modules
1210             remain using Log::Any to produce logs. Applications can upgrade to full Log4perl
1211             later when necessary, although in my experience, they usually don't.
1212              
1213             With Log::Any::App, you can replace this code in your application:
1214              
1215             use Log::Any '$log';
1216             use Log::Any::Adapter;
1217             use Log::Log4perl;
1218             my $log4perl_config = '
1219             some
1220             long
1221             multiline
1222             config...';
1223             Log::Log4perl->init(\$log4perl_config);
1224             Log::Any::Adapter->set('Log4perl');
1225              
1226             with just this:
1227              
1228             use Log::Any::App '$log'; # plus some other options when necessary
1229              
1230             Most of the time you don't need to configure anything as Log::Any::App will
1231             construct the most appropriate default Log4perl configuration for your
1232             application.
1233              
1234             =head1 USING AND EXAMPLES
1235              
1236             To use Log::Any::App, just do:
1237              
1238             use Log::Any::App '$log';
1239              
1240             or from the command line:
1241              
1242             % perl -MLog::Any::App -MModuleThatUsesLogAny -e ...
1243              
1244             This will send logs to screen as well as file (unless -e scripts, which only log
1245             to screen). Default log file is ~/$SCRIPT_NAME.log, or /var/log/$SCRIPT_NAME.log
1246             if script is running as root. Default level is 'warn'.
1247              
1248             The 'use Log::Any::App' statement can be issued before or after the modules that
1249             use Log::Any, it doesn't matter. Logging will be initialized in the INIT phase
1250             by Log::Any::App.
1251              
1252             You are not required to import '$log', and don't need to if you do not produce
1253             logs in your application (only in the modules).
1254              
1255             =head2 Changing logging level
1256              
1257             Since one of the most commonly tweaked logging setting is level (for example:
1258             increasing level when debugging problems), Log::Any::App provides several
1259             mechanisms to change log level, either from the script or from outside the
1260             script, for your convenience. Below are the mechanisms, ordered from highest
1261             priority:
1262              
1263             =over 4
1264              
1265             =item * import argument (inside the script)
1266              
1267             =item * command line arguments (outside the script)
1268              
1269             =item * environment variables (outside the script)
1270              
1271             =item * level flag files (outside the script)
1272              
1273             =item * variables in 'main' package (inside the script)
1274              
1275             =back
1276              
1277             These mechanisms are explained in more details in the documentation for the
1278             B<init()> function. But below are some examples.
1279              
1280             To change level from inside the script:
1281              
1282             use Log::Any::App '$log', -level => 'debug';
1283              
1284             This is useful if you want a fixed level that cannot be overridden by other
1285             mechanisms (since setting level using import argument has the highest priority).
1286             But oftentimes what you want is changing level without modifying the script
1287             itself. Thereby, just write:
1288              
1289             use Log::Any::App '$log';
1290              
1291             and then you can use environment variables to change level:
1292              
1293             TRACE=1 script.pl; # setting level to trace
1294             DEBUG=1 script.pl; # setting level to debug
1295             VERBOSE=1 script.pl; # setting level to info
1296             QUIET=1 script.pl; # setting level to error
1297             LOG_LEVEL=trace script.pl; # setting a specific log level
1298              
1299             or command-line options:
1300              
1301             script.pl --trace
1302             script.pl --debug
1303             script.pl --verbose
1304             script.pl --quiet
1305             script.pl --log_level=debug; # '--log-level debug' will also do
1306              
1307             Regarding command-line options: Log::Any::App won't consume the command-line
1308             options from @ARGV and thus won't interfere with command-line processing modules
1309             like L<Getopt::Long> or L<App::Options>. If you use a command-line processing
1310             module and plan to use command-line options to set level, you might want to
1311             define these level options, or your command-line processing module will complain
1312             about unknown options.
1313              
1314             =head2 Changing default level
1315              
1316             The default log level is 'warn'. To change the default level, you can use 'main'
1317             package variables (since they have the lowest priority):
1318              
1319             use Log::Any::App '$log';
1320             BEGIN { our $Log_Level = 'info' } # be more verbose by default
1321              
1322             Then you will still be able to use level flag files or environment variables or
1323             command-line options to override this setting.
1324              
1325             =head2 Changing per-output level
1326              
1327             Logging level can also be specified on a per-output level. For example, if you
1328             want your script to be chatty on the screen but still logs to file at the
1329             default 'warn' level:
1330              
1331             SCREEN_VERBOSE=1 script.pl
1332             SCREEN_DEBUG=1 script.pl
1333             SCREEN_TRACE=1 script.pl
1334             SCREEN_LOG_LEVEL=info script.pl
1335              
1336             script.pl --screen_verbose
1337             script.pl --screen-debug
1338             script.pl --screen-trace=1
1339             script.pl --screen-log-level=info
1340              
1341             Similarly, to set only file level, use FILE_VERBOSE, FILE_LOG_LEVEL,
1342             --file-trace, and so on.
1343              
1344             =head2 Setting default per-output level
1345              
1346             As with setting default level, you can also set default level on a per-output
1347             basis:
1348              
1349             use Log::Any::App '$log';
1350             BEGIN {
1351             our $Screen_Log_Level = 'off';
1352             our $File_Quiet = 1; # setting file level to 'error'
1353             # and so on
1354             }
1355              
1356             If a per-output level is not specified, it will default to the general log level.
1357              
1358             =head2 Enabling/disabling output
1359              
1360             To disable a certain output, you can do this:
1361              
1362             use Log::Any::App '$log', -file => 0;
1363              
1364             or:
1365              
1366             use Log::Any::App '$log', -screen => {level=>'off'};
1367              
1368             and this won't allow the output to be re-enabled from outside the script. However
1369             if you do this:
1370              
1371             use Log::Any::App;
1372             BEGIN { our $Screen_Log_Level = 'off' }
1373              
1374             then by default screen logging is turned off but you will be able to override
1375             the screen log level using level flag files or environment variables or
1376             command-line options (SCREEN_DEBUG, --screen-verbose, and so on).
1377              
1378             =head2 Changing log level of cron scripts
1379              
1380             Environment variables and command-line options allow changing log level without
1381             modifying the script. But for scripts specified in crontab, they still require
1382             changing crontab entries, e.g.:
1383              
1384             # turn on debugging
1385             */5 * * * * DEBUG=1 foo
1386              
1387             # be silent
1388             */5 * * * * bar --quiet
1389              
1390             Another mechanism, level flag file, is useful in this case. By doing:
1391              
1392             $ echo debug > ~/foo.log_level
1393             # touch /etc/bar.QUIET
1394              
1395             you can also change log levels without modifying your crontab.
1396              
1397             =head2 Changing log file name/location
1398              
1399             By default Log::Any::App will log to file to ~/$NAME.log (or /var/log/$NAME.log
1400             if script is running as root), where $NAME is taken from the basename of $0. But
1401             this can be changed using:
1402              
1403             use Log::Any::App '$log', -name => 'myprog';
1404              
1405             Or, using custom path:
1406              
1407             use Log::Any::App '$log', -file => '/path/to/file';
1408              
1409             =head2 Changing other output parameters
1410              
1411             Each output argument can accept a hashref to specify various options. For
1412             example:
1413              
1414             use Log::Any::App '$log',
1415             -screen => {color=>0}, # never use color
1416             -file => {path=>'/var/log/foo',
1417             max_size=>'10M',
1418             histories=>10,
1419             },
1420              
1421             For all the available options of each output, see the init() function.
1422              
1423             =head2 Logging to syslog
1424              
1425             Logging to syslog is enabled by default if your script looks like or declare
1426             that it is a daemon, e.g.:
1427              
1428             use Net::Daemon; # this indicate your program is a daemon
1429             use Log::Any::App; # syslog logging will be turned on by default
1430              
1431             use Log::Any::App -daemon => 1; # script declares that it is a daemon
1432              
1433             # idem
1434             package main;
1435             our $IS_DAEMON = 1;
1436              
1437             But if you are certain you don't want syslog logging:
1438              
1439             use Log::Any::App -syslog => 0;
1440              
1441             =head2 Logging to directory
1442              
1443             This is done using L<Log::Dispatch::Dir> where each log message is logged to a
1444             different file in a specified directory. By default logging to dir is not turned
1445             on, to turn it on:
1446              
1447             use Log::Any::App '$log', -dir => 1;
1448              
1449             For all the available options of directory output, see the init() function.
1450              
1451             =head2 Multiple outputs
1452              
1453             Each output argument can accept an arrayref to specify more than one output. For
1454             example below is a code to log to three files:
1455              
1456             use Log::Any::App '$log',
1457             -file => [1, # default, to ~/$NAME.log or /var/log/$NAME.log
1458             "/var/log/log1",
1459             {path=>"/var/log/debug_foo", category=>'Foo', level=>'debug'}];
1460              
1461             =head2 Changing level of certain module(s)
1462              
1463             Suppose you want to shut up logs from modules Foo, Bar::Baz, and Qux (and their
1464             submodules as well, e.g. Foo::Alpha, Bar::Baz::Beta::Gamma) because they are too
1465             noisy:
1466              
1467             use Log::Any::App '$log',
1468             -category_level => { Foo => 'off', 'Bar::Baz' => 'off', Qux => 'off' };
1469              
1470             or (same thing):
1471              
1472             use Log::Any::App '$log',
1473             -category_alias => { -noisy => [qw/Foo Bar::Baz Qux/] },
1474             -category_level => { -noisy => 'off' };
1475              
1476             You can even specify this on a per-output basis. Suppose you only want to shut
1477             up the noisy modules on the screen, but not on the file:
1478              
1479             use Log::Any::App '$log',
1480             -category_alias => { -noisy => [qw/Foo Bar::Baz Qux/] },
1481             -screen => { category_level => { -noisy => 'off' } };
1482              
1483             Or perhaps, you want to shut up the noisy modules everywhere, except on the
1484             screen:
1485              
1486             use Log::Any::App '$log',
1487             -category_alias => { -noisy => [qw/Foo Bar::Baz Qux/] },
1488             -category_level => { -noisy => 'off' },
1489             -syslog => 1, # uses general -category_level
1490             -file => "/var/log/foo", # uses general -category_level
1491             -screen => { category_level => {} }; # overrides general -category_level
1492              
1493             You can also do this from the outside the script using environment variable,
1494             which is more flexible. Encode data structure using JSON:
1495              
1496             % LOG_SHOW_CATEGORY=1 \
1497             LOG_CATEGORY_ALIAS='{"-noisy":["Foo","Bar::Baz","Quz"]}' \
1498             LOG_CATEGORY_LEVEL='{"-noisy":"off"}' script.pl ...
1499              
1500             =head2 Only displaying log from certain module(s)
1501              
1502             Use a combination of LOG_LEVEL and LOG_CATEGORY_LEVEL. For example:
1503              
1504             % LOG_LEVEL=off LOG_CATEGORY_LEVEL='{"Foo.Bar":"trace", "Baz":"info"}' \
1505             script.pl ...
1506              
1507             =head2 Displaying category name
1508              
1509             % LOG_SHOW_CATEGORY=1 script.pl ...
1510              
1511             Now instead of:
1512              
1513             [25] Starting baz ritual ...
1514              
1515             now log messages will be prefixed with category:
1516              
1517             [cat Foo.Bar][25] Starting baz ritual ...
1518              
1519             =head2 Displaying location name
1520              
1521             % LOG_SHOW_LOCATION=1 script.pl ...
1522              
1523             Now log messages will be prefixed with location (function/file/line number)
1524             information:
1525              
1526             [loc Foo::Bar lib/Foo/Bar.pm (12)][25] Starting baz ritual ...
1527              
1528             =head2 Preventing logging level to be changed from outside the script
1529              
1530             Sometimes, for security/audit reasons, you don't want to allow script caller to
1531             change logging level. As explained previously, you can use the 'level' import
1532             argument (the highest priority of level-setting):
1533              
1534             use Log::Any::App '$log', -level => 'debug'; # always use debug level
1535              
1536             TODO: Allow something like 'debug+' to allow other mechanisms to *increase* the
1537             level but not decrease it. Or 'debug-' to allow other mechanisms to decrease
1538             level but not increase it. And finally 'debug,trace' to specify allowable levels
1539             (is this necessary?)
1540              
1541             =head2 Debugging
1542              
1543             To see the Log4perl configuration that is generated by Log::Any::App and how it
1544             came to be, set environment LOGANYAPP_DEBUG to true.
1545              
1546             =head1 PATTERN STYLES
1547              
1548             Log::Any::App provides some styles for Log4perl patterns. You can specify
1549             C<pattern_style> instead of directly specifying C<pattern>. example:
1550              
1551             use Log::Any::App -screen => {pattern_style=>"script_long"};
1552              
1553             Name Description Example output
1554             ---- ----------- --------------
1555             plain The message, the whole message, Message
1556             and nothing but the message.
1557             Used by dir logging.
1558              
1559             Equivalent to pattern: '%m'
1560              
1561             plain_nl Message plus newline. The default Message
1562             for screen without
1563             LOG_ELAPSED_TIME_IN_SCREEN.
1564              
1565             Equivalent to pattern: '%m%n'
1566              
1567             script_short For scripts that run for a short [234] Message
1568             time (a few seconds). Shows just
1569             the number of milliseconds. This
1570             is the default for screen under
1571             LOG_ELAPSED_TIME_IN_SCREEN.
1572              
1573             Equivalent to pattern:
1574             '[%r] %m%n'
1575              
1576             script_long Scripts that will run for a [2010-04-22 18:01:02] Message
1577             while (more than a few seconds).
1578             Shows date/time.
1579              
1580             Equivalent to pattern:
1581             '[%d] %m%n'
1582              
1583             daemon For typical daemons. Shows PID [pid 1234] [2010-04-22 18:01:02] Message
1584             and date/time. This is the
1585             default for file logging.
1586              
1587             Equivalent to pattern:
1588             '[pid %P] [%d] %m%n'
1589              
1590             syslog Style suitable for syslog [pid 1234] Message
1591             logging.
1592              
1593             Equivalent to pattern:
1594             '[pid %p] %m'
1595              
1596             For each of the above there are also C<cat_XXX> (e.g. C<cat_script_long>) which
1597             are the same as XXX but with C<[cat %c]> in front of the pattern. It is used
1598             mainly to show categories and then filter by categories. You can turn picking
1599             default pattern style with category using environment variable
1600             LOG_SHOW_CATEGORY.
1601              
1602             And for each of the above there are also C<loc_XXX> (e.g. C<loc_syslog>) which
1603             are the same as XXX but with C<[loc %l]> in front of the pattern. It is used to
1604             show calling location (file, function/method, and line number). You can turn
1605             picking default pattern style with location prefix using environment variable
1606             LOG_SHOW_LOCATION.
1607              
1608             If you have a favorite pattern style, please do share them.
1609              
1610             =head1 BUGS/TODOS
1611              
1612             Need to provide appropriate defaults for Windows/other OS.
1613              
1614             =head1 ROAD TO 1.0
1615              
1616             Here are some planned changes/development before 1.0 is reached. There might be
1617             some incompatibilities, please read this section carefully.
1618              
1619             =over 4
1620              
1621             =item * Everything is configurable via environment/command-line/option file
1622              
1623             As I I<love> specifying log options from environment, I will make I<every>
1624             init() options configurable from outside the script
1625             (environment/command-line/control file). Of course, init() arguments still take
1626             precedence for authors that do not want some/all options to be overridden from
1627             outside.
1628              
1629             =item * Reorganization of command-line/environment names
1630              
1631             Aside from the handy and short TRACE (--trace), DEBUG, VERBOSE, QUIET, all the
1632             other environment names will be put under LOG_ prefix. This means FILE_LOG_LEVEL
1633             will be changed to LOG_FILE_LEVEL, and so on. SCREEN_VERBOSE will be changed to
1634             VERBOSE_SCREEN.
1635              
1636             This is meant to reduce "pollution" of the environment variables namespace.
1637              
1638             Log option file (option file for short, previously "flag file") will be searched
1639             in <PROG>.log_options. Its content is in JSON and will become init() arguments.
1640             For example:
1641              
1642             {"file": 1, "screen":{"level":"trace"}}
1643              
1644             or more akin to init() (both will be supported):
1645              
1646             ["-file": 1, "-screen":{"level":"trace"}]
1647              
1648             =item * Possible reorganization of package variable names
1649              
1650             To be more strict and reduce confusion, case variation might not be searched.
1651              
1652             =item * Pluggable backend
1653              
1654             This is actually the main motivator to reach 1.0 and all these changes. Backends
1655             will be put in Log::Any::App::Backend::Log4perl, and so on.
1656              
1657             =item * Pluggable output
1658              
1659             Probably split to Log::Any::App::Output::file, and so on. Each output needs
1660             its backend support.
1661              
1662             =item * App::Options support will probably be dropped
1663              
1664             I no longer use App::Options these days, and I don't know of any Log::Any::App
1665             user who does.
1666              
1667             =item * Probably some hooks to allow for more flexibility.
1668              
1669             For example, if user wants to parse or detect levels/log file paths/etc from
1670             some custom logic.
1671              
1672             =back
1673              
1674             =head1 FUNCTIONS
1675              
1676             None is exported.
1677              
1678             =head2 init(\@args)
1679              
1680             This is the actual function that implements the setup and configuration of
1681             logging. You normally need not call this function explicitly (but see below), it
1682             will be called once in an INIT block. In fact, when you do:
1683              
1684             use Log::Any::App 'a', 'b', 'c';
1685              
1686             it is actually passed as:
1687              
1688             init(['a', 'b', 'c']);
1689              
1690             You will need to call init() manually if you require Log::Any::App at runtime,
1691             in which case it is too late to run INIT block. If you want to run Log::Any::App
1692             in runtime phase, do this:
1693              
1694             require Log::Any::App;
1695             Log::Any::App::init(['a', 'b', 'c']);
1696              
1697             Arguments to init can be one or more of:
1698              
1699             =over 4
1700              
1701             =item -log => BOOL
1702              
1703             Whether to do log at all. Default is from LOG environment variable, or 1. This
1704             option is only to allow users to disable Log::Any::App (thus speeding up startup
1705             by avoiding loading Log4perl, etc) by passing LOG=1 environment when running
1706             programs. However, if you explicitly set this option to 1, Log::Any::App cannot
1707             be disabled this way.
1708              
1709             =item -init => BOOL
1710              
1711             Whether to call Log::Log4perl->init() after setting up the Log4perl
1712             configuration. Default is true. You can set this to false, and you can
1713             initialize Log4perl yourself (but then there's not much point in using this
1714             module, right?)
1715              
1716             =item -name => STRING
1717              
1718             Change the program name. Default is taken from $0.
1719              
1720             =item -level_flag_paths => ARRAY OF STRING
1721              
1722             Edit level flag file locations. The default is [$homedir, "/etc"].
1723              
1724             =item -daemon => BOOL
1725              
1726             Declare that script is a daemon. Default is no. Aside from this, to declare that
1727             your script is a daemon you can also set $main::IS_DAEMON to true.
1728              
1729             =item -category_alias => {ALIAS=>CATEGORY, ...}
1730              
1731             Create category aliases so the ALIAS can be used in place of real categories in
1732             each output's category specification. For example, instead of doing this:
1733              
1734             init(
1735             -file => [category=>[qw/Foo Bar Baz/], ...],
1736             -screen => [category=>[qw/Foo Bar Baz/]],
1737             );
1738              
1739             you can do this instead:
1740              
1741             init(
1742             -category_alias => {-fbb => [qw/Foo Bar Baz/]},
1743             -file => [category=>'-fbb', ...],
1744             -screen => [category=>'-fbb', ...],
1745             );
1746              
1747             You can also specify this from the environment variable LOG_CATEGORY_ALIAS using
1748             JSON encoding, e.g.
1749              
1750             LOG_CATEGORY_ALIAS='{"-fbb":["Foo","Bar","Baz"]}'
1751              
1752             =item -category_level => {CATEGORY=>LEVEL, ...}
1753              
1754             Specify per-category level. Categories not mentioned on this will use the
1755             general level (-level). This can be used to increase or decrease logging on
1756             certain categories/modules.
1757              
1758             You can also specify this from the environment variable LOG_CATEGORY_LEVEL using
1759             JSON encoding, e.g.
1760              
1761             LOG_CATEGORY_LEVEL='{"-fbb":"off"}'
1762              
1763             =item -level => 'trace'|'debug'|'info'|'warn'|'error'|'fatal'|'off'
1764              
1765             Specify log level for all outputs. Each output can override this value. The
1766             default log level is determined as follow:
1767              
1768             B<Search in command-line options>. If L<App::Options> is present, these keys are
1769             checked in B<%App::options>: B<log_level>, B<trace> (if true then level is
1770             C<trace>), B<debug> (if true then level is C<debug>), B<verbose> (if true then
1771             level is C<info>), B<quiet> (if true then level is C<error>).
1772              
1773             Otherwise, it will try to scrape @ARGV for the presence of B<--log-level>,
1774             B<--trace>, B<--debug>, B<--verbose>, or B<--quiet> (this usually works because
1775             Log::Any::App does this in the INIT phase, before you call L<Getopt::Long>'s
1776             GetOptions() or the like).
1777              
1778             B<Search in environment variables>. Otherwise, it will look for environment
1779             variables: B<LOG_LEVEL>, B<QUIET>. B<VERBOSE>, B<DEBUG>, B<TRACE>.
1780              
1781             B<Search in level flag files>. Otherwise, it will look for existence of files
1782             with one of these names C<$NAME.QUIET>, C<$NAME.VERBOSE>, C<$NAME.TRACE>,
1783             C<$NAME.DEBUG>, or content of C<$NAME.log_level> in ~ or /etc.
1784              
1785             B<Search in main package variables>. Otherwise, it will try to search for
1786             package variables in the C<main> namespace with names like C<$Log_Level> or
1787             C<$LOG_LEVEL> or C<$log_level>, C<$Quiet> or C<$QUIET> or C<$quiet>, C<$Verbose>
1788             or C<$VERBOSE> or C<$verbose>, C<$Trace> or C<$TRACE> or C<$trace>, C<$Debug> or
1789             C<$DEBUG> or C<$debug>.
1790              
1791             If everything fails, it defaults to 'warn'.
1792              
1793             =item -filter_text => STR
1794              
1795             Only show log lines matching STR. Default from C<LOG_FILTER_TEXT> environment.
1796              
1797             =item -filter_no_text => STR
1798              
1799             Only show log lines not matching STR. Default from C<LOG_FILTER_NO_TEXT>
1800             environment.
1801              
1802             =item -filter_citext => STR
1803              
1804             Only show log lines matching STR (case insensitive). Default from
1805             C<LOG_FILTER_CITEXT> environment.
1806              
1807             =item -filter_no_citext => STR
1808              
1809             Only show log lines not matching STR (case insensitive). Default from
1810             C<LOG_FILTER_NO_CITEXT> environment.
1811              
1812             =item -filter_re => RE
1813              
1814             Only show log lines matching regex pattern RE. Default from C<LOG_FILTER_RE>
1815             environment.
1816              
1817             =item -filter_no_re => RE
1818              
1819             Only show log lines not matching regex pattern RE. Default from
1820             C<LOG_FILTER_NO_RE> environment.
1821              
1822             =item -file => 0 | 1|yes|true | PATH | {opts} | [{opts}, ...]
1823              
1824             Specify output to one or more files, using L<Log::Dispatch::FileWriteRotate>.
1825              
1826             If the argument is a false boolean value, file logging will be turned off. If
1827             argument is a true value that matches /^(1|yes|true)$/i, file logging will be
1828             turned on with default path, etc. If the argument is another scalar value then
1829             it is assumed to be a path. If the argument is a hashref, then the keys of the
1830             hashref must be one of: C<level>, C<path>, C<max_size> (maximum size before
1831             rotating, in bytes, 0 means unlimited or never rotate), C<histories> (number of
1832             old files to keep, excluding the current file), C<suffix> (will be passed to
1833             Log::Dispatch::FileWriteRotate's constructor), C<period> (will be passed to
1834             Log::Dispatch::FileWriteRotate's constructor), C<buffer_size> (will be passed to
1835             Log::Dispatch::FileWriteRotate's constructor), C<category> (a string of ref to
1836             array of strings), C<category_level> (a hashref, similar to -category_level),
1837             C<pattern_style> (see L<"PATTERN STYLES">), C<pattern> (Log4perl pattern),
1838             C<filter_text>, C<filter_no_text>, C<filter_citext>, C<filter_no_citext>,
1839             C<filter_re>, C<filter_no_re>.
1840              
1841             If the argument is an arrayref, it is assumed to be specifying multiple files,
1842             with each element of the array as a hashref.
1843              
1844             How Log::Any::App determines defaults for file logging:
1845              
1846             If program is a one-liner script specified using "perl -e", the default is no
1847             file logging. Otherwise file logging is turned on.
1848              
1849             If the program runs as root, the default path is C</var/log/$NAME.log>, where
1850             $NAME is taken from B<$0> (or C<-name>). Otherwise the default path is
1851             ~/$NAME.log. Intermediate directories will be made with L<File::Path>.
1852              
1853             If specified C<path> ends with a slash (e.g. "/my/log/"), it is assumed to be a
1854             directory and the final file path is directory appended with $NAME.log.
1855              
1856             Default rotating behaviour is no rotate (max_size = 0).
1857              
1858             Default level for file is the same as the global level set by B<-level>. But
1859             App::options, command line, environment, level flag file, and package variables
1860             in main are also searched first (for B<FILE_LOG_LEVEL>, B<FILE_TRACE>,
1861             B<FILE_DEBUG>, B<FILE_VERBOSE>, B<FILE_QUIET>, and the similars).
1862              
1863             You can also specify category level from environment FILE_LOG_CATEGORY_LEVEL.
1864              
1865             =item -dir => 0 | 1|yes|true | PATH | {opts} | [{opts}, ...]
1866              
1867             Log messages using L<Log::Dispatch::Dir>. Each message is logged into separate
1868             files in the directory. Useful for dumping content (e.g. HTML, network dumps, or
1869             temporary results).
1870              
1871             If the argument is a false boolean value, dir logging will be turned off. If
1872             argument is a true value that matches /^(1|yes|true)$/i, dir logging will be
1873             turned on with defaults path, etc. If the argument is another scalar value then
1874             it is assumed to be a directory path. If the argument is a hashref, then the
1875             keys of the hashref must be one of: C<level>, C<path>, C<max_size> (maximum
1876             total size of files before deleting older files, in bytes, 0 means unlimited),
1877             C<max_age> (maximum age of files to keep, in seconds, undef means unlimited).
1878             C<histories> (number of old files to keep, excluding the current file),
1879             C<category>, C<category_level> (a hashref, similar to -category_level),
1880             C<pattern_style> (see L<"PATTERN STYLES">), C<pattern> (Log4perl pattern),
1881             C<filename_pattern> (pattern of file name), C<filter_text>, C<filter_no_text>,
1882             C<filter_citext>, C<filter_no_citext>, C<filter_re>, C<filter_no_re>.
1883              
1884             If the argument is an arrayref, it is assumed to be specifying multiple
1885             directories, with each element of the array as a hashref.
1886              
1887             How Log::Any::App determines defaults for dir logging:
1888              
1889             Directory logging is by default turned off. You have to explicitly turn it on.
1890              
1891             If the program runs as root, the default path is C</var/log/$NAME/>, where $NAME
1892             is taken from B<$0>. Otherwise the default path is ~/log/$NAME/. Intermediate
1893             directories will be created with File::Path. Program name can be changed using
1894             C<-name>.
1895              
1896             Default rotating parameters are: histories=1000, max_size=0, max_age=undef.
1897              
1898             Default level for dir logging is the same as the global level set by B<-level>.
1899             But App::options, command line, environment, level flag file, and package
1900             variables in main are also searched first (for B<DIR_LOG_LEVEL>, B<DIR_TRACE>,
1901             B<DIR_DEBUG>, B<DIR_VERBOSE>, B<DIR_QUIET>, and the similars).
1902              
1903             You can also specify category level from environment DIR_LOG_CATEGORY_LEVEL.
1904              
1905             =item -screen => 0 | 1|yes|true | {opts}
1906              
1907             Log messages using L<Log::Log4perl::Appender::ScreenColoredLevels>.
1908              
1909             If the argument is a false boolean value, screen logging will be turned off. If
1910             argument is a true value that matches /^(1|yes|true)$/i, screen logging will be
1911             turned on with default settings. If the argument is a hashref, then the keys of
1912             the hashref must be one of: C<color> (default is true, set to 0 to turn off
1913             color), C<stderr> (default is true, set to 0 to log to stdout instead),
1914             C<level>, C<category>, C<category_level> (a hashref, similar to
1915             -category_level), C<pattern_style> (see L<"PATTERN STYLE">), C<pattern>
1916             (Log4perl string pattern), C<filter_text>, C<filter_no_text>, C<filter_citext>,
1917             C<filter_no_citext>, C<filter_re>, C<filter_no_re>.
1918              
1919             How Log::Any::App determines defaults for screen logging:
1920              
1921             Screen logging is turned on by default.
1922              
1923             Default level for screen logging is the same as the global level set by
1924             B<-level>. But App::options, command line, environment, level flag file, and
1925             package variables in main are also searched first (for B<SCREEN_LOG_LEVEL>,
1926             B<SCREEN_TRACE>, B<SCREEN_DEBUG>, B<SCREEN_VERBOSE>, B<SCREEN_QUIET>, and the
1927             similars).
1928              
1929             Color can also be turned on/off using environment variable COLOR (if B<color>
1930             argument is not set).
1931              
1932             You can also specify category level from environment SCREEN_LOG_CATEGORY_LEVEL.
1933              
1934             =item -syslog => 0 | 1|yes|true | {opts}
1935              
1936             Log messages using L<Log::Dispatch::Syslog>.
1937              
1938             If the argument is a false boolean value, syslog logging will be turned off. If
1939             argument is a true value that matches /^(1|yes|true)$/i, syslog logging will be
1940             turned on with default level, ident, etc. If the argument is a hashref, then the
1941             keys of the hashref must be one of: C<level>, C<ident>, C<facility>,
1942             C<category>, C<category_level> (a hashref, similar to -category_level),
1943             C<pattern_style> (see L<"PATTERN STYLES">), C<pattern> (Log4perl pattern),
1944             C<filter_text>, C<filter_no_text>, C<filter_citext>, C<filter_no_citext>,
1945             C<filter_re>, C<filter_no_re>.
1946              
1947             How Log::Any::App determines defaults for syslog logging:
1948              
1949             If a program is a daemon (determined by detecting modules like L<Net::Server> or
1950             L<Proc::PID::File>, or by checking if -daemon or $main::IS_DAEMON is true) then
1951             syslog logging is turned on by default and facility is set to C<daemon>,
1952             otherwise the default is off.
1953              
1954             Ident is program's name by default ($0, or C<-name>).
1955              
1956             Default level for syslog logging is the same as the global level set by
1957             B<-level>. But App::options, command line, environment, level flag file, and
1958             package variables in main are also searched first (for B<SYSLOG_LOG_LEVEL>,
1959             B<SYSLOG_TRACE>, B<SYSLOG_DEBUG>, B<SYSLOG_VERBOSE>, B<SYSLOG_QUIET>, and the
1960             similars).
1961              
1962             You can also specify category level from environment SYSLOG_LOG_CATEGORY_LEVEL.
1963              
1964             =item -unixsock => 0 | 1|yes|true | PATH | {opts} | [{opts}, ...]
1965              
1966             Specify output to one or more B<existing, listening, datagram> Unix domain
1967             sockets, using L<Log::Log4perl::Appender::Socket::UNIX>.
1968              
1969             The listening end might be a different process, or the same process using a
1970             different thread of nonblocking I/O. It usually makes little sense to make the
1971             same program the listening end. If you want, for example, to let a client
1972             connects to your program to see logs being produced, you might want to setup an
1973             in-memory output (C<-array>) and create another thread or non-blocking I/O to
1974             listen to client requests and show them the content of the array when requested.
1975              
1976             If the argument is a false boolean value, Unix domain socket logging will be
1977             turned off. If argument is a true value that matches /^(1|yes|true)$/i, Unix
1978             domain socket logging will be turned on with default path, etc. If the argument
1979             is another scalar value then it is assumed to be a path. If the argument is a
1980             hashref, then the keys of the hashref must be one of: C<level>, C<path>,
1981             C<filter_text>, C<filter_no_text>, C<filter_citext>, C<filter_no_citext>,
1982             C<filter_re>, C<filter_no_re>.
1983              
1984             If the argument is an arrayref, it is assumed to be specifying multiple sockets,
1985             with each element of the array as a hashref.
1986              
1987             How Log::Any::App determines defaults for Unix domain socket logging:
1988              
1989             By default Unix domain socket logging is off.
1990              
1991             If the program runs as root, the default path is C</var/run/$NAME-log.sock>,
1992             where $NAME is taken from B<$0> (or C<-name>). Otherwise the default path is
1993             ~/$NAME-log.sock.
1994              
1995             If specified C<path> ends with a slash (e.g. "/my/log/"), it is assumed to be a
1996             directory and the final socket path is directory appended with $NAME-log.sock.
1997              
1998             Default level is the same as the global level set by B<-level>. But
1999             App::options, command line, environment, level flag file, and package variables
2000             in main are also searched first (for B<UNIXSOCK_LOG_LEVEL>, B<UNIXSOCK_TRACE>,
2001             B<UNIXSOCK_DEBUG>, B<UNIXSOCK_VERBOSE>, B<UNIXSOCK_QUIET>, and the similars).
2002              
2003             You can also specify category level from environment
2004             UNIXSOCK_LOG_CATEGORY_LEVEL.
2005              
2006             =item -array => 0 | {opts} | [{opts}, ...]
2007              
2008             Specify output to one or more Perl arrays. Logging will be done using
2009             L<Log::Dispatch::ArrayWithLimits>. Note that the syntax is:
2010              
2011             -array => {array=>$ary}
2012              
2013             and not just:
2014              
2015             -array => $ary
2016              
2017             because that will be interpreted as multiple array outputs:
2018              
2019             -array => [{output1}, ...]
2020              
2021             If the argument is a false boolean value, array logging will be turned off.
2022             Otherwise argument must be a hashref or an arrayref (to specify multiple
2023             outputs). If the argument is a hashref, then the keys of the hashref must be one
2024             of: C<level>, C<array> (defaults to new anonymous array []), C<filter_text>,
2025             C<filter_no_text>, C<filter_citext>, C<filter_no_citext>, C<filter_re>,
2026             C<filter_no_re>. If the argument is an arrayref, it is assumed to be specifying
2027             multiple sockets, with each element of the array as a hashref.
2028              
2029             How Log::Any::App determines defaults for array logging:
2030              
2031             By default array logging is off.
2032              
2033             Default level is the same as the global level set by B<-level>. But
2034             App::options, command line, environment, level flag file, and package variables
2035             in main are also searched first (for B<ARRAY_LOG_LEVEL>, B<ARRAY_TRACE>,
2036             B<ARRAY_DEBUG>, B<ARRAY_VERBOSE>, B<ARRAY_QUIET>, and the similars).
2037              
2038             You can also specify category level from environment ARRAY_LOG_CATEGORY_LEVEL.
2039              
2040             =item -dump => BOOL
2041              
2042             If set to true then Log::Any::App will dump the generated Log4perl config.
2043             Useful for debugging the logging.
2044              
2045             =back
2046              
2047             =head1 FAQ
2048              
2049             =head2 Why?
2050              
2051             I initially wrote Log::Any::App because I'm sick of having to parse command-line
2052             options to set log level like --verbose, --log-level=debug for every script.
2053             Also, before using Log::Any I previously used Log4perl directly and modules
2054             which produce logs using Log4perl cannot be directly use'd in one-liners without
2055             Log4perl complaining about uninitialized configuration or some such. Thus, I
2056             like Log::Any's default null adapter and want to settle using Log::Any for any
2057             kind of logging. Log::Any::App makes it easy to output Log::Any logs in your
2058             scripts and even one-liners.
2059              
2060             =head2 What's the benefit of using Log::Any::App?
2061              
2062             You get all the benefits of Log::Any, as what Log::Any::App does is just wrap
2063             Log::Any and Log4perl with some nice defaults. It provides you with an easy way
2064             to consume Log::Any logs and customize level/some other options via various
2065             ways.
2066              
2067             =head2 And what's the benefit of using Log::Any?
2068              
2069             This is better described in the Log::Any documentation itself, but in short:
2070             Log::Any frees your module users to use whatever logging framework they want. It
2071             increases the reusability of your modules.
2072              
2073             =head2 Do I need Log::Any::App if I am writing modules?
2074              
2075             No, if you write modules just use Log::Any.
2076              
2077             =head2 Why use Log4perl?
2078              
2079             Log::Any::App uses the Log4perl adapter to display the logs because it is
2080             mature, flexible, featureful. The other alternative adapter is Log::Dispatch,
2081             but you can use Log::Dispatch::* output modules in Log4perl and (currently) not
2082             vice versa.
2083              
2084             Other adapters might be considered in the future, for now I'm fairly satisfied
2085             with Log4perl. It does have a slightly heavy startup cost for my taste, but it
2086             is still bearable.
2087              
2088             =head2 Are you coupling adapter with Log::Any (thus defeating Log::Any's purpose)?
2089              
2090             No, producing logs are still done with Log::Any as usual and not tied to
2091             Log4perl in any way. Your modules, as explained above, only 'use Log::Any' and
2092             do not depend on Log::Any::App at all.
2093              
2094             Should portions of your application code get refactored into modules later, you
2095             don't need to change the logging part. And if your application becomes more
2096             complex and Log::Any::App doesn't suffice your custom logging needs anymore, you
2097             can just replace 'use Log::Any::App' line with something more adequate.
2098              
2099             =head2 How do I create extra logger objects?
2100              
2101             The usual way as with Log::Any:
2102              
2103             my $other_log = Log::Any->get_logger(category => $category);
2104              
2105             =head2 My needs are not met by the simple configuration system of Log::Any::App!
2106              
2107             You can use the Log4perl adapter directly and write your own Log4perl
2108             configuration (or even other adapters). Log::Any::App is meant for quick and
2109             simple logging output needs anyway (but do tell me if your logging output needs
2110             are reasonably simple and should be supported by Log::Any::App).
2111              
2112             =head2 What is array output for?
2113              
2114             Logging to a Perl array might be useful for testing/debugging, or (one use-case
2115             I can think of) for letting users of your program connect to your program
2116             directly to request viewing the logs being produced (although logging to other
2117             outputs doesn't preclude this ability). For example, here is a program that uses
2118             a separate thread to listen to Unix socket for requests to view the (last 100)
2119             logs. Requires perl built with threads enabled.
2120              
2121             use threads;
2122             use threads::shared;
2123             BEGIN { our @buf :shared }
2124             use IO::Socket::UNIX::Util qw(create_unix_stream_socket);
2125             use Log::Any::App '$log', -array => [{array => 'main::buf', max_elems=>100}];
2126              
2127             my $sock = create_unix_stream_socket('/tmp/app-logview.sock');
2128              
2129             # thread to listen to unix socket and receive log viewing instruction
2130             my $thr = threads->create(
2131             sub {
2132             local $| = 1;
2133             while (my $cli = $sock->accept) {
2134             while (1) {
2135             print $cli "> ";
2136             my $line = <$cli>;
2137             last unless $line;
2138             if ($line =~ /\Ar(ead)?\b/i) {
2139             print $cli @buf;
2140             } else {
2141             print $cli "Unknown command\n";
2142             }
2143             }
2144             }
2145             });
2146              
2147             # main thread, application which produces logs
2148             $|++;
2149             while (1) {
2150             $log->warnf("Log (%d) ...", ++$i);
2151             sleep 1;
2152             }
2153              
2154             After you run this program, you can connect to it, e.g. from another terminal:
2155              
2156             % socat UNIX-CONNECT:/tmp/app-logview.sock -
2157             > read
2158             [2014/07/06 23:34:49] Log (1) ...
2159             [2014/07/06 23:34:50] Log (2) ...
2160             [2014/07/06 23:34:50] Log (3) ...
2161             [2014/07/06 23:34:51] Log (4) ...
2162             [2014/07/06 23:34:51] Log (5) ...
2163              
2164             =head1 ENVIRONMENT
2165              
2166             Below is summary of environment variables used.
2167              
2168             =head2 Turning on/off logging
2169              
2170             LOG (bool)
2171              
2172             =head2 Setting general level
2173              
2174             TRACE (bool) setting general level to trace
2175             DEBUG (bool) setting general level to debug
2176             VERBOSE (bool) setting general level to info
2177             QUIET (bool) setting general level to error (turn off warnings)
2178             LOG_LEVEL (str)
2179              
2180             =head2 Setting per-output level
2181              
2182             FILE_TRACE, FILE_DEBUG, FILE_VERBOSE, FILE_QUIET, FILE_LOG_LEVEL
2183             SCREEN_TRACE and so on
2184             DIR_TRACE and so on
2185             SYSLOG_TRACE and so on
2186             UNIXSOCK_TRACE and so on
2187             ARRAY_TRACE and so on
2188              
2189             =head2 Setting per-category level
2190              
2191             LOG_CATEGORY_LEVEL (hash, json)
2192             LOG_CATEGORY_ALIAS (hash, json)
2193              
2194             =head2 Setting per-output, per-category level
2195              
2196             FILE_LOG_CATEGORY_LEVEL
2197             SCREEN_LOG_CATEGORY_LEVEL
2198             and so on
2199              
2200             =head2 Controlling extra fields to log
2201              
2202             LOG_SHOW_LOCATION
2203             LOG_SHOW_CATEGORY
2204              
2205             =head2 Force-enable or disable color
2206              
2207             COLOR (bool)
2208              
2209             =head2 Turn on Log::Any::App's debugging
2210              
2211             LOGANYAPP_DEBUG (bool)
2212              
2213             =head2 Turn on showing elapsed time in screen
2214              
2215             LOG_ELAPSED_TIME_IN_SCREEN (bool)
2216              
2217             Note that elapsed time is currently produced using Log::Log4perl's %r (number of
2218             milliseconds since the program started, where program started means when
2219             Log::Log4perl starts counting time).
2220              
2221             =head2 Filtering
2222              
2223             LOG_FILTER_TEXT (str)
2224             LOG_FILTER_NO_TEXT (str)
2225             LOG_FILTER_CITEXT (str)
2226             LOG_FILTER_NO_CITEXT (str)
2227             LOG_FILTER_RE (str)
2228             LOG_FILTER_NO_RE (str)
2229              
2230             =head2 Per-output filtering
2231              
2232             {FILE,DIR,SCREEN,SYSLOG,UNIXSOCK,ARRAY}_LOG_FILTER_TEXT (str)
2233             and so on
2234              
2235             =head2 Extra things to log
2236              
2237             =over
2238              
2239             =item * LOG_ENV (bool)
2240              
2241             If set to 1, will dump environment variables at the start of program. Useful for
2242             debugging e.g. CGI or git hook scripts. You might also want to look at
2243             L<Log::Any::Adapter::Core::Patch::UseDataDump> to make the dump more readable.
2244              
2245             Logging will be done under category C<main> and at level C<trace>.
2246              
2247             =back
2248              
2249             =head1 HOMEPAGE
2250              
2251             Please visit the project's homepage at L<https://metacpan.org/release/Log-Any-App>.
2252              
2253             =head1 SOURCE
2254              
2255             Source repository is at L<https://github.com/perlancar/perl-Log-Any-App>.
2256              
2257             =head1 BUGS
2258              
2259             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Log-Any-App>
2260              
2261             When submitting a bug or request, please include a test-file or a
2262             patch to an existing test-file that illustrates the bug or desired
2263             feature.
2264              
2265             =head1 SEE ALSO
2266              
2267             L<Log::Any> and L<Log::Log4perl>
2268              
2269             Some alternative logging modules: L<Log::Dispatchouli> (based on
2270             L<Log::Dispatch>), L<Log::Fast>, L<Log::Log4perl::Tiny>. Really, there are 7,451
2271             of them (roughly one third of CPAN) at the time of this writing.
2272              
2273             =head1 AUTHOR
2274              
2275             perlancar <perlancar@cpan.org>
2276              
2277             =head1 COPYRIGHT AND LICENSE
2278              
2279             This software is copyright (c) 2019, 2015, 2014, 2013, 2012, 2011, 2010 by perlancar@cpan.org.
2280              
2281             This is free software; you can redistribute it and/or modify it under
2282             the same terms as the Perl 5 programming language system itself.
2283              
2284             =cut