File Coverage

blib/lib/Log/Agent/Channel/File.pm
Criterion Covered Total %
statement 81 98 82.6
branch 29 54 53.7
condition 6 17 35.2
subroutine 15 16 93.7
pod 3 11 27.2
total 134 196 68.3


line stmt bran cond sub pod time code
1             ###########################################################################
2             #
3             # File.pm
4             #
5             # Copyright (C) 1999 Raphael Manfredi.
6             # Copyright (C) 2002-2017 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   64 use strict;
  8         16  
  8         500  
15             require Log::Agent::Channel;
16             require Log::Agent::Prefixer;
17            
18             ########################################################################
19             package Log::Agent::Channel::File;
20            
21 8     8   47 use vars qw(@ISA);
  8         13  
  8         448  
22            
23             @ISA = qw(Log::Agent::Channel Log::Agent::Prefixer);
24            
25 8     8   4083 use Symbol;
  8         6579  
  8         620  
26 8     8   56 use Fcntl;
  8         16  
  8         1915  
27 8     8   3247 use Log::Agent::Stamping;
  8         20  
  8         10038  
28            
29             #
30             # ->make -- defined
31             #
32             # Creation routine.
33             #
34             # Attributes (and switches that set them):
35             #
36             # prefix the application name
37             # stampfmt stamping format ("syslog", "date", "own", "none") or closure
38             # showpid whether to show pid after prefix in []
39             # filename file name to open (magical open needs -magic_open)
40             # fileperm permissions to open file with
41             # magic_open flag to tell whether ">>file" or "|proc" are allowed filenames
42             # rotate rotating policy for this file
43             # share true implies that non-magic filenames share the same fd object
44             # no_ucfirst don't capitalize first letter of message when no prefix
45             # no_prefixing don't prefix logs
46             # no_newline never append any newline character at the end of messages
47             #
48             # Other attributes:
49             #
50             # fd records Log::Agent::File::* objects
51             # crlf the new-line marker for this OS ("\n" on UNIX)
52             # warned records calls made to hardwired warn() to only do them once
53             #
54             sub make {
55 33     33 1 82 my $self = bless {}, shift;
56 33         174 my (%args) = @_;
57            
58             my %set = (
59             -prefix => \$self->{'prefix'},
60             -stampfmt => \$self->{'stampfmt'},
61             -showpid => \$self->{'showpid'},
62             -magic_open => \$self->{'magic_open'},
63             -filename => \$self->{'filename'},
64             -fileperm => \$self->{'fileperm'},
65             -rotate => \$self->{'rotate'},
66             -no_ucfirst => \$self->{'no_ucfirst'},
67             -no_prefixing => \$self->{'no_prefixing'},
68             -no_newline => \$self->{'no_newline'},
69 33         354 -share => \$self->{'share'},
70             );
71            
72 33         134 while (my ($arg, $val) = each %args) {
73 210         358 my $vset = $set{lc($arg)};
74 210 50       370 unless (ref $vset) {
75 0         0 require Carp;
76 0         0 Carp::croak("Unknown switch $arg");
77             }
78 210         607 $$vset = $val;
79             }
80            
81             #
82             # Initialize proper time-stamping routine.
83             #
84            
85 33 100       139 $self->{'stampfmt'} = stamping_fn($self->stampfmt)
86             unless ref $self->stampfmt eq 'CODE';
87            
88 33         67 $self->{'fd'} = undef;
89 33 50       200 $self->{'crlf'} = $^O =~ /^dos|win/i ? "\r\n" : "\n";
90 33         70 $self->{'warned'} = {};
91            
92 33 50       77 if ($self->rotate) {
93 0         0 eval {
94 0         0 require Log::Agent::File::Rotate;
95             };
96 0 0       0 if ($@) {
97 0         0 warn $@;
98 0         0 require Carp;
99 0         0 Carp::croak("Must install Log::Agent::Rotate to use rotation");
100             }
101             }
102            
103 33         175 return $self;
104             }
105            
106             #
107             # Attribute access
108             #
109            
110 2     2 0 7 sub magic_open { $_[0]->{'magic_open'} }
111 64     64 0 197 sub rotate { $_[0]->{'rotate'} }
112 52     52 0 111 sub filename { $_[0]->{'filename'} }
113 44     44 0 2361 sub fileperm { $_[0]->{'fileperm'} }
114 19     19 0 35 sub fd { $_[0]->{'fd'} }
115 81     81 0 306 sub share { $_[0]->{'share'} }
116 0     0 0 0 sub warned { $_[0]->{'warned'} }
117            
118             #
119             # ->write -- defined
120             #
121             # Write logstring to the file.
122             # Priority is ignored by this channel.
123             #
124             sub write {
125 69     69 1 103 my $self = shift;
126 69         131 my ($priority, $logstring) = @_;
127            
128             #
129             # This routine is called often...
130             # Bypass the attribute access routines.
131             #
132            
133 69         114 my $fd = $self->{fd};
134 69 100       168 $fd = $self->open unless $fd;
135 69 50       162 return unless ref $fd;
136            
137 69         94 my $prefix = '';
138             $prefix = $self->prefixing_string(\$logstring)
139 69 50       288 unless $self->{no_prefixing};
140            
141 69         127 my $crlf = '';
142 69 50       253 $crlf = $self->{crlf} unless $self->{no_newline};
143            
144             #
145             # The innocent-looking ->print statement below is NOT a polymorphic call.
146             #
147             # It can be targetted on various Log::Agent::File::* objects, which
148             # all happen to provide a print() feature with the same signature.
149             # However, those clases have no inheritance relationship because Perl
150             # is not typed, and the ancestor would be a deferred class anyway.
151             #
152            
153 69         213 $fd->print($prefix, $logstring, $crlf);
154 69         350 return;
155             }
156            
157             #
158             # ->open
159             #
160             # Open channel, and return the opened file descriptor.
161             # Also record opened file within $self->fd.
162             #
163             sub open {
164 33     33 0 100 my $self = shift;
165 33         68 my $filename = $self->filename;
166            
167 33         3351 require Log::Agent::File::Native;
168            
169 33         61 my $fobj;
170             my $note;
171            
172             #
173             # They may use ">file" or "|proc" as channel files if -magic_open
174             #
175            
176 33 100 66     165 if ($filename =~ /^\s*[>|]/ && $self->magic_open) {
177            
178             # restrict the permissions
179 2         21 my $mask = umask;
180 2 50       9 umask($mask | 0666 ^ $self->fileperm) if defined $self->fileperm;
181            
182             # open the file
183 2         8 my $h = gensym;
184 2 50       144 $fobj = Log::Agent::File::Native->make($h) if open($h, $filename);
185            
186             # restore the permissions
187 2         14 umask $mask;
188            
189             } else {
190             #
191             # If the file is already opened, and the current channel can be
192             # shared, do not re-open it: share the same Log::Agent::File::* object,
193             # along with its rotation policy.
194             #
195            
196 31         75 my $rotate = $self->rotate; # A Log::Agent::Rotate object
197 31         58 my $pool;
198            
199 31 50       62 if ($self->share) {
200 31         3702 require Log::Agent::File_Pool;
201 31         90 $pool = Log::Agent::File_Pool::file_pool();
202 31         84 my ($eobj, $erot) = $pool->get($filename);
203            
204 31 100       79 if (defined $eobj) {
205 1         2 $fobj = $eobj; # Reuse same object
206 1 0 33     7 $note = "rotation for '$filename' may be wrong" .
      33        
207             " (shared with distinct policies)" if
208             defined $erot && defined $rotate &&
209             !$erot->is_same($rotate);
210             }
211             }
212            
213 31 100       73 unless (defined $fobj) {
214 30 50       60 if (defined $rotate) {
215 0         0 $fobj = Log::Agent::File::Rotate->make($filename, $rotate);
216             } else {
217 30         76 my $h = gensym;
218 30 100       431 $fobj = Log::Agent::File::Native->make($h)
    50          
219             if sysopen($h, $filename, O_CREAT|O_APPEND|O_WRONLY,
220             defined $self->fileperm ? $self->fileperm : 0666);
221             }
222             }
223            
224             #
225             # Record object in pool if shared, even if already present.
226             # We maintain a refcount of all the shared items.
227             #
228            
229 31 50 33     141 $pool->put($filename, $fobj, $rotate)
230             if defined $fobj && $self->share;
231             }
232            
233             #
234             # If an error occurred, we have no choice but to emit a warning via warn().
235             # Otherwise, the error would disappear, and we know they don't want to
236             # silence us, or they would not try to open a logfile.
237             #
238             # Warn only once per filename though.
239             #
240            
241 33 50       127 unless (defined $fobj) {
242 0   0     0 my $prefix = $self->prefixing_string() || "$0: ";
243             warn "${prefix}can't open logfile \"$filename\": $!\n"
244 0 0       0 unless $self->warned->{$filename}++;
245 0         0 return undef;
246             }
247            
248 33   50     124 $self->{fd} = $fobj || 1; # Avoid recursion in open if not defined
249            
250             #
251             # Print the note, using ->write() now that $self->fd is recorded.
252             #
253            
254 33 50       74 if (defined $note) {
255 0 0       0 $note .= $self->crlf if $self->no_newline;
256 0         0 $self->write(undef, $note);
257             }
258            
259 33         59 return $fobj;
260             }
261            
262             #
263             # ->close -- defined
264             #
265             sub close {
266 19     19 1 29 my $self = shift;
267 19         41 my $fd = $self->fd;
268 19 50       44 return unless ref $fd;
269            
270 19         35 $self->{fd} = 1; # Prevents further opening from ->write
271 19 50       32 unless ($self->share) {
272 0         0 $fd->close;
273 0         0 return;
274             }
275            
276             #
277             # A shared file is physically closed only when the last reference
278             # to it is removed.
279             #
280            
281 19         45 my $pool = Log::Agent::File_Pool::file_pool();
282 19 50       41 $fd->close if $pool->remove($self->filename);
283 19         101 return;
284             }
285            
286             1; # for require
287             __END__