I'm posting this at the suggestion of @ikegami, who helped me in How to replace Log::Dispatch::File with ::FileRotate? and noted some unexpected behavior.
The main point is that Perl's Log::Dispatch::FileRotate automatically moves log files according to different constraints (time, size, etc.), and has a post_rotate callback in the constructor so you can perform actions after the file rotation is complete. This does not behave in an obvious way in what I consider to be the most obvious use case: when you don't want to create multiple backup logs called filename.1, filename.2, etc., but rather when you want to immediately move the backup somewhere else. The docs do explain that when you're dealing with the first rotated file, the filename passed to the callback is "the new, empty, log file", but what you probably want (at least somewhere) is the name of the just-rotated file.
Below is a MCVE showing this behavior: the log1 actions, which would seem to be the way one "should" code this, do not work in a sensible way.
In order to get the "expected" result, you need to use the log2 actions to recreate the name of the just-rotated file. This is not really documented, and it's not very clean to regenerate the name of the file. Is this the only way, or is there a better way of doing this?
#!/usr/bin/env perl
use strict;
use warnings;
use Log::Dispatch::FileRotate;
my $rotate_size = 1_000;
##### setup code
my $file1 = "log-one.log";
my $backupdir1 = "Old1";
my $log1 = Log::Dispatch->new(
outputs => [
[ 'FileRotate',
min_level => 'debug',
mode => '>>',
newline => 1,
filename => $file1,
size => $rotate_size,
post_rotate => sub {
my ($filename, $index, $fileRotate ) = @_;
print "ONE: filename: [$filename]; fileRotate filename: [". $fileRotate->filename()."]\n";
mkdir $backupdir1 unless -e $backupdir1;
my $new_filename = $backupdir1 . "/" . $filename . "-" . time();
rename($filename, $new_filename) or die "Can't move first logfile: $!";
},
],
],
);
my $file2 = "log-two.log";
my $backupdir2 = "Old2";
my $log2 = Log::Dispatch->new(
outputs => [
[ 'FileRotate',
min_level => 'debug',
mode => '>>',
newline => 1,
filename => $file2,
size => $rotate_size,
post_rotate => sub {
my ($filename, $index, $fileRotate ) = @_;
print "TWO: filename: [$filename]; fileRotate filename: [". $fileRotate->filename()."]\n";
mkdir $backupdir2 unless -e $backupdir2;
my $old_file = $filename . ".1"; # already-rotated file
my $new_filename = $backupdir2 . "/" . $file2 . "-" . time();
rename($old_file, $new_filename) or die "Can't move second logfile: $!";
},
],
],
);
##### We do stuff here
$log1->info("starting!");
open(my $fh, '>>', $file1) or die "Could not open file '$file1' $!\n";
print $fh "x" x $rotate_size;
close $fh;
$log1->info("finished!");
# "Sensible" result: a 1010-byte file called Old1/log-one.log-160134[etc].
# consisting of "starting!\n" plus 1,000 "x"s; a 10-byte file called test.log
# consisting of "finished!\n"
#
# Actual result: a 10-byte file called Old1/log-one.log-160134[etc], consisting
# of "finished!\n"; a 1010-byte file called log.one-log.1 consisting of "starting!\n"
# plus 1,000 "x"s.
$log2->info("starting!");
open($fh, '>>', $file2) or die "Could not open file '$file2' $!\n";
print $fh "x" x $rotate_size;
close $fh;
$log2->info("finished!");
# This is the desired result: the old, rotated log is archived as
# Old2/log-two.log-160134[etc], consisting of "starting!\n" plus 1,000 "x"s;
# the "current" log is "log-two.log", consisting of "finished!\n". But this
# requires some non-obvious stuff in post_rotate, where we have to recreate
# the filename of the just-rotated file.