File Coverage

blib/lib/Log/Agent/Driver/File.pm
Criterion Covered Total %
statement 98 130 75.3
branch 34 58 58.6
condition 8 15 53.3
subroutine 21 25 84.0
pod 10 22 45.4
total 171 250 68.4


line stmt bran cond sub pod time code
1             ###########################################################################
2             #
3             # File.pm
4             #
5             # Copyright (C) 1999 Raphael Manfredi.
6             # Copyright (C) 2002-2015 Mark Rogaski, mrogaski@cpan.org;
7             # all rights reserved.
8             #
9             # See the README file included with the
10             # distribution for license information.
11             #
12             ##########################################################################
13              
14 8     8   10505 use strict;
  8         17  
  8         357  
15             require Log::Agent::Driver;
16              
17             ########################################################################
18             package Log::Agent::Driver::File;
19              
20 8     8   43 use vars qw(@ISA);
  8         15  
  8         13786  
21              
22             @ISA = qw(Log::Agent::Driver);
23              
24             #
25             # ->make -- defined
26             #
27             # Creation routine.
28             #
29             # Attributes (and switches that set them):
30             #
31             # prefix the application name
32             # duperr whether to duplicate "error" channels to "output"
33             # stampfmt stamping format ("syslog", "date", "own", "none") or closure
34             # showpid whether to show pid after prefix in []
35             # channels where each channel ("error", "output", "debug") goes
36             # chanperm what permissions each channel ("error", "output", "debug") has
37             # magic_open flag to tell whether ">>file" or "|proc" are allowed filenames
38             # rotate default rotating policy for logfiles
39             #
40             # Additional switches:
41             #
42             # file sole channel, implies -duperr = 0 and supersedes -channels
43             # perm file permissions that supersedes all channel permissions
44             #
45             # Other attributes:
46             #
47             # channel_obj opened channel objects
48             #
49             sub make {
50 21     21 1 18030 my $self = bless {}, shift;
51 21         100 my (%args) = @_;
52 21         34 my $prefix;
53             my $file;
54 0         0 my $perm;
55              
56             my %set = (
57             -prefix => \$prefix, # Handled by parent via _init
58             -duperr => \$self->{'duperr'},
59             -channels => \$self->{'channels'},
60             -chanperm => \$self->{'chanperm'},
61             -stampfmt => \$self->{'stampfmt'},
62             -showpid => \$self->{'showpid'},
63             -magic_open => \$self->{'magic_open'},
64             -file => \$file,
65             -perm => \$perm,
66 21         297 -rotate => \$self->{'rotate'},
67             );
68              
69 21         109 while (my ($arg, $val) = each %args) {
70 52         97 my $vset = $set{lc($arg)};
71 52 50       124 unless (ref $vset) {
72 0         0 require Carp;
73 0         0 Carp::croak("Unknown switch $arg");
74             }
75 52         192 $$vset = $val;
76             }
77              
78             #
79             # If -file was used, it supersedes -duperr and -channels
80             #
81              
82 21 100 66     128 if (defined $file && length $file) {
83 6         25 $self->{'channels'} = {
84             'debug' => $file,
85             'output' => $file,
86             'error' => $file,
87             };
88 6         10 $self->{'duperr'} = 0;
89             }
90              
91             #
92             # and we do something similar for file permissions
93             #
94              
95 21 100 66     91 if (defined $perm && length $perm) {
96             $self->{chanperm} = {
97 6         22 debug => $perm,
98             output => $perm,
99             error => $perm
100             };
101             }
102              
103 21         108 $self->_init($prefix, 0); # 1 is the skip Carp penalty for confess
104              
105 21 100       50 $self->{channels} = {} unless $self->channels; # No defined channels
106 21 100       59 $self->{chanperm} = {} unless $self->chanperm; # No defined perms
107 21         40 $self->{channel_obj} = {}; # No opened files
108              
109             #
110             # Check for logfile rotation, which can be specified on a global or
111             # file by file basis. Since Log::Agent::Rotate is a separate extension,
112             # it may not be installed.
113             #
114              
115 21 50       50 my $use_rotate = defined($self->rotate) ? 1 : 0;
116 21 50       56 unless ($use_rotate) {
117 21         29 foreach my $chan (keys %{$self->channels}) {
  21         46  
118 47 50       95 $use_rotate = 1 if ref $self->channels->{$chan} eq 'ARRAY';
119 47 50       121 last if $use_rotate;
120             }
121             }
122              
123 21 50       57 if ($use_rotate) {
124 0         0 eval {
125 0         0 require Log::Agent::File::Rotate;
126             };
127 0 0       0 if ($@) {
128 0         0 warn $@;
129 0         0 require Carp;
130 0         0 Carp::croak("Must install Log::Agent::Rotate to use rotation");
131             }
132             }
133              
134 21         117 return $self;
135             }
136              
137             #
138             # Attribute access
139             #
140              
141 35     35 0 122 sub duperr { $_[0]->{duperr} }
142 128     128 0 353 sub channels { $_[0]->{channels} }
143 66     66 0 235 sub chanperm { $_[0]->{chanperm} }
144 119     119 0 326 sub channel_obj { $_[0]->{channel_obj} }
145 35     35 0 102 sub stampfmt { $_[0]->{stampfmt} }
146 35     35 0 105 sub showpid { $_[0]->{showpid} }
147 33     33 0 178 sub magic_open { $_[0]->{magic_open} }
148 56     56 0 131 sub rotate { $_[0]->{rotate} }
149              
150             #
151             # ->prefix_msg -- defined
152             #
153             # NOP: channel handles prefixing for us.
154             #
155             sub prefix_msg {
156 65     65 1 93 my $self = shift;
157 65         175 return $_[0];
158             }
159              
160             #
161             # ->chanfn
162             #
163             # Return channel file name.
164             #
165             sub chanfn {
166 0     0 0 0 my $self = shift;
167 0         0 my ($channel) = @_;
168 0         0 my $filename = $self->channels->{$channel};
169 0 0       0 if (ref $filename eq 'ARRAY') {
170 0         0 $filename = $filename->[0];
171             }
172             # No channel defined, use 'error'
173 0 0 0     0 $filename = $self->channels->{'error'} unless
174             defined $filename && length $filename;
175 0 0       0 $filename = '' unless defined $filename;
176              
177 0         0 return $filename;
178             }
179              
180             #
181             # ->channel_eq -- defined
182             #
183             # Compare two channels.
184             #
185             # It's hard to know for certain that two channels are equivalent, so we
186             # compare filenames. This is not correct, of course, but it will do for
187             # what we're trying to achieve here, namely avoid duplicates if possible
188             # when traces are remapped to Carp::Datum.
189             #
190             sub channel_eq {
191 0     0 1 0 my $self = shift;
192 0         0 my ($chan1, $chan2) = @_;
193 0         0 my $fn1 = $self->chanfn($chan1);
194 0         0 my $fn2 = $self->chanfn($chan2);
195 0         0 return $fn1 eq $fn2;
196             }
197              
198             #
199             # ->write -- defined
200             #
201             sub write {
202 71     71 1 84 my $self = shift;
203 71         117 my ($channel, $priority, $logstring) = @_;
204 71         141 my $chan = $self->channel($channel);
205 71 50       152 return unless $chan;
206              
207 71         205 $chan->write($priority, $logstring);
208             }
209              
210             #
211             # ->channel
212             #
213             # Return channel object (one of the Log::Agent::Channel::* objects)
214             #
215             sub channel {
216 71     71 0 86 my $self = shift;
217 71         116 my ($name) = @_;
218 71         140 my $obj = $self->channel_obj->{$name};
219 71 100       209 $obj = $self->open_channel($name) unless $obj;
220 71         135 return $obj;
221             }
222              
223              
224             #
225             # ->open_channel
226             #
227             # Open given channel according to the configured channel description and
228             # return the object file descriptor.
229             #
230             # If no channel of that name was defined, use 'error' or STDERR.
231             #
232             sub open_channel {
233 35     35 0 50 my $self = shift;
234 35         46 my ($name) = @_;
235 35         79 my $filename = $self->channels->{$name};
236              
237             #
238             # Handle possible logfile rotation, which may be defined globally
239             # or on a file by file basis.
240             #
241              
242 35         54 my $rotate; # A Log::Agent::Rotate object
243 35 50       114 if (ref $filename eq 'ARRAY') {
244 0         0 ($filename, $rotate) = @$filename;
245             } else {
246 35         80 $rotate = $self->rotate;
247             }
248              
249 35         135 my @common_args = (
250             -prefix => $self->prefix,
251             -stampfmt => $self->stampfmt,
252             -showpid => $self->showpid,
253             );
254 35         58 my @other_args;
255             my $type;
256              
257             #
258             # No channel defined, use 'error', or revert to STDERR
259             #
260              
261 35 100 66     175 $filename = $self->channels->{'error'} unless
262             defined $filename && length $filename;
263              
264 35 100 66     170 unless (defined $filename && length $filename) {
265 2         781 require Log::Agent::Channel::Handle;
266 2         11 select((select(main::STDERR), $| = 1)[0]);
267 2         5 $type = "Log::Agent::Channel::Handle";
268 2         5 @other_args = (-handle => \*main::STDERR);
269             } else {
270 33         5396 require Log::Agent::Channel::File;
271 33         64 $type = "Log::Agent::Channel::File";
272 33         108 @other_args = (
273             -filename => $filename,
274             -magic_open => $self->magic_open,
275             -share => 1,
276             );
277             push(@other_args, -fileperm => $self->chanperm->{$name})
278 33 100       84 if $self->chanperm->{$name};
279 33 50       91 push(@other_args, -rotate => $rotate) if ref $rotate;
280             }
281              
282 35         217 return $self->channel_obj->{$name} =
283             $type->make(@common_args, @other_args);
284             }
285              
286             #
287             # ->emit_output
288             #
289             # Force error message to the regular 'output' channel with a specified tag.
290             #
291             sub emit_output {
292 6     6 0 8 my $self = shift;
293 6         12 my ($prio, $tag, $str) = @_;
294 6         21 my $cstr = $str->clone; # We're prepending tag on a copy
295 6         26 $cstr->prepend("$tag: ");
296 6         17 $self->write('output', $prio, $cstr);
297             }
298              
299             ###
300             ### Redefined routines to handle duperr
301             ###
302              
303             #
304             # ->logconfess
305             #
306             # When `duperr' is true, emit message on the 'output' channel prefixed
307             # with FATAL.
308             #
309             sub logconfess {
310 0     0 1 0 my $self = shift;
311 0         0 my ($str) = @_;
312 0 0       0 $self->emit_output('critical', "FATAL", $str) if $self->duperr;
313 0         0 $self->SUPER::logconfess($str); # Carp strips calls within hierarchy
314             }
315              
316             #
317             # ->logxcroak
318             #
319             # When `duperr' is true, emit message on the 'output' channel prefixed
320             # with FATAL.
321             #
322             sub logxcroak {
323 0     0 1 0 my $self = shift;
324 0         0 my ($offset, $str) = @_;
325 0         0 my $msg = Log::Agent::Message->make(
326             $self->carpmess($offset, $str, \&Carp::shortmess)
327             );
328 0 0       0 $self->emit_output('critical', "FATAL", $msg) if $self->duperr;
329              
330             #
331             # Carp strips calls within hierarchy, so that new call should not show,
332             # there's no need to adjust the frame offset.
333             #
334 0         0 $self->SUPER::logdie($msg);
335             }
336              
337             #
338             # ->logdie
339             #
340             # When `duperr' is true, emit message on the 'output' channel prefixed
341             # with FATAL.
342             #
343             sub logdie {
344 2     2 1 3 my $self = shift;
345 2         4 my ($str) = @_;
346 2 100       13 $self->emit_output('critical', "FATAL", $str) if $self->duperr;
347 2         12 $self->SUPER::logdie($str);
348             }
349              
350             #
351             # ->logerr
352             #
353             # When `duperr' is true, emit message on the 'output' channel prefixed
354             # with ERROR.
355             #
356             sub logerr {
357 12     12 1 19 my $self = shift;
358 12         19 my ($str) = @_;
359 12 100       34 $self->emit_output('error', "ERROR", $str) if $self->duperr;
360 12         59 $self->SUPER::logerr($str);
361             }
362              
363             #
364             # ->logwarn
365             #
366             # When `duperr' is true, emit message on the 'output' channel prefixed
367             # with WARNING.
368             #
369             sub logwarn {
370 4     4 1 7 my $self = shift;
371 4         8 my ($str) = @_;
372 4 100       14 $self->emit_output('warning', "WARNING", $str) if $self->duperr;
373 4         35 $self->SUPER::logwarn($str);
374             }
375              
376             #
377             # ->logxcarp
378             #
379             # When `duperr' is true, emit message on the 'output' channel prefixed
380             # with WARNING.
381             #
382             sub logxcarp {
383 13     13 1 18 my $self = shift;
384 13         25 my ($offset, $str) = @_;
385 13         69 my $msg = Log::Agent::Message->make(
386             $self->carpmess($offset, $str, \&Carp::shortmess)
387             );
388 13 50       41 $self->emit_output('warning', "WARNING", $msg) if $self->duperr;
389 13         46 $self->SUPER::logwarn($msg);
390             }
391              
392             #
393             # ->DESTROY
394             #
395             # Close all opened channels, so they may be removed from the common pool.
396             #
397             sub DESTROY {
398 13     13   35 my $self = shift;
399 13         30 my $channel_obj = $self->channel_obj;
400 13 50       32 return unless defined $channel_obj;
401 13         36 foreach my $chan (values %$channel_obj) {
402 21 50       82 $chan->close if defined $chan;
403             }
404             }
405              
406             1; # for require
407             __END__