File Coverage

blib/lib/File/Write/Rotate.pm
Criterion Covered Total %
statement 217 235 92.3
branch 97 132 73.4
condition 35 43 81.4
subroutine 21 22 95.4
pod 7 7 100.0
total 377 439 85.8


line stmt bran cond sub pod time code
1             ## no critic: InputOutput::ProhibitOneArgSelect
2              
3             package File::Write::Rotate;
4              
5             our $DATE = '2019-06-27'; # DATE
6             our $VERSION = '0.321'; # VERSION
7              
8 3     3   269388 use 5.010001;
  3         30  
9 3     3   15 use strict;
  3         6  
  3         60  
10 3     3   14 use warnings;
  3         6  
  3         80  
11              
12             # we must not use Log::Any, looping if we are used as log output
13             #use Log::Any '$log';
14              
15 3     3   16 use File::Spec;
  3         5  
  3         80  
16 3     3   1737 use IO::Compress::Gzip qw(gzip $GzipError);
  3         103891  
  3         364  
17 3     3   29 use Scalar::Util qw(weaken);
  3         6  
  3         141  
18             #use Taint::Runtime qw(untaint is_tainted);
19 3     3   1604 use Time::HiRes 'time';
  3         4100  
  3         14  
20              
21             our $Debug;
22              
23             sub new {
24 278     278 1 201746 my $class = shift;
25 278         1074 my %args0 = @_;
26              
27 278         461 my %args;
28              
29             defined($args{dir} = delete $args0{dir})
30 278 50       1088 or die "Please specify dir";
31             defined($args{prefix} = delete $args0{prefix})
32 278 50       752 or die "Please specify prefix";
33 278   100     1046 $args{suffix} = delete($args0{suffix}) // "";
34              
35 278   100     703 $args{size} = delete($args0{size}) // 0;
36              
37 278         474 $args{period} = delete($args0{period});
38 278 100       556 if ($args{period}) {
39 39 50       229 $args{period} =~ /\A(daily|day|month|monthly|year|yearly)\z/
40             or die "Invalid period, please use daily/monthly/yearly";
41             }
42              
43 278         633 for (map {"hook_$_"} qw(before_rotate after_rotate after_create
  1390         2981  
44             before_write a)) {
45 1390 100       2741 next unless $args0{$_};
46 17         36 $args{$_} = delete($args0{$_});
47             die "Invalid $_, please supply a coderef"
48 17 50       49 unless ref($args{$_}) eq 'CODE';
49             }
50              
51 278 100 100     1106 if (!$args{period} && !$args{size}) {
52 21         40 $args{size} = 10 * 1024 * 1024;
53             }
54              
55 278   100     836 $args{histories} = delete($args0{histories}) // 10;
56              
57 278         465 $args{binmode} = delete($args0{binmode});
58              
59 278         600 $args{buffer_size} = delete($args0{buffer_size});
60              
61 278   100     737 $args{lock_mode} = delete($args0{lock_mode}) // 'write';
62 278 50       1424 $args{lock_mode} =~ /\A(none|write|exclusive)\z/
63             or die "Invalid lock_mode, please use none/write/exclusive";
64              
65 278         485 $args{rotate_probability} = delete($args0{rotate_probability});
66 278 50       532 if (defined $args{rotate_probability}) {
67 0 0 0     0 $args{rotate_probability} > 0 && $args{rotate_probability} < 1.0
68             or die "Invalid rotate_probability, must be 0 < x < 1";
69             }
70              
71 278 50       619 if (keys %args0) {
72 0         0 die "Unknown arguments to new(): ".join(", ", sort keys %args0);
73             }
74              
75 278         513 $args{_buffer} = [];
76              
77 278         586 my $self = bless \%args, $class;
78              
79             $self->{_exclusive_lock} = $self->_get_lock
80 278 100       654 if $self->{lock_mode} eq 'exclusive';
81              
82 278         822 $self;
83             }
84              
85             sub buffer_size {
86 2     2 1 11 my $self = shift;
87 2 100       7 if (@_) {
88 1         3 my $old = $self->{buffer_size};
89 1         2 $self->{buffer_size} = $_[0];
90 1         2 return $old;
91             } else {
92 1         5 return $self->{buffer_size};
93             }
94             }
95              
96             sub handle {
97 1     1 1 8 my $self = shift;
98 1         3 $self->{_fh};
99             }
100              
101             sub path {
102 0     0 1 0 my $self = shift;
103 0         0 $self->{_fp};
104             }
105              
106             # file path, without the rotate suffix
107             sub _file_path {
108 801     801   3413 my ($self) = @_;
109              
110             # _now is calculated every time we access this method
111 801         2086 $self->{_now} = time();
112              
113 801         4383 my @lt = localtime($self->{_now});
114 801         1806 $lt[5] += 1900;
115 801         1033 $lt[4]++;
116              
117 801         1012 my $period;
118 801 100       1699 if ($self->{period}) {
119 110 100       714 if ($self->{period} =~ /year/i) {
    100          
    50          
120 13         51 $period = sprintf("%04d", $lt[5]);
121             } elsif ($self->{period} =~ /month/) {
122 13         62 $period = sprintf("%04d-%02d", $lt[5], $lt[4]);
123             } elsif ($self->{period} =~ /day|daily/) {
124 84         407 $period = sprintf("%04d-%02d-%02d", $lt[5], $lt[4], $lt[3]);
125             }
126             } else {
127 691         1028 $period = "";
128             }
129              
130             my $path = join(
131             '',
132             $self->{dir}, '/',
133             $self->{prefix},
134             length($period) ? ".$period" : "",
135             $self->{suffix},
136 801 100       2715 );
137 801 100       1536 if (wantarray) {
138 308         1050 return ($path, $period);
139             } else {
140 493         1378 return $path;
141             }
142             }
143              
144             sub lock_file_path {
145 996     996 1 3477 my ($self) = @_;
146 996         20508 return File::Spec->catfile($self->{dir}, $self->{prefix} . '.lck');
147             }
148              
149             sub _get_lock {
150 592     592   987 my ($self) = @_;
151 592 100       1398 return undef if $self->{lock_mode} eq 'none';
152 588 100       1153 return $self->{_weak_lock} if defined($self->{_weak_lock});
153              
154 510         4642 require File::Flock::Retry;
155 510         4827 my $lock = File::Flock::Retry->lock($self->lock_file_path);
156 510         67191 $self->{_weak_lock} = $lock;
157 510         2155 weaken $self->{_weak_lock};
158 510         1131 return $lock;
159             }
160              
161             # will return \@files. each entry is [filename without compress suffix,
162             # rotate_suffix (for sorting), period (for sorting), compress suffix (for
163             # renaming back)]
164             sub _get_files {
165 98     98   155 my ($self) = @_;
166              
167 98 50       2636 opendir my ($dh), $self->{dir} or do {
168 0         0 warn "Can't opendir '$self->{dir}': $!";
169 0         0 return;
170             };
171              
172 98         245 my @files;
173 98         1568 while (my $e = readdir($dh)) {
174 463 100       822 my $cs; $cs = $1 if $e =~ s/(\.gz)\z//; # compress suffix
  463         942  
175 463 100       3898 next unless $e =~ /\A\Q$self->{prefix}\E
176             (?:\. (?<period>\d{4}(?:-\d\d(?:-\d\d)?)?) )?
177             \Q$self->{suffix}\E
178             (?:\. (?<rotate_suffix>\d+) )?
179             \z
180             /x;
181             push @files,
182 3   100 3   5345 [ $e, $+{rotate_suffix} // 0, $+{period} // "", $cs // "" ];
  3   100     1042  
  3   100     5140  
  152         2538  
183             }
184 98         1088 closedir($dh);
185              
186 98 50       635 [ sort { $a->[2] cmp $b->[2] || $b->[1] <=> $a->[1] } @files ];
  123         622  
187             }
188              
189             # rename (increase rotation suffix) and keep only n histories. note: failure in
190             # rotating should not be fatal, we just warn and return.
191             sub _rotate_and_delete {
192 85     85   9516 my ($self, %opts) = @_;
193              
194 85         211 my $delete_only = $opts{delete_only};
195 85         173 my $lock = $self->_get_lock;
196             CASE:
197             {
198 85 50       148 my $files = $self->_get_files or last CASE;
  85         168  
199              
200             # is there a compression process in progress? this is marked by the
201             # existence of <prefix>-compress.pid PID file.
202             #
203             # XXX check validity of PID file, otherwise a stale PID file will always
204             # prevent rotation to be done
205 85 50       1069 if (-f "$self->{dir}/$self->{prefix}-compress.pid") {
206 0         0 warn "Compression is in progress, rotation is postponed";
207 0         0 last CASE;
208             }
209              
210 7         35 $self->{hook_before_rotate}->($self, [map {$_->[0]} @$files])
211 85 100       324 if $self->{hook_before_rotate};
212              
213 85         3702 my @deleted;
214             my @renamed;
215              
216 85         0 my $i;
217 85         178 my $dir = $self->{dir};
218 85 100       239 my $rotating_period = @$files ? $files->[-1][2] : undef;
219 85         199 for my $f (@$files) {
220 122         336 my ($orig, $rs, $period, $cs) = @$f;
221 122         539 $i++;
222              
223             #say "DEBUG: is_tainted \$dir? ".is_tainted($dir);
224             #say "DEBUG: is_tainted \$orig? ".is_tainted($orig);
225             #say "DEBUG: is_tainted \$cs? ".is_tainted($cs);
226              
227             # TODO actually, it's more proper to taint near the source (in this
228             # case, _get_files)
229             #untaint \$orig;
230 122         541 ($orig) = $orig =~ /(.*)/s; # we use this instead, no module needed
231              
232 122 100       376 if ($i <= @$files - $self->{histories}) {
233 22 50       49 say "DEBUG: Deleting old rotated file $dir/$orig$cs ..."
234             if $Debug;
235 22 50       913 if (unlink "$dir/$orig$cs") {
236 22         118 push @deleted, "$orig$cs";
237             } else {
238 0         0 warn "Can't delete $dir/$orig$cs: $!";
239             }
240 22         73 next;
241             }
242 100 100 66     455 if (!$delete_only && defined($rotating_period) && $period eq $rotating_period) {
      100        
243 48         89 my $new = $orig;
244 48 100       107 if ($rs) {
245 14         90 $new =~ s/\.(\d+)\z/"." . ($1+1)/e;
  14         113  
246             } else {
247 34         64 $new .= ".1";
248             }
249 48 50       111 if ($new ne $orig) {
250 48 50       112 say "DEBUG: Renaming rotated file $dir/$orig$cs -> ".
251             "$dir/$new$cs ..." if $Debug;
252 48 50       1576 if (rename "$dir/$orig$cs", "$dir/$new$cs") {
253 48         315 push @renamed, "$new$cs";
254             } else {
255 0         0 warn "Can't rename '$dir/$orig$cs' -> '$dir/$new$cs': $!";
256             }
257             }
258             }
259             }
260              
261             $self->{hook_after_rotate}->($self, \@renamed, \@deleted)
262 85 100       466 if $self->{hook_after_rotate};
263             } # CASE
264             }
265              
266             sub _open {
267 298     298   485 my $self = shift;
268              
269 298         542 my ($fp, $period) = $self->_file_path;
270 298 50       11314 open $self->{_fh}, ">>", $fp or die "Can't open '$fp': $!";
271 298 100       1186 if (defined $self->{binmode}) {
272 2 50       8 if ($self->{binmode} eq "1") {
273 0         0 binmode $self->{_fh};
274             } else {
275             binmode $self->{_fh}, $self->{binmode}
276 2 50       30 or die "Can't set PerlIO layer on '$fp' ".
277             "to '$self->{binmode}': $!";
278             }
279             }
280 298         1151 my $oldfh = select $self->{_fh};
281 298         866 $| = 1;
282 298         945 select $oldfh; # set autoflush
283 298         689 $self->{_fp} = $fp;
284 298 100       1147 $self->{hook_after_create}->($self) if $self->{hook_after_create};
285             }
286              
287             # (re)open file and optionally rotate if necessary
288             sub _rotate_and_open {
289              
290 493     493   787 my $self = shift;
291 493         896 my ($do_open, $do_rotate) = @_;
292 493         787 my $fp;
293             my %rotate_params;
294              
295             CASE:
296             {
297             # if instructed, only do rotate some of the time to shave overhead
298 493 0 33     675 if ($self->{rotate_probability} && $self->{_fh}) {
  493         1255  
299 0 0       0 last CASE if rand() > $self->{rotate_probability};
300             }
301              
302 493         1033 $fp = $self->_file_path;
303 493 100       6320 unless (-e $fp) {
304 50         133 $do_open++;
305 50         80 $do_rotate++;
306 50         134 $rotate_params{delete_only} = 1;
307 50         103 last CASE;
308             }
309              
310             # file is not opened yet, open
311 443 100       1584 unless ($self->{_fh}) {
312 219         510 $self->_open;
313             }
314              
315             # period has changed, rotate
316 443 50       1031 if ($self->{_fp} ne $fp) {
317 0         0 $do_rotate++;
318 0         0 $rotate_params{delete_only} = 1;
319 0         0 last CASE;
320             }
321              
322             # check whether size has been exceeded
323 443         619 my $inode;
324              
325 443 100       1003 if ($self->{size} > 0) {
326              
327 437         3896 my @st = stat($self->{_fh});
328 437         1048 my $size = $st[7];
329 437         592 $inode = $st[1];
330              
331 437 100       1089 if ($size >= $self->{size}) {
332 28 50       89 say "DEBUG: Size of $self->{_fp} is $size, exceeds $self->{size}, rotating ..."
333             if $Debug;
334 28         53 $do_rotate++;
335 28         77 last CASE;
336             } else {
337             # stat the current file (not our handle _fp)
338 409         4132 my @st = stat($fp);
339 409 50       1141 die "Can't stat '$fp': $!" unless @st;
340 409         676 my $finode = $st[1];
341              
342             # check whether other process has rename/rotate under us (for
343             # example, 'prefix' has been moved to 'prefix.1'), in which case
344             # we need to reopen
345 409 100 66     2072 if (defined($inode) && $finode != $inode) {
346 1         4 $do_open++;
347             }
348             }
349              
350             }
351             } # CASE
352              
353 493 100       1133 $self->_rotate_and_delete(%rotate_params) if $do_rotate;
354 493 100 100     5017 $self->_open if $do_rotate || $do_open; # (re)open
355             }
356              
357             sub write {
358 493     493 1 131656 my $self = shift;
359              
360             # the buffering implementation is currently pretty naive. it assume any
361             # die() as a write failure and store the message to buffer.
362              
363             # FYI: if privilege is dropped from superuser, the failure is usually at
364             # locking the lock file (permission denied).
365              
366 493         803 my @msg = (map( {@$_} @{ $self->{_buffer} } ), @_);
  6         18  
  493         1385  
367              
368 493         868 eval {
369 493         1052 my $lock = $self->_get_lock;
370              
371 493         1543 $self->_rotate_and_open;
372              
373             $self->{hook_before_write}->($self, \@msg, $self->{_fh})
374 493 100       3765 if $self->{hook_before_write};
375              
376 488         3208 print { $self->{_fh} } @msg;
  488         8371  
377 488         3384 $self->{_buffer} = [];
378              
379             };
380 493         38096 my $err = $@;
381              
382 493 100       1889 if ($err) {
383 5 100 50     18 if (($self->{buffer_size} // 0) > @{ $self->{_buffer} }) {
  5         17  
384             # put message to buffer temporarily
385 4         6 push @{ $self->{_buffer} }, [@_];
  4         30  
386             } else {
387             # buffer is already full, let's dump the buffered + current message
388             # to the die message anyway.
389             die join(
390             "",
391             "Can't write",
392             (
393 1         4 @{ $self->{_buffer} }
394             ? " (buffer is full, "
395 1 50       3 . scalar(@{ $self->{_buffer} })
  1         15  
396             . " message(s))"
397             : ""
398             ),
399             ": $err, message(s)=",
400             @msg
401             );
402             }
403             }
404             }
405              
406             sub compress {
407 13     13 1 10510 my ($self) = shift;
408              
409 13         38 my $lock = $self->_get_lock;
410 13         43 my $files_ref = $self->_get_files;
411 13         28 my $done_compression = 0;
412              
413 13 50       24 if (@{$files_ref}) {
  13         37  
414 13         707 require Proc::PID::File;
415              
416             my $pid = Proc::PID::File->new(
417             dir => $self->{dir},
418 13         2430 name => "$self->{prefix}-compress",
419             verify => 1,
420             );
421 13         453 my $latest_period = $files_ref->[-1][2];
422              
423 13 50       38 if ($pid->alive) {
424 0         0 warn "Another compression is in progress";
425             } else {
426 13         1629 my @tocompress;
427             #use DD; dd $self;
428 13         24 for my $file_ref (@{$files_ref}) {
  13         34  
429 30         51 my ($orig, $rs, $period, $cs) = @{ $file_ref };
  30         73  
430             #say "D:compress: orig=<$orig> rs=<$rs> period=<$period> cs=<$cs>";
431 30 50       68 next if $cs; # already compressed
432 30 100 100     143 next if !$self->{period} && !$rs; # not old file
433 25 100 100     94 next if $self->{period} && $period eq $latest_period; # not old file
434 17         206 push @tocompress, File::Spec->catfile($self->{dir}, $orig);
435             }
436              
437 13 50       34 if (@tocompress) {
438 13         24 for my $file (@tocompress) {
439             gzip($file => "$file.gz")
440 17 50       86 or do { warn "gzip failed: $GzipError\n"; next };
  0         0  
  0         0  
441 17         40152 unlink $file;
442             }
443 13         108 $done_compression = 1;
444             }
445             }
446             }
447              
448 13         1309 return $done_compression;
449              
450             }
451              
452             sub DESTROY {
453 278     278   141267 my ($self) = @_;
454              
455             # Proc::PID::File's DESTROY seem to create an empty PID file, remove it.
456 278         8139 unlink "$self->{dir}/$self->{prefix}-compress.pid";
457             }
458              
459             1;
460              
461             # ABSTRACT: Write to files that archive/rotate themselves
462              
463             __END__
464              
465             =pod
466              
467             =encoding UTF-8
468              
469             =head1 NAME
470              
471             File::Write::Rotate - Write to files that archive/rotate themselves
472              
473             =head1 VERSION
474              
475             This document describes version 0.321 of File::Write::Rotate (from Perl distribution File-Write-Rotate), released on 2019-06-27.
476              
477             =head1 SYNOPSIS
478              
479             use File::Write::Rotate;
480              
481             my $fwr = File::Write::Rotate->new(
482             dir => '/var/log', # required
483             prefix => 'myapp', # required
484             #suffix => '.log', # default is ''
485             size => 25*1024*1024, # default is 10MB, unless period is set
486             histories => 12, # default is 10
487             #buffer_size => 100, # default is none
488             );
489              
490             # write, will write to /var/log/myapp.log, automatically rotate old log files
491             # to myapp.log.1 when myapp.log reaches 25MB. will keep old log files up to
492             # myapp.log.12.
493             $fwr->write("This is a line\n");
494             $fwr->write("This is", " another line\n");
495              
496             To compressing old log files:
497              
498             $fwr->compress;
499              
500             This is usually done in a separate process, because it potentially takes a long
501             time if the files to compress are large; we are rotating automatically in
502             write() so doing automatic compression too would annoyingly block writer for a
503             potentially long time.
504              
505             =head1 DESCRIPTION
506              
507             This module can be used to write to file, usually for logging, that can rotate
508             itself. File will be opened in append mode. By default, locking will be done to
509             avoid conflict when there are multiple writers. Rotation can be done by size
510             (after a certain size is reached), by time (daily/monthly/yearly), or both.
511              
512             I first wrote this module for logging script STDERR output to files (see
513             L<Tie::Handle::FileWriteRotate>).
514              
515             =for Pod::Coverage ^(file_path|DESTROY)$
516              
517             =head1 ATTRIBUTES
518              
519             =head2 buffer_size => int
520              
521             Get or set buffer size. If set to a value larger than 0, then when a write()
522             failed, instead of dying, the message will be stored in an internal buffer first
523             (a regular Perl array). When the number of items in the buffer exceeds this
524             size, then write() will die upon failure. Otherwise, every write() will try to
525             flush the buffer.
526              
527             Can be used for example when a program runs as superuser/root then temporarily
528             drops privilege to a normal user. During this period, logging can fail because
529             the program cannot lock the lock file or write to the logging directory. Before
530             dropping privilege, the program can set buffer_size to some larger-than-zero
531             value to hold the messages emitted during dropping privilege. The next write()
532             as the superuser/root will succeed and flush the buffer to disk (provided there
533             is no other error condition, of course).
534              
535             =head2 path => str (ro)
536              
537             Current file's path.
538              
539             =head2 handle => (ro)
540              
541             Current file handle. You should not use this directly, but use write() instead.
542             This attribute is provided for special circumstances (e.g. in hooks, see example
543             in the hook section).
544              
545             =head2 hook_before_write => code
546              
547             Will be called by write() before actually writing to filehandle (but after
548             locking is done). Code will be passed ($self, \@msgs, $fh) where @msgs is an
549             array of strings to be written (the contents of buffer, if any, plus arguments
550             passed to write()) and $fh is the filehandle.
551              
552             =head2 hook_before_rotate => code
553              
554             Will be called by the rotating routine before actually doing rotating. Code will
555             be passed ($self).
556              
557             This can be used to write a footer to the end of each file, e.g.:
558              
559             # hook_before_rotate
560             my ($self) = @_;
561             my $fh = $self->handle;
562             print $fh "Some footer\n";
563              
564             Since this hook is indirectly called by write(), locking is already done.
565              
566             =head2 hook_after_rotate => code
567              
568             Will be called by the rotating routine after the rotating process. Code will be
569             passed ($self, \@renamed, \@deleted) where @renamed is array of new filenames
570             that have been renamed, @deleted is array of new filenames that have been
571             deleted.
572              
573             =head2 hook_after_create => code
574              
575             Will be called by after a new file is created. Code will be passed ($self).
576              
577             This hook can be used to write a header to each file, e.g.:
578              
579             # hook_after_create
580             my ($self) = @_;
581             my $fh $self->handle;
582             print $fh "header\n";
583              
584             Since this is called indirectly by write(), locking is also already done.
585              
586             =head2 binmode => str
587              
588             If set to "1", will cause the file handle to be set:
589              
590             binmode $fh;
591              
592             which might be necessary on some OS, e.g. Windows when writing binary data.
593             Otherwise, other defined values will cause the file handle to be set:
594              
595             binmode $fh, $value
596              
597             which can be used to set PerlIO layer(s).
598              
599             =head1 METHODS
600              
601             =head2 $obj = File::Write::Rotate->new(%args)
602              
603             Create new object. Known arguments:
604              
605             =over
606              
607             =item * dir => STR (required)
608              
609             Directory to put the files in.
610              
611             =item * prefix => STR (required)
612              
613             Name of files. The files will be named like the following:
614              
615             <prefix><period><suffix><rotate_suffix>
616              
617             C<< <period> >> will only be given if the C<period> argument is set. If
618             C<period> is set to C<yearly>, C<< <period> >> will be C<YYYY> (4-digit year).
619             If C<period> is C<monthly>, C<< <period> >> will be C<YYYY-MM> (4-digit year and
620             2-digit month). If C<period> is C<daily>, C<< <period> >> will be C<YYYY-MM-DD>
621             (4-digit year, 2-digit month, and 2-digit day).
622              
623             C<< <rotate_suffix> >> is either empty string for current file; or C<.1>, C<.2>
624             and so on for rotated files. C<.1> is the most recent rotated file, C<.2> is the
625             next most recent, and so on.
626              
627             An example, with C<prefix> set to C<myapp>:
628              
629             myapp # current file
630             myapp.1 # most recently rotated
631             myapp.2 # the next most recently rotated
632              
633             With C<prefix> set to C<myapp>, C<period> set to C<monthly>, C<suffix> set to
634             C<.log>:
635              
636             myapp.2012-12.log # file name for december 2012
637             myapp.2013-01.log # file name for january 2013
638              
639             Like previous, but additionally with C<size> also set (which will also rotate
640             each period file if it exceeds specified size):
641              
642             myapp.2012-12.log # file(s) for december 2012
643             myapp.2012-12.log.1
644             myapp.2012-12.log.2
645             myapp.2013-01.log # file(s) for january 2013
646              
647             All times will use local time, so you probably want to set C<TZ> environment
648             variable or equivalent methods to set time zone.
649              
650             =item * suffix => STR (default: '')
651              
652             Suffix to give to file names, usually file extension like C<.log>. See C<prefix>
653             for more details.
654              
655             If you use a yearly period, setting suffix is advised to avoid ambiguity with
656             rotate suffix (for example, is C<myapp.2012> the current file for year 2012 or
657             file with C<2012> rotate suffix?)
658              
659             =item * size => INT (default: 10*1024*1024)
660              
661             Maximum file size, in bytes, before rotation is triggered. The default is 10MB
662             (10*1024*1024) I<if> C<period> is not set. If C<period> is set, no default for
663             C<size> is provided, which means files will not be rotated for size (only for
664             period).
665              
666             =item * period => STR
667              
668             Can be set to either C<daily>, C<monthly>, or C<yearly>. If set, will
669             automatically rotate after period change. See C<prefix> for more details.
670              
671             =item * histories => INT (default: 10)
672              
673             Number of rotated files to keep. After the number of files exceeds this, the
674             oldest one will be deleted. 0 means not to keep any history, 1 means to only
675             keep C<.1> file, and so on.
676              
677             =item * buffer_size => INT (default: 0)
678              
679             Set initial value of buffer. See the C<buffer_size> attribute for more
680             information.
681              
682             =item * lock_mode => STR (default: 'write')
683              
684             Can be set to either C<none>, C<write>, or C<exclusive>. C<none> disables
685             locking and increases write performance, but should only be used when there is
686             only one writer. C<write> acquires and holds the lock for each write.
687             C<exclusive> acquires the lock at object creation and holds it until the the
688             object is destroyed.
689              
690             Lock file is named C<< <prefix> >>C<.lck>. Will wait for up to 1 minute to
691             acquire lock, will die if failed to acquire lock.
692              
693             =item * hook_before_write => CODE
694              
695             =item * hook_before_rotate => CODE
696              
697             =item * hook_after_rotate => CODE
698              
699             =item * hook_after_create => CODE
700              
701             See L</ATTRIBUTES>.
702              
703             =item * buffer_size => int
704              
705             =item * rotate_probability => float (between 0 < x < 1)
706              
707             If set, instruct to only check for rotation under a certain probability, for
708             example if value is set to 0.1 then will only check for rotation 10% of the
709             time.
710              
711             =back
712              
713             =head2 lock_file_path => STR
714              
715             Returns a string representing the complete pathname to the lock file, based
716             on C<dir> and C<prefix> attributes.
717              
718             =head2 $fwr->write(@args)
719              
720             Write to file. Will automatically rotate file if period changes or file size
721             exceeds specified limit. When rotating, will only keep a specified number of
722             histories and delete the older ones.
723              
724             Does not append newline so you'll have to do it yourself.
725              
726             =head2 $fwr->compress
727              
728             Compress old rotated files and remove the uncompressed originals. Currently uses
729             L<IO::Compress::Gzip> to do the compression. Extension given to compressed file
730             is C<.gz>.
731              
732             Will not lock writers, but will create C<< <prefix> >>C<-compress.pid> PID file
733             to prevent multiple compression processes running and to signal the writers to
734             postpone rotation.
735              
736             After compression is finished, will remove the PID file, so rotation can be done
737             again on the next C<write()> if necessary.
738              
739             =head1 FAQ
740              
741             =head2 Why use autorotating file?
742              
743             Mainly convenience and low maintenance. You no longer need a separate rotator
744             process like the Unix B<logrotate> utility (which when accidentally disabled or
745             misconfigured will cause your logs to stop being rotated and grow indefinitely).
746              
747             =head2 What is the downside of using FWR (and LDFR)?
748              
749             Mainly (significant) performance overhead. At (almost) every C<write()>, FWR
750             needs to check file sizes and/or dates for rotation. Under default configuration
751             (where C<lock_mode> is C<write>), it also performs locking on each C<write()> to
752             make it safe to use with multiple processes. Below is a casual benchmark to give
753             a sense of the overhead, tested on my Core i5-2400 3.1GHz desktop:
754              
755             Writing lines in the size of ~ 200 bytes, raw writing to disk (SSD) has the
756             speed of around 3.4mil/s, while using FWR it goes down to around ~13k/s. Using
757             C<lock_mode> C<none> or C<exclusive>, the speed is ~52k/s.
758              
759             However, this is not something you'll notice or need to worry about unless
760             you're writing near that speed.
761              
762             If you need more speed, you can try setting C<rotate_probability> which will
763             cause FWR to only check for rotation probabilistically, e.g. if you set this to
764             0.1 then checks will only be done in about 1 of 10 writes. This can
765             significantly reduce the overhead and increase write speed several times (e.g.
766             5-8 times), but understand that this will make the writes "overflow" a bit, e.g.
767             file sizes will exceed for a bit if you do size-based rotation. More suitable if
768             you only do size-based rotation since it is usually okay to exceed sizes for a
769             bit.
770              
771             =head2 I want a filehandle instead of a File::Write::Rotate object!
772              
773             Use L<Tie::Handle::FileWriteRotate>.
774              
775             =head1 HOMEPAGE
776              
777             Please visit the project's homepage at L<https://metacpan.org/release/File-Write-Rotate>.
778              
779             =head1 SOURCE
780              
781             Source repository is at L<https://github.com/perlancar/perl-File-Write-Rotate>.
782              
783             =head1 BUGS
784              
785             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=File-Write-Rotate>
786              
787             When submitting a bug or request, please include a test-file or a
788             patch to an existing test-file that illustrates the bug or desired
789             feature.
790              
791             =head1 SEE ALSO
792              
793             L<Log::Dispatch::FileRotate>, which inspires this module. Differences between
794             File::Write::Rotate (FWR) and Log::Dispatch::FileRotate (LDFR) are as follows:
795              
796             =over
797              
798             =item * FWR is not part of the L<Log::Dispatch> family.
799              
800             This makes FWR more general to use.
801              
802             For using together with Log::Dispatch/Log4perl, I have also written
803             L<Log::Dispatch::FileWriteRotate> which is a direct (although not a perfect
804             drop-in) replacement for Log::Dispatch::FileRotate.
805              
806             =item * Secondly, FWR does not use L<Date::Manip>.
807              
808             Date::Manip is relatively large (loading Date::Manip 6.37 equals to loading 34
809             files and ~ 22k lines; while FWR itself is only < 1k lines!)
810              
811             As a consequence of this, FWR does not support DatePattern; instead, FWR
812             replaces it with a simple daily/monthly/yearly period.
813              
814             =item * And lastly, FWR supports compressing and rotating compressed old files.
815              
816             Using separate processes like the Unix B<logrotate> utility means having to deal
817             with yet another race condition. FWR takes care of that for you (see the
818             compress() method). You also have the option to do file compression in the same
819             script/process if you want, which is convenient.
820              
821             =back
822              
823             There is no significant overhead difference between FWR and LDFR (FWR is
824             slightly faster than LDFR on my testing).
825              
826             L<Tie::Handle::FileWriteRotate> and Log::Dispatch::FileWriteRotate, which use
827             this module.
828              
829             =head1 AUTHOR
830              
831             perlancar <perlancar@cpan.org>
832              
833             =head1 COPYRIGHT AND LICENSE
834              
835             This software is copyright (c) 2019, 2016, 2015, 2014, 2013, 2012 by perlancar@cpan.org.
836              
837             This is free software; you can redistribute it and/or modify it under
838             the same terms as the Perl 5 programming language system itself.
839              
840             =cut