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-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   38 use strict;
  8         14  
  8         401  
15             require Log::Agent::Channel;
16             require Log::Agent::Prefixer;
17              
18             ########################################################################
19             package Log::Agent::Channel::File;
20              
21 8     8   38 use vars qw(@ISA);
  8         15  
  8         384  
22              
23             @ISA = qw(Log::Agent::Channel Log::Agent::Prefixer);
24              
25 8     8   5927 use Symbol;
  8         7110  
  8         576  
26 8     8   41 use Fcntl;
  8         15  
  8         2759  
27 8     8   3940 use Log::Agent::Stamping;
  8         21  
  8         9982  
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 75 my $self = bless {}, shift;
56 33         170 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         436 -share => \$self->{'share'},
70             );
71              
72 33         140 while (my ($arg, $val) = each %args) {
73 210         345 my $vset = $set{lc($arg)};
74 210 50       432 unless (ref $vset) {
75 0         0 require Carp;
76 0         0 Carp::croak("Unknown switch $arg");
77             }
78 210         679 $$vset = $val;
79             }
80              
81             #
82             # Initialize proper time-stamping routine.
83             #
84              
85 33 100       131 $self->{'stampfmt'} = stamping_fn($self->stampfmt)
86             unless ref $self->stampfmt eq 'CODE';
87              
88 33         73 $self->{'fd'} = undef;
89 33 50       225 $self->{'crlf'} = $^O =~ /^dos|win/i ? "\r\n" : "\n";
90 33         64 $self->{'warned'} = {};
91              
92 33 50       87 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         213 return $self;
104             }
105              
106             #
107             # Attribute access
108             #
109              
110 2     2 0 9 sub magic_open { $_[0]->{'magic_open'} }
111 64     64 0 201 sub rotate { $_[0]->{'rotate'} }
112 52     52 0 122 sub filename { $_[0]->{'filename'} }
113 44     44 0 2394 sub fileperm { $_[0]->{'fileperm'} }
114 19     19 0 63 sub fd { $_[0]->{'fd'} }
115 81     81 0 327 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 90 my $self = shift;
126 69         101 my ($priority, $logstring) = @_;
127              
128             #
129             # This routine is called often...
130             # Bypass the attribute access routines.
131             #
132              
133 69         103 my $fd = $self->{fd};
134 69 100       176 $fd = $self->open unless $fd;
135 69 50       157 return unless ref $fd;
136              
137 69         90 my $prefix = '';
138             $prefix = $self->prefixing_string(\$logstring)
139 69 50       430 unless $self->{no_prefixing};
140              
141 69         110 my $crlf = '';
142 69 50       225 $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         207 $fd->print($prefix, $logstring, $crlf);
154 69         268 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 44 my $self = shift;
165 33         70 my $filename = $self->filename;
166              
167 33         4171 require Log::Agent::File::Native;
168              
169 33         54 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     175 if ($filename =~ /^\s*[>|]/ && $self->magic_open) {
177              
178             # restrict the permissions
179 2         7 my $mask = umask;
180 2 50       5 umask($mask | 0666 ^ $self->fileperm) if defined $self->fileperm;
181              
182             # open the file
183 2         6 my $h = gensym;
184 2 50       115 $fobj = Log::Agent::File::Native->make($h) if open($h, $filename);
185              
186             # restore the permissions
187 2         6 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         69 my $rotate = $self->rotate; # A Log::Agent::Rotate object
197 31         43 my $pool;
198              
199 31 50       69 if ($self->share) {
200 31         4549 require Log::Agent::File_Pool;
201 31         91 $pool = Log::Agent::File_Pool::file_pool();
202 31         93 my ($eobj, $erot) = $pool->get($filename);
203              
204 31 100       93 if (defined $eobj) {
205 1         2 $fobj = $eobj; # Reuse same object
206 1 0 33     5 $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       85 unless (defined $fobj) {
214 30 50       56 if (defined $rotate) {
215 0         0 $fobj = Log::Agent::File::Rotate->make($filename, $rotate);
216             } else {
217 30         81 my $h = gensym;
218 30 100       366 $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     135 $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       87 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     145 $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       75 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         66 return $fobj;
260             }
261              
262             #
263             # ->close -- defined
264             #
265             sub close {
266 19     19 1 26 my $self = shift;
267 19         42 my $fd = $self->fd;
268 19 50       46 return unless ref $fd;
269              
270 19         29 $self->{fd} = 1; # Prevents further opening from ->write
271 19 50       36 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         49 my $pool = Log::Agent::File_Pool::file_pool();
282 19 50       43 $fd->close if $pool->remove($self->filename);
283 19         64 return;
284             }
285              
286             1; # for require
287             __END__