Replace --debug options with --log options.
authorDavid E. Wheeler <david@justatheory.com>
Thu, 1 Nov 2012 20:37:18 +0000 (13:37 -0700)
committerDavid E. Wheeler <david@justatheory.com>
Thu, 1 Nov 2012 20:37:18 +0000 (13:37 -0700)
`--debugdir`, `--debugsyslog`, and `--debugfile` have all been folded into
`--debug-destination`. This option may be specified multiple times, with the
values "syslog", "stderr", "stdout", "none", or a directory name. This means
you can log to a directory, syslog, and STDERR all at the same time if you
like. The old options are still around in name, but their values are used only
if `--log-destination` is not, and then they are adapted to it.

Implementation-wise, there is now a new method, _logto(), that returns a list
of code references, one for each destination. glog() simply passes the log
header and message to each code reference in turn. _logto() also notices when
the process has been forked and creates new code refernces as appropriate.
This is especially important when --log-separate is specified. If
--log-destination is passed "none", there will be no logging at all (even if
there are other calls to log-destination).

Relatedly:
* `--debugfilesep` has been renamed to `--log-separate`.
* `--debugname` has been renamed to `--log-extension`.
* `--cleandebugs` has been renamed to `log-clean`.
* If there are "stderr" or "stdout" destinations, the corrsponding file
  handles are no longer closed once the MCP forks.
* Logging to the warning file no longer happens when the string "Warning"
  appears in the log message, but when th elog leve is LOG_WARN.

Bucardo.pm
bucardo
t/BucardoTesting.pm

index 209354e25c630439a34683e44980e9b5086e3e74..cea8340b04eff7db00e74b0683b3f5f8062c7c63 100644 (file)
@@ -191,13 +191,11 @@ sub new {
         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    => '',
@@ -217,16 +215,19 @@ sub new {
     ## 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};
         }
     }
 
@@ -254,11 +255,6 @@ sub new {
     ## 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} || '';
 
@@ -378,6 +374,14 @@ sub start_mcp {
     $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}
@@ -4834,6 +4838,49 @@ sub log_config {
 } ## 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
@@ -4847,8 +4894,6 @@ sub glog { ## no critic (RequireArgUnpacking)
 
     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;
@@ -4856,6 +4901,13 @@ sub glog { ## no critic (RequireArgUnpacking)
     ## 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} || '???';
@@ -4889,52 +4941,17 @@ sub glog { ## no critic (RequireArgUnpacking)
         $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
diff --git a/bucardo b/bucardo
index 3f110b99c8dcc62dd6678488bbb2e0398e2edc02..f299aa305bbebd050f014d541bd6281b53f8fde8 100755 (executable)
--- a/bucardo
+++ b/bucardo
@@ -69,12 +69,9 @@ my $bcargs = {
               dbpass       => '',
               sendmail     => 0,
               extraname    => '',
-              debugfilesep => 0,
-              debugdir     => '.',
-              debugname    => '',
-              debugsyslog  => 1,
-              debugfile    => 1,
-              cleandebugs  => 0,
+              logseparate  => 0,
+              logextension => '',
+              logclean     => 0,
               batch        => 0,
           };
 
@@ -151,12 +148,17 @@ GetOptions ## no critic (ProhibitCallsToUndeclaredSubs)
      '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',
@@ -178,6 +180,29 @@ if ($bcargs->{version}) {
     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};
 
@@ -754,8 +779,6 @@ sub start {
         ## 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 );
@@ -8816,35 +8839,43 @@ shutdown, and errors. Default is on.
 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>
 
index 95d18b069b57553c0778003066c13cf88ef53aa3..534b87cc481f8b6461fba3e2a8c9f45c26b71cc1 100644 (file)
@@ -900,7 +900,7 @@ sub ctl {
         next unless exists $bc->{$val} and length $bc->{$val};
         $connopts .= " --db$arg=$bc->{$val}";
     }
-    $connopts .= " --dbname=bucardo --debugfile=1";
+    $connopts .= " --dbname=bucardo --log-dest .";
     $connopts .= " --dbuser=$user";
     ## Just hard-code these, no sense in multiple Bucardo base dbs yet:
     $connopts .= " --dbport=58921";