created => scalar localtime,
mcppid => $$,
verbose => 1,
- debugsyslog => 1,
- debugdir => './tmp',
- debugfile => 0,
+ logdest => ['/var/log/bucardo'],
warning_file => '',
- debugfilesep => 0,
- debugname => '',
- cleandebugs => 0,
+ logseparate => 0,
+ logextension => '',
+ logclean => 0,
dryrun => 0,
sendmail => 1,
extraname => '',
## Transform our hash into a genuine 'Bucardo' object:
bless $self, $class;
- ## Remove any previous debugging files if requested
- if ($self->{cleandebugs}) {
+ ## Remove any previous log files if requested
+ if ($self->{logclean} && (my @dirs = grep {
+ $_ !~ /^(?:std(?:out|err)|none|syslog)/
+ } @{ $self->{logdest} }) ) {
## If the dir does not exists, silently proceed
- if (opendir my $dh, $self->{debugdir}) {
+ for my $dir (@dirs) {
+ opendir my $dh, $dir or next;
## We look for any files that start with 'log.bucardo' plus another dot
for my $file (grep { /^log\.bucardo\./ } readdir $dh) {
- my $fullfile = File::Spec->catfile( $self->{debugdir} => $file );
+ my $fullfile = File::Spec->catfile( $dir => $file );
unlink $fullfile or warn qq{Could not remove "$fullfile": $!\n};
}
- closedir $dh or warn qq{Could not closedir "$self->{debugdir}": $!\n};
+ closedir $dh or warn qq{Could not closedir "$dir": $!\n};
}
}
## Load in the configuration information
$self->reload_config_database();
- ## If using syslog, open with the current facility
- if ($self->{debugsyslog}) {
- openlog 'Bucardo', 'pid nowait', $config{syslog_facility};
- }
-
## Figure out if we are writing emails to a file
$self->{sendmail_file} = $ENV{BUCARDO_EMAIL_DEBUG_FILE} || $config{email_debug_file} || '';
$self->glog("Starting Bucardo version $VERSION", LOG_WARN);
$self->glog("Log level: $config{log_level}", LOG_WARN);
+ ## Close unused file handles.
+ unless (grep { $_ eq 'stderr' } @{ $self->{logdest} }) {
+ close STDERR or warn "Could not close STDERR\n";
+ }
+ unless (grep { $_ eq 'stdout' } @{ $self->{logdest} }) {
+ close STDOUT or warn "Could not close STDOUT\n";
+ }
+
## Create a new (temporary) PID file
## We will overwrite later with a new PID once we do the initial fork
open my $pidfh, '>', $self->{pidfile}
} ## end of log_config
+sub _logto {
+ my $self = shift;
+ if ($self->{logpid} && $self->{logpid} != $$) {
+ # We've forked! Get rid of any existing handles.
+ delete $self->{logcodes};
+ }
+ return @{ $self->{logcodes} } if $self->{logcodes};
+
+ # Do no logging if any destination is "none".
+ return @{ $self->{logcodes} = [] }
+ if grep { $_ eq 'none' } @{ $self->{logdest} };
+
+ $self->{logpid} = $$;
+ my %code_for;
+ for my $dest (@{ $self->{logdest}} ) {
+ next if $code_for{$dest};
+ if ($dest eq 'syslog') {
+ openlog 'Bucardo', 'pid nowait', $config{syslog_facility};
+ ## Ignore the header argument for syslog output.
+ $code_for{syslog} = sub { shift; syslog 'info', @_ };
+ } elsif ($dest eq 'stderr') {
+ $code_for{stderr} = sub { print STDERR @_, $/ };
+ } elsif ($dest eq 'stdout') {
+ $code_for{stdout} = sub { print STDOUT @_, $/ };
+ } else {
+ my $fn = File::Spec->catfile($dest, 'log.bucardo');
+ $fn .= ".$self->{logextension}" if length $self->{logextension};
+
+ ## If we are writing each process to a separate file,
+ ## append the prefix and the PID to the file name
+ $fn .= "$self->{logprefix}.$$" if $self->{logseparate};
+
+ open my $fh, '>>', $fn or die qq{Could not append to "$fn": $!\n};
+ ## Turn off buffering on this handle
+ $fh->autoflush(1);
+
+ $code_for{$dest} = sub { print {$fh} @_, $/ };
+ }
+ }
+
+ return @{ $self->{logcodes} = [ values %code_for ] };
+}
+
sub glog { ## no critic (RequireArgUnpacking)
## Reformat and log internal messages to the correct place
my $self = shift;
my $msg = shift;
- ## Remove newline from the end of the message, in case it has one
- chomp $msg;
## Grab the log level: defaults to 0 (LOG_WARN)
my $loglevel = shift || 0;
## Return and do nothing, if we have not met the minimum log level
return if $loglevel > $config{log_level_number};
+ ## Just return if there is no place to log to.
+ my @logs = $self->_logto;
+ return unless @logs || ($loglevel == LOG_WARN && $self->{warning_file});
+
+ ## Remove newline from the end of the message, in case it has one
+ chomp $msg;
+
## We should always have a prefix, either BC!, MCP, CTL, KID, or VAC
## Prepend it to our message
my $prefix = $self->{logprefix} || '???';
$header = "$loglevel $header";
}
- ## If using syslog, send the message at the 'info' priority
- ## Using syslog does not rule out using other things as well
- $self->{debugsyslog} and syslog 'info', $msg;
-
## Warning messages may also get written to a separate file
## Note that a 'warning message' is simply anything starting with "Warning"
- if ($self->{warning_file} and index($msg, 'Warning') == 0) {
+ if ($self->{warning_file} and $loglevel == LOG_WARN) {
my $file = $self->{warning_file};
open my $fh, , '>>', $file or die qq{Could not append to "$file": $!\n};
print {$fh} "$header$msg\n";
close $fh or warn qq{Could not close "$file": $!\n};
}
- ## Possibly send the message to a debug file
- if ($self->{debugfile}) {
-
- ## If we've not set the name yet, do so now
- if (!exists $self->{debugfilename}) {
- $self->{debugfilename} = "$self->{debugdir}/log.bucardo";
- ## e.g. for when you don't want to overwrite an existing log.bucardo:
- if ($self->{debugname}) {
- $self->{debugfilename} .= ".$self->{debugname}";
- }
- }
- my $file = $self->{debugfilename};
-
- ## If we are writing each process to a separate file,
- ## append the prefix and the PID to the file name
- if ($self->{debugfilesep}) {
- $file = $self->{debugfilename} . ".$prefix.$$";
- }
-
- ## If this file has not been opened yet, do so now
- if (!exists $self->{debugfilehandle}{$$}{$file}) {
- open $self->{debugfilehandle}{$$}{$file}, '>>', $file
- or die qq{Could not append to "$file": $!\n};
- ## Turn off buffering on this handle
- $self->{debugfilehandle}{$$}{$file}->autoflush(1);
- }
-
- ## Write the header, the message, and a newline to the log file.
- printf {$self->{debugfilehandle}{$$}{$file}} "%s%s\n",
- $header,
- $msg;
- }
-
+ # Send it to all logs.
+ $_->($header, $msg) for @logs;
return;
} ## end of glog
dbpass => '',
sendmail => 0,
extraname => '',
- debugfilesep => 0,
- debugdir => '.',
- debugname => '',
- debugsyslog => 1,
- debugfile => 1,
- cleandebugs => 0,
+ logseparate => 0,
+ logextension => '',
+ logclean => 0,
batch => 0,
};
'dbpass|db-pass|P=s',
'sendmail=i',
'extraname|extra-name=s',
- 'debugfilesep',
- 'debugname=s',
- 'debugsyslog=i',
- 'debugdir=s',
- 'debugfile=i',
- 'cleandebugs=i',
+
+ 'debugsyslog=i', # legacy
+ 'debugdir=s', # legacy
+ 'debugfile=i', # legacy
+ 'cleandebugs=i', # legacy
+
+
+ 'logdest|log-dest|log-destination=s@', # stderr, syslog, or file path
+ 'logseparate|log-sep|log-separate|debugfilesep!',
+ 'logextension|log-extension|log-ext|debugname=s',
+ 'logclean|log-clean!',
## Used internally
'force',
exit 0;
}
+# Determine the logging destination.
+unless (exists $bcargs->{logdest}) {
+ if (exists $bcargs->{debugfile} && !delete $bcargs->{debugfile}) {
+ # Old --debugfile option can disable logging.
+ $bcargs->{logdest} = [];
+ } elsif (my $dir = $bcargs->{debugdir}) {
+ # Old --debugdir option determins log directory.
+ $bcargs->{logdest} = [$dir];
+ } else {
+ # Default value.
+ $bcargs->{logdest} = ['/var/log/bucardo'];
+ }
+
+ if ($bcargs->{debugsyslog}) {
+ # Old --debugsyslog option enables syslog logging.
+ push @{ $bcargs->{logdest} } => 'syslog';
+ }
+}
+
+# Handle legacy --cleandebugs option.
+$bcargs->{logclean} = 1
+ if delete $bcargs->{cleandebugs} && !exists $bcargs->{logclean};
+
## Sometimes we want to be as quiet as possible
my $QUIET = delete $bcargs->{quiet};
## We are the kid, do nothing
}
else {
- close STDERR or warn "Could not close STDERR\n";
- close STDOUT or warn "Could not close STDOUT\n";
setsid() or die;
## Here we go!
$bc->start_mcp( \@opts );
A short string that will be appended to the version string as output by the
Bucardo process names. Mostly useful for debugging.
-=item C<--debugfilesep>
+=item C<--log-destination destination>
-Forces creation of separate log files for each Bucardo process of the form
-"log.bucardo.X.Y", where X is the type of process (MCP, CTL, or KID), and Y is
-the process ID.
+Determines the destinotion for logging output. The supported values are:
+
+=over
+
+=item C<stderr>
-=item C<--debugsyslog>
+=item C<stdout>
-Sends all log messages to the syslog daemon. On by default. The facility used
-is controlled by the row "syslog_facility" in the bucardo_config table, and
-defaults to "LOG_LOCAL1".
+=item C<syslog>
-=item C<--debugfile>
+=item C<none>
-If set, writes detailed debugging information to one or more files.
+=item A file system directory.
-=item C<--debugdir directory name>
+=back
-Directory where the debug files should go.
+May be specified more than once, which is useful for, e.g., logging both to a
+directory and to syslog. If C<--log-destination> is not specified at all, the
+default is to log to files in F</var/log/bucardo>.
+
+=item C<--log-separate>
+
+Forces creation of separate log files for each Bucardo process of the form
+"log.bucardo.X.Y", where X is the type of process (MCP, CTL, or KID), and Y is
+the process ID.
-=item C<--debugname string>
+=item C<--log-extension string>
-Appends the given string to the end of the default debug file name,
-"log.bucardo". A dot is added before the name as well, so a debugname of
-"rootdb" would produce a log file named "log.bucardo.rootdb".
+Appends the given string to the end of the default log file name,
+F<log.bucardo>. A dot is added before the name as well, so a log extension of
+"rootdb" would produce a log file named F<log.bucardo.rootdb>.
-=item C<--cleandebugs>
+=item C<--log-clean>
-Forces removal of all old debug files before running.
+Forces removal of all old log files before running.
=item C<--debug>