File Coverage

blib/lib/Log/Dispatch/File.pm
Criterion Covered Total %
statement 65 66 98.4
branch 30 38 78.9
condition 8 12 66.6
subroutine 12 13 92.3
pod 0 3 0.0
total 115 132 87.1


line stmt bran cond sub pod time code
1             package Log::Dispatch::File;
2              
3 27     27   7963 use strict;
  27         56  
  27         793  
4 27     27   180 use warnings;
  27         65  
  27         1050  
5              
6             our $VERSION = '2.71';
7              
8 27     27   684 use IO::Handle;
  27         6671  
  27         1115  
9 27     27   394 use Log::Dispatch::Types;
  27         54  
  27         231  
10 27     27   768171 use Params::ValidationCompiler qw( validation_for );
  27         77  
  27         1351  
11 27     27   166 use Scalar::Util qw( openhandle );
  27         52  
  27         1124  
12              
13 27     27   146 use base qw( Log::Dispatch::Output );
  27         42  
  27         14124  
14              
15             # Prevents death later on if IO::File can't export this constant.
16             *O_APPEND = \&APPEND unless defined &O_APPEND;
17              
18 0     0 0 0 sub APPEND {0}
19              
20             {
21             my $validator = validation_for(
22             params => {
23             filename => { type => t('NonEmptyStr') },
24             mode => {
25             type => t('Value'),
26             default => '>',
27             },
28             binmode => {
29             type => t('Str'),
30             optional => 1,
31             },
32             autoflush => {
33             type => t('Bool'),
34             default => 1,
35             },
36             close_after_write => {
37             type => t('Bool'),
38             default => 0,
39             },
40             lazy_open => {
41             type => t('Bool'),
42             default => 0,
43             },
44             permissions => {
45             type => t('PositiveOrZeroInt'),
46             optional => 1,
47             },
48             syswrite => {
49             type => t('Bool'),
50             default => 0,
51             },
52             },
53             slurpy => 1,
54             );
55              
56             # We stick these in $self as-is without looking at them in new().
57             my @self_p = qw(
58             autoflush
59             binmode
60             close_after_write
61             filename
62             lazy_open
63             permissions
64             syswrite
65             );
66              
67             sub new {
68 68     68 0 5036 my $class = shift;
69 68         2263 my %p = $validator->(@_);
70              
71 68         8189 my $self = bless { map { $_ => delete $p{$_} } @self_p }, $class;
  476         2879  
72              
73 68 100 33     1143 if ( $self->{close_after_write} ) {
    100 66        
74 23         266 $self->{mode} = '>>';
75             }
76             elsif (
77             $p{mode} =~ /^(?:>>|append)$/
78             || ( $p{mode} =~ /^\d+$/
79             && $p{mode} == O_APPEND() )
80             ) {
81 31         248 $self->{mode} = '>>';
82             }
83             else {
84 14         52 $self->{mode} = '>';
85             }
86 68         309 delete $p{mode};
87              
88 68         726 $self->_basic_init(%p);
89             $self->_open_file()
90 68 100 100     844 unless $self->{close_after_write} || $self->{lazy_open};
91              
92 68         678 return $self;
93             }
94             }
95              
96             sub _open_file {
97 78     78   198 my $self = shift;
98              
99             ## no critic (InputOutput::RequireBriefOpen)
100             open my $fh, $self->{mode}, $self->{filename}
101 78 50       9426 or die "Cannot write to '$self->{filename}': $!";
102              
103 78 50       738 if ( $self->{autoflush} ) {
104 78         1375 $fh->autoflush(1);
105             }
106              
107 78 100 66     7773 if ( $self->{permissions}
108             && !$self->{chmodded} ) {
109             ## no critic (ValuesAndExpressions::ProhibitLeadingZeros)
110 2         40 my $current_mode = ( stat $self->{filename} )[2] & 07777;
111 2 100       13 if ( $current_mode ne $self->{permissions} ) {
112             chmod $self->{permissions}, $self->{filename}
113             or die sprintf(
114             'Cannot chmod %s to %04o: %s',
115 1 50       27 $self->{filename}, $self->{permissions} & 07777, $!
116             );
117             }
118              
119 2         7 $self->{chmodded} = 1;
120             }
121              
122 78 100       337 if ( $self->{binmode} ) {
123             binmode $fh, $self->{binmode}
124 2 50   1   83 or die "Cannot set binmode on filehandle: $!";
  1         15  
  1         2  
  1         11  
125             }
126              
127 78         2305 $self->{fh} = $fh;
128             }
129              
130             sub log_message {
131 28     28 0 47 my $self = shift;
132 28         70 my %p = @_;
133              
134 28 100       113 if ( $self->{close_after_write} ) {
    100          
135 3         26 $self->_open_file;
136             }
137             elsif ( $self->{lazy_open} ) {
138 1         2 $self->_open_file;
139 1         2 $self->{lazy_open} = 0;
140             }
141              
142 28         58 my $fh = $self->{fh};
143              
144 28 100       61 if ( $self->{syswrite} ) {
145             defined syswrite( $fh, $p{message} )
146 2 50       13 or die "Cannot write to '$self->{filename}': $!";
147             }
148             else {
149             print $fh $p{message}
150 26 50       958 or die "Cannot write to '$self->{filename}': $!";
151             }
152              
153 28 100       325 if ( $self->{close_after_write} ) {
154 3 50       39 close $fh
155             or die "Cannot close '$self->{filename}': $!";
156 3         40 delete $self->{fh};
157             }
158             }
159              
160             sub DESTROY {
161 68     68   18557967 my $self = shift;
162              
163 68 100       3899 if ( $self->{fh} ) {
164 45         233 my $fh = $self->{fh};
165             ## no critic (InputOutput::RequireCheckedSyscalls)
166 45 50       5398 close $fh if openhandle($fh);
167             }
168             }
169              
170             1;
171              
172             # ABSTRACT: Object for logging to files
173              
174             __END__
175              
176             =pod
177              
178             =encoding UTF-8
179              
180             =head1 NAME
181              
182             Log::Dispatch::File - Object for logging to files
183              
184             =head1 VERSION
185              
186             version 2.71
187              
188             =head1 SYNOPSIS
189              
190             use Log::Dispatch;
191              
192             my $log = Log::Dispatch->new(
193             outputs => [
194             [
195             'File',
196             min_level => 'info',
197             filename => 'Somefile.log',
198             mode => '>>',
199             newline => 1
200             ]
201             ],
202             );
203              
204             $log->emerg("I've fallen and I can't get up");
205              
206             =head1 DESCRIPTION
207              
208             This module provides a simple object for logging to files under the
209             Log::Dispatch::* system.
210              
211             Note that a newline will I<not> be added automatically at the end of a message
212             by default. To do that, pass C<< newline => 1 >>.
213              
214             B<NOTE:> If you are writing to a single log file from multiple processes, the
215             log output may become interleaved and garbled. Use the
216             L<Log::Dispatch::File::Locked> output instead, which allows multiple processes
217             to safely share a single file.
218              
219             =for Pod::Coverage new log_message
220              
221             =head1 CONSTRUCTOR
222              
223             The constructor takes the following parameters in addition to the standard
224             parameters documented in L<Log::Dispatch::Output>:
225              
226             =over 4
227              
228             =item * filename ($)
229              
230             The filename to be opened for writing.
231              
232             =item * mode ($)
233              
234             The mode the file should be opened with. Valid options are 'write', '>',
235             'append', '>>', or the relevant constants from Fcntl. The default is 'write'.
236              
237             =item * binmode ($)
238              
239             A layer name to be passed to binmode, like ":encoding(UTF-8)" or ":raw".
240              
241             =item * close_after_write ($)
242              
243             Whether or not the file should be closed after each write. This defaults to
244             false.
245              
246             If this is true, then the mode will always be append, so that the file is not
247             re-written for each new message.
248              
249             =item * lazy_open ($)
250              
251             Whether or not the file should be opened only on first write. This defaults to
252             false.
253              
254             =item * autoflush ($)
255              
256             Whether or not the file should be autoflushed. This defaults to true.
257              
258             =item * syswrite ($)
259              
260             Whether or not to perform the write using L<perlfunc/syswrite>(), as opposed to
261             L<perlfunc/print>(). This defaults to false. The usual caveats and warnings as
262             documented in L<perlfunc/syswrite> apply.
263              
264             =item * permissions ($)
265              
266             If the file does not already exist, the permissions that it should be created
267             with. Optional. The argument passed must be a valid octal value, such as 0600
268             or the constants available from Fcntl, like S_IRUSR|S_IWUSR.
269              
270             See L<perlfunc/chmod> for more on potential traps when passing octal values
271             around. Most importantly, remember that if you pass a string that looks like an
272             octal value, like this:
273              
274             my $mode = '0644';
275              
276             Then the resulting file will end up with permissions like this:
277              
278             --w----r-T
279              
280             which is probably not what you want.
281              
282             =back
283              
284             =head1 SUPPORT
285              
286             Bugs may be submitted at L<https://github.com/houseabsolute/Log-Dispatch/issues>.
287              
288             =head1 SOURCE
289              
290             The source code repository for Log-Dispatch can be found at L<https://github.com/houseabsolute/Log-Dispatch>.
291              
292             =head1 AUTHOR
293              
294             Dave Rolsky <autarch@urth.org>
295              
296             =head1 COPYRIGHT AND LICENSE
297              
298             This software is Copyright (c) 2023 by Dave Rolsky.
299              
300             This is free software, licensed under:
301              
302             The Artistic License 2.0 (GPL Compatible)
303              
304             The full text of the license can be found in the
305             F<LICENSE> file included with this distribution.
306              
307             =cut