File Coverage

blib/lib/Log/Rolling.pm
Criterion Covered Total %
statement 88 124 70.9
branch 28 76 36.8
condition 4 36 11.1
subroutine 12 17 70.5
pod 11 11 100.0
total 143 264 54.1


line stmt bran cond sub pod time code
1             # Log::Rolling
2             # Written by Fairlight
3             # Copyright 2008-2009, Fairlight Consulting
4             # Last Modified: 07/27/09
5             ##############################################################################
6              
7             ########## Initialise.
8             ##### Package name.
9             package Log::Rolling;
10              
11             ##### Pragmas.
12 2     2   51094 use 5.006;
  2         8  
  2         362  
13 2     2   15 use warnings;
  2         5  
  2         68  
14 2     2   13 use strict;
  2         18  
  2         92  
15              
16             ##### Need a few modules.
17 2     2   12 use Fcntl;
  2         4  
  2         896  
18 2     2   14 use Fcntl qw(:seek :flock);
  2         3  
  2         323  
19 2     2   110 use Carp;
  2         5  
  2         4287  
20              
21             =head1 NAME
22              
23             Log::Rolling - Log to simple and self-limiting logfiles.
24              
25             =head1 VERSION
26              
27             Version 1.02
28              
29             =cut
30              
31             ##### Set version.
32             our $VERSION = '1.02';
33              
34              
35             ########## Begin meat of module.
36              
37             =head1 SYNOPSIS
38              
39             Log::Rolling is a module designed to give programs lightweight, yet
40             powerful logging facilities. One of the primary benefits is that, while the
41             logs I be infinitely long and handled by something like C,
42             the module is capable of limiting the number of lines in the log in a fashion
43             where by the oldest lines roll off to keep the size constant at the maximum
44             allowed size, if so tuned.
45              
46             This module is particularly useful when you need to keep logs around for a
47             certain amount of available data, but do not need to incur the complexity
48             and overhead of using something as heavy as C or other methods
49             of archiving. Since the rolling is built into the logging facility, no
50             extra cron jobs or the like are necessary.
51              
52             Data is buffered throughout the run of a program with each call to C.
53             Once C is called, that buffer is written to the log file, and the
54             log buffer is cleared. The C method may be called as many times as
55             necessary; however, it is best to do so as few times as required due to the
56             overhead of file operations involved in rolling the log--hence the reason the
57             entries are stored in memory until manually committed in the first place.
58              
59             use Log::Rolling;
60             my $log = Log::Rolling->new('/path/to/logfile.txt');
61              
62             # Define the maximum log size in lines. Default is 0 (infinite).
63             $log->max_size(50000);
64              
65             # Add a log entry line.
66             $log->entry("Log information string here...");
67              
68             # Commit all log entry lines in memory to file and roll the log lines
69             # to accomodate max_size.
70             $log->commit;
71              
72             =head1 METHODS
73              
74             =head2 new()
75            
76             my $log = Log::Rolling->new('/path/to/logfile.txt');
77             my $log = Log::Rolling->new(log_file => '/path/to/file',
78             max_size => 5000,
79             wait_attempts => 30,
80             wait_interval => 1,
81             mode => 0600
82             pid => 1);
83              
84             If no logfile is given, or if the logfile is unusable, the constructor
85             returns false (0).
86              
87             =cut
88              
89             sub new {
90 2     2 1 404 my $class = shift;
91 2         3 my $self = {};
92 2         5 ${self}->{'log_file'} = undef;
93 2         6 ${self}->{'max_size'} = 0; # Unlimited by default.
94 2         4 ${self}->{'wait_attempts'} = 30;
95 2         4 ${self}->{'wait_interval'} = 1;
96 2         4 ${self}->{'mode'} = 0600;
97 2         5 ${self}->{'acc'} = '';
98 2         4 ${self}->{'pid'} = 0;
99 2         5 ${self}->{'roll_allowed'} = 0;
100 2 100       13 if (scalar(@_) % 2) {
101 1 50       9 ${self}->{'log_file'} = shift if scalar(@_) == 1;
102             } else {
103 1         3 my %attrs = @_;
104 1         6 while (my ($key, $val) = each %attrs) {
105 0 0 0     0 if (${key} eq 'log_file') {
    0 0        
    0 0        
    0 0        
    0 0        
    0          
106 0         0 ${self}->{'log_file'} = ${val};
107             } elsif (${key} eq 'max_size' and ${val} =~ /^\d+$/) {
108 0         0 ${self}->{'max_size'} = ${val};
109             } elsif (${key} eq 'wait_attempts' and ${val} =~ /^\d+$/) {
110 0         0 ${self}->{'wait_attempts'} = ${val};
111             } elsif (${key} eq 'wait_interval' and ${val} =~ /^\d+$/) {
112 0         0 ${self}->{'wait_interval'} = ${val};
113             } elsif (${key} eq 'pid' and ${val} =~ /^(?:0|1)$/) {
114 0         0 ${self}->{'pid'} = ${val};
115             } elsif (${key} eq 'mode' and ${val} =~ /^\d{4}$/) {
116 0         0 ${self}->{'mode'} = ${val};
117             }
118             }
119             }
120 2 100       10 return(0) unless defined(${self}->{'log_file'});
121 1 50       129 if (not sysopen(LOG,${self}->{'log_file'},O_CREAT|O_WRONLY,${self}->{'mode'})) {
122 0         0 return(0);
123             } else {
124 1 50       14 close(LOG) or croak('Could not close log file.');
125             }
126 1         3 bless(${self},${class});
127 1         3 return(${self});
128             }
129              
130             =head2 log_file()
131              
132             This method defines the path of the logfile. Returns the value of the
133             logfile, or false (0) if the logfile is unusable.
134              
135             =cut
136              
137             sub log_file {
138 0     0 1 0 my $self = shift;
139 0 0       0 if (@_) {
140 0         0 ${self}->{'log_file'} = shift;
141             }
142 0 0       0 if (not sysopen(LOG,${self}->{'log_file'},O_CREAT|O_WRONLY,${self}->{'mode'})) {
143 0         0 return(0);
144             } else {
145 0 0       0 close(LOG) or croak('Could not close log file.');
146             }
147 0         0 return(${self}->{'log_file'});
148             }
149              
150             =head2 max_size()
151              
152             This method sets the maximum size of the logfile in lines. The size
153             is infinite (0) unless this method is called, or unless the size was
154             defined using C. Returns the maximum size.
155              
156             =cut
157              
158             sub max_size {
159 1     1 1 323 my $self = shift;
160 1 50       4 if (@_) {
161 1         3 ${self}->{'max_size'} = shift;
162             }
163 1 50 33     12 ${self}->{'max_size'} = 0, return(0) if defined(${self}->{'max_size'}) and ${self}->{'max_size'} !~ /^\d+$/;
164 1         5 return(${self}->{'max_size'});
165             }
166              
167             =head2 wait_attempts()
168              
169             This method sets the maximum number of attempts to wait for a lock on
170             the logfile. Returns the maximum wait attempt setting.
171              
172             =cut
173              
174             sub wait_attempts {
175 0     0 1 0 my $self = shift;
176 0 0       0 if (@_) {
177 0         0 ${self}->{'wait_attempts'} = shift;
178             }
179 0 0 0     0 ${self}->{'wait_attempts'} = 30, return(0) if defined(${self}->{'wait_attempts'}) and ${self}->{'wait_attempts'} !~ /^\d+$/;
180 0         0 return(${self}->{'wait_attempts'});
181             }
182              
183             =head2 wait_interval()
184              
185             This method sets the interval in seconds between attempts to wait for
186             a lock on the logfile. Returns the wait interval setting.
187              
188             =cut
189              
190             sub wait_interval {
191 0     0 1 0 my $self = shift;
192 0 0       0 if (@_) {
193 0         0 ${self}->{'wait_interval'} = shift;
194             }
195 0 0 0     0 ${self}->{'wait_interval'} = 1, return(0) if defined(${self}->{'wait_interval'}) and ${self}->{'wait_interval'} !~ /^\d+$/;
196 0         0 return(${self}->{'wait_interval'});
197             }
198              
199             =head2 mode()
200              
201             This method sets the file mode to be used when creating the log file
202             if the file does not yet exist. The value should be an octal value
203             (e.g., 0644). Returns the file mode.
204              
205             =cut
206              
207             sub mode {
208 0     0 1 0 my $self = shift;
209 0 0       0 if (@_) {
210 0         0 ${self}->{'mode'} = shift;
211             }
212 0 0 0     0 ${self}->{'mode'} = 0600, return(0) if defined(${self}->{'mode'}) and ${self}->{'mode'} !~ /^\d{4}$/;
213 0         0 return(${self}->{'mode'});
214             }
215              
216             =head2 pid()
217              
218             This method sets whether the process ID will be recorded in the log
219             entry. Enable PID with 1, disable with 0. Returns the value of the
220             setting.
221              
222             =cut
223              
224             sub pid {
225 1     1 1 2 my $self = shift;
226 1 50       5 if (@_) {
227 1         2 my $pset = shift;
228 1 50       9 ${self}->{'pid'} = ${pset} if ${pset} =~ /^(?:0|1)$/;
229             }
230 1         5 return(${self}->{'pid'});
231             }
232              
233             =head2 entry()
234              
235             Adds an entry to the log file accumulation buffer B. No
236             entries are ever written to disk unless and until C is
237             called.
238              
239             =cut
240              
241             sub entry {
242 2     2 1 4 my $self = shift;
243 2         7 my $pid = "[$$] - ";
244 2 100       8 $pid = '' unless ${self}->{'pid'};
245 2 50       7 if (@_) {
246 2         7 my @entries = @_;
247 2         4 foreach my $item (@entries) {
248 10         18 chomp(${item});
249 10         393 ${self}->{'acc'} .= scalar(localtime(time)). ": ${pid}" . ${item} . "\n";
250             }
251             }
252 2         10 return(1);
253             }
254              
255             =head2 commit()
256              
257             Commits the current log file data in memory to the actual file on
258             disk, and clears the log accumulation buffer.
259              
260             =cut
261              
262             sub commit {
263 2     2 1 4 my $self = shift;
264 2 50       8 return(0) unless defined(${self}->{'log_file'});
265 2         4 my $track_waits = 0;
266 2         21 my $fres = 0;
267 2 50       73 sysopen(LOG,${self}->{'log_file'},O_RDWR|O_CREAT|O_APPEND,${self}->{'mode'}) or croak('Could not open log file.');
268 2         8 my $switchhandle = select(LOG);
269 2         6 $| = 1;
270 2         7 select(${switchhandle});
271 2         4 ${self}->{'roll_allowed'} = 1;
272 2         16 $fres = flock(LOG,LOCK_EX|LOCK_NB);
273 2   33     9 while (${fres} != 1 and ${track_waits} < ${self}->{'wait_attempts'}) {
274 0         0 sleep(${self}->{'wait_interval'});
275 0         0 $track_waits++;
276 0         0 $fres = flock(LOG,LOCK_EX|LOCK_NB);
277             }
278 2 50       8 croak('Could not lock log file.') if ${track_waits} == ${self}->{'wait_attempts'};
279 2         97 print LOG (${self}->{'acc'});
280 2         5 ${self}->{'acc'} = '';
281 2 50 33     41 &roll(${self}) if defined(${self}->{'max_size'}) and ${self}->{'max_size'} =~ /^\d+$/ and ${self}->{'max_size'} > 0;
      33        
282 2         5 ${self}->{'roll_allowed'} = 0;
283 2 50       17 flock(LOG,LOCK_UN) or croak('Could not unlock log file.')
284             ;
285 2 50       26 close(LOG) or croak('Could not close log file.');
286 2         9 return(1);
287             }
288              
289             =head2 roll()
290              
291             This method rolls the oldest entries out of the logfile and leaves
292             only up to max_size lines (or less, if the contents are not that
293             long) within the logfile. Returns true (1) if log was successfully
294             rolled, or false (0) if it was not. B
295             called independantly. Doing so will simply return false (0).>
296              
297             =cut
298              
299             sub roll {
300 3     3 1 4 my $self = shift;
301 3 100       13 return(0) unless ${self}->{'roll_allowed'};
302 2         3 my (@loglines,$line);
303 2 50       18 seek(LOG,0,SEEK_SET) or croak('Unable to seek to zero to roll logfile.');
304 2         55 @loglines = ;
305 2 50       11 seek(LOG,0,SEEK_END), return(0) if not defined(${self}->{'log_file'});
306 2 100       13 seek(LOG,0,SEEK_END), return(0) if scalar(@loglines) < ${self}->{'max_size'};
307 1         3 my $length_delete_log = (scalar(@loglines)-${self}->{'max_size'});
308 1         5 splice(@loglines,0,${length_delete_log});
309 1 50       71 truncate(LOG,0) or croak('Unable to truncate while rolling logfile.')
310             ;
311 1 50       9 seek(LOG,0,SEEK_SET) or croak('Unable to seek to zero after truncation while rolling logfile.');
312 1         31 print LOG (@loglines);
313 1         15 return(1);
314             }
315              
316             =head2 clear()
317              
318             This method clears the buffered log entries B writing them
319             to file, should it be deemed necessary to "revoke" log entries
320             already made but not yet committed to file. Returns true (1).
321              
322             =cut
323              
324             sub clear {
325 0     0 1   my $self = shift;
326 0           ${self}->{'acc'} = '';
327 0           return(1);
328             }
329              
330             =head1 AUTHOR
331              
332             Mark Luljak,
333              
334             Fairlight Consulting - L
335              
336             =head1 BUGS
337              
338             Please report any bugs or feature requests to C, or through
339             the web interface at L. I will be notified, and then you'll
340             automatically be notified of progress on your bug as I make changes.
341              
342              
343              
344              
345             =head1 SUPPORT
346              
347             You can find documentation for this module with the perldoc command.
348              
349             perldoc Log::Rolling
350              
351              
352             You can also look for information at:
353              
354             =over 5
355              
356             =item * RT: CPAN's request tracker
357              
358             L
359              
360             =item * AnnoCPAN: Annotated CPAN documentation
361              
362             L
363              
364             =item * CPAN Ratings
365              
366             L
367              
368             =item * Search CPAN
369              
370             L
371              
372             =back
373              
374             =head1 COPYRIGHT & LICENSE
375              
376             Copyright 2008-2009 Mark Luljak, all rights reserved.
377              
378             This program is free software; you can redistribute it and/or modify it
379             under the same terms as Perl itself.
380              
381              
382             =cut
383              
384             1; # End of Log::Rolling