Import file & syslog log plugins. File plugins include strftime(3) formatting
and unique session naming support based roughly on patch from pjh. git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.3x@609 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
parent
0f5d720359
commit
ffd453d012
8
Changes
8
Changes
@ -1,3 +1,11 @@
|
|||||||
|
0.32 -
|
||||||
|
|
||||||
|
Add logging/file plugin for simple logging to a file (Devin Carraway and
|
||||||
|
Peter J. Holzer).
|
||||||
|
|
||||||
|
Add logging/syslog plugin for logging via the syslog facility (Devin
|
||||||
|
Carrway)
|
||||||
|
|
||||||
0.31.1 - 2005/11/18
|
0.31.1 - 2005/11/18
|
||||||
|
|
||||||
Add missing files to the distribution, oops... (Thanks Budi Ang!)
|
Add missing files to the distribution, oops... (Thanks Budi Ang!)
|
||||||
|
267
plugins/logging/file
Normal file
267
plugins/logging/file
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
file - Simple log-to-file logging for qpsmtpd
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The 'file' logging plugin for qpsmtpd records qpsmtpd log messages into a
|
||||||
|
file (or a named pipe, if you prefer.)
|
||||||
|
|
||||||
|
=head1 CONFIGURATION
|
||||||
|
|
||||||
|
To enable the logging plugin, add a line of this form to the qpsmtpd plugins
|
||||||
|
configuration file:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
logging/file [loglevel I<level>] [reopen] [nosplit] I<path>
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
logging/file loglevel LOGINFO /var/log/qpsmtpd.log
|
||||||
|
logging/file /var/log/qpsmtpd.log.%Y-%m-%d
|
||||||
|
logging/file loglevel LOGCRIT reopen |/usr/local/sbin/page-sysadmin
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Multiple instances of the plugin can be configured by appending :I<N> for any
|
||||||
|
integer(s) I<N>, to log to multiple files simultaneously, e.g. to log critical
|
||||||
|
errors and normally verbose logs elsewhere.
|
||||||
|
|
||||||
|
The filename or command given can include strftime conversion specifiers,
|
||||||
|
which can be used to substitute time and date information into the logfile.
|
||||||
|
The file will be reopened whenever this output changes (for example, with a
|
||||||
|
format of qpsmtpd.log.%Y-%m-%d-%h, the log would be reopened once per hour).
|
||||||
|
|
||||||
|
The list of supported conversion specifiers depends on the strftime()
|
||||||
|
implementation of your C library. See strftime(3) for details. Additionally,
|
||||||
|
%i will be expanded to a (hopefully) unique session-id; if %i is used, a new
|
||||||
|
logfile will be started for each SMTP connection.
|
||||||
|
|
||||||
|
The following optional configuration setting can be supplied:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item nosplit
|
||||||
|
|
||||||
|
If specified, the output file or pipe will be reopened at once once per
|
||||||
|
connection, and only prior to the first log output. This prevents logs for
|
||||||
|
sessions that span log intervals being split across multiple logfiles.
|
||||||
|
Without this option, the log will be reopened only when its output filename
|
||||||
|
changes; if strftime specifiers are not used, the log will not be reopened
|
||||||
|
at all.
|
||||||
|
|
||||||
|
=item reopen
|
||||||
|
|
||||||
|
Forces the log output to be reopened once per connection, as soon as something
|
||||||
|
is available to be logged. This can be combined with a high log severity (see
|
||||||
|
I<loglevel> below) to facilitate SMTP service alarms with Nagios or a similar
|
||||||
|
monitoring agent.
|
||||||
|
|
||||||
|
=item loglevel I<loglevel>
|
||||||
|
|
||||||
|
The internal log level below which messages will be logged. The I<loglevel>
|
||||||
|
given should be chosen from the list below. Priorities count downward (for
|
||||||
|
example, if LOGWARN were selected, LOGERROR, LOGCRIT and LOGEMERG messages
|
||||||
|
would be logged as well).
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<LOGDEBUG>
|
||||||
|
|
||||||
|
=item B<LOGINFO>
|
||||||
|
|
||||||
|
=item B<LOGNOTICE>
|
||||||
|
|
||||||
|
=item B<LOGWARN>
|
||||||
|
|
||||||
|
=item B<LOGERROR>
|
||||||
|
|
||||||
|
=item B<LOGCRIT>
|
||||||
|
|
||||||
|
=item B<LOGALERT>
|
||||||
|
|
||||||
|
=item B<LOGEMERG>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
|
||||||
|
The chosen I<path> should be writable by the user running qpsmtpd; it will be
|
||||||
|
created it did not already exist, and appended to otherwise.
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Devin Carraway <qpsmtpd@devin.com>, with contributions by Peter J.
|
||||||
|
Holzer <hjp@hjp.at>.
|
||||||
|
|
||||||
|
=head1 LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2005-2006, Devin Carraway
|
||||||
|
Copyright (c) 2006, Peter J. Holzer.
|
||||||
|
|
||||||
|
This plugin is licensed under the same terms as the qpsmtpd package itself.
|
||||||
|
Please see the LICENSE file included with qpsmtpd for details.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use IO::File;
|
||||||
|
use Sys::Hostname;
|
||||||
|
use POSIX qw(strftime);
|
||||||
|
|
||||||
|
sub register {
|
||||||
|
my ($self, $qp, @args) = @_;
|
||||||
|
my %args;
|
||||||
|
|
||||||
|
$self->{_loglevel} = LOGWARN;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
last if !@args;
|
||||||
|
if (lc $args[0] eq 'loglevel') {
|
||||||
|
shift @args;
|
||||||
|
my $ll = shift @args;
|
||||||
|
if (!defined $ll) {
|
||||||
|
warn "Malformed arguments to logging/file plugin";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($ll =~ /^(\d+)$/) {
|
||||||
|
$self->{_loglevel} = $1;
|
||||||
|
}
|
||||||
|
elsif ($ll =~ /^(LOG\w+)$/) {
|
||||||
|
$self->{_loglevel} = log_level($1);
|
||||||
|
defined $self->{_loglevel} or $self->{_loglevel} = LOGWARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif (lc $args[0] eq 'nosplit') {
|
||||||
|
shift @args;
|
||||||
|
$self->{_nosplit} = 1;
|
||||||
|
}
|
||||||
|
elsif (lc $args[0] eq 'reopen') {
|
||||||
|
shift @args;
|
||||||
|
$self->{_reopen} = 1;
|
||||||
|
}
|
||||||
|
else { last }
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (@args && $args[0]) {
|
||||||
|
warn "Malformed arguments to syslog plugin";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $output = join(' ', @args);
|
||||||
|
|
||||||
|
if ($output =~ /^\s*\|(.*)/) {
|
||||||
|
$self->{_log_pipe} = 1;
|
||||||
|
$self->{_log_format} = $1;
|
||||||
|
} else {
|
||||||
|
$output =~ /^(.*)/; # detaint
|
||||||
|
$self->{_log_format} = $1;
|
||||||
|
}
|
||||||
|
$self->{_current_output} = '';
|
||||||
|
$self->{_session_counter} = 0;
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub log_output {
|
||||||
|
my ($self, $txn) = @_;
|
||||||
|
my $output = $self->{_log_format};
|
||||||
|
$output =~ s/%i/($txn->notes('logging-session-id') || 'parent')/ge;
|
||||||
|
$output = strftime $output, localtime;
|
||||||
|
$output;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_log {
|
||||||
|
my ($self,$output,$qp) = @_;
|
||||||
|
|
||||||
|
if ($self->{_log_pipe}) {
|
||||||
|
unless ($self->{_f} = new IO::File "|$output") {
|
||||||
|
warn "Error opening log output to command $output: $!";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unless ($self->{_f} = new IO::File ">>$output") {
|
||||||
|
warn "Error opening log output to path $output: $!";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$self->{_current_output} = $output;
|
||||||
|
$self->{_f}->autoflush(1);
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Reopen the output iff the interpolated output filename has changed
|
||||||
|
# from the one currently open, or if reopening was selected and we haven't
|
||||||
|
# yet done so during this session.
|
||||||
|
#
|
||||||
|
# Returns true if the file was reopened, zero if not, undef on error.
|
||||||
|
sub maybe_reopen {
|
||||||
|
my ($self, $txn) = @_;
|
||||||
|
|
||||||
|
my $new_output = $self->log_output($txn);
|
||||||
|
if (!$self->{_current_output} ||
|
||||||
|
$self->{_current_output} ne $new_output ||
|
||||||
|
($self->{_reopen} &&
|
||||||
|
!$txn->notes('file-reopened-this-session'))) {
|
||||||
|
unless ($self->open_log($new_output, $txn)) {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
$txn->notes('file-reopened-this-session', 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hook_connect {
|
||||||
|
my ($self, $txn) = @_;
|
||||||
|
|
||||||
|
$txn->notes('file-logged-this-session', 0);
|
||||||
|
$txn->notes('file-reopened-this-session', 0);
|
||||||
|
$txn->notes('logging-session-id',
|
||||||
|
sprintf("%08d-%04d-%d",
|
||||||
|
scalar time, $$, ++$self->{_session_counter}));
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hook_disconnect {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
if ($self->{reopen_} && $self->{_f}) {
|
||||||
|
$self->{_f} = undef;
|
||||||
|
}
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hook_logging {
|
||||||
|
my ($self, $txn, $trace, $hook, $plugin, @log) = @_;
|
||||||
|
|
||||||
|
return DECLINED if !defined $self->{_loglevel} or
|
||||||
|
$trace > $self->{_loglevel};
|
||||||
|
return DECLINED if defined $plugin and $plugin eq $self->plugin_name;
|
||||||
|
|
||||||
|
# Possibly reopen the log iff:
|
||||||
|
# - It's not already open
|
||||||
|
# - We're allowed to split sessions across logfiles
|
||||||
|
# - We haven't logged anything yet this session
|
||||||
|
if (!$self->{_f} ||
|
||||||
|
!$self->{_nosplit} ||
|
||||||
|
!$txn->notes('file-logged-this-session')) {
|
||||||
|
unless (defined $self->maybe_reopen($txn)) {
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
$txn->notes('file-logged-this-session', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $f = $self->{_f};
|
||||||
|
print $f scalar localtime, ' ', hostname(), '[', $$, ']: ', @log, "\n";
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: tabstop=4 shiftwidth=4 expandtab:
|
166
plugins/logging/syslog
Normal file
166
plugins/logging/syslog
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
syslog - Syslog logging plugin for qpsmtpd
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The syslog plugin for qpsmtpd passes qpsmtpd log messages into the standard
|
||||||
|
UNIX syslog facility, mapping qpsmtpd priorities to syslog priorities.
|
||||||
|
|
||||||
|
=head1 CONFIGURATION
|
||||||
|
|
||||||
|
To enable the logging plugin, add a line of this form to the qpsmtpd plugins
|
||||||
|
configuration file:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
logging/syslog [loglevel l] [priority p] [ident str] [facility f]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
logging/syslog loglevel LOGINFO priority LOG_NOTICE
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The following optional configuration settings can be supplied:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<loglevel>
|
||||||
|
|
||||||
|
The internal log level below which messages will be logged. Priorities count
|
||||||
|
downward as follows:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<LOGDEBUG>
|
||||||
|
|
||||||
|
=item B<LOGINFO>
|
||||||
|
|
||||||
|
=item B<LOGNOTICE>
|
||||||
|
|
||||||
|
=item B<LOGWARN>
|
||||||
|
|
||||||
|
=item B<LOGERROR>
|
||||||
|
|
||||||
|
=item B<LOGCRIT>
|
||||||
|
|
||||||
|
=item B<LOGALERT>
|
||||||
|
|
||||||
|
=item B<LOGEMERG>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
|
||||||
|
=item B<priority>
|
||||||
|
|
||||||
|
Normally, log messages will be mapped from the above log levels into the
|
||||||
|
syslog(3) log levels of their corresponding names. This will cause various
|
||||||
|
messages to appear or not in syslog outputs according to your syslogd
|
||||||
|
configuration (typically /etc/syslog.conf). However, if the B<priority>
|
||||||
|
setting is used, all messages will be logged at that priority regardless of
|
||||||
|
what the original priority might have been.
|
||||||
|
|
||||||
|
=item B<ident>
|
||||||
|
|
||||||
|
The ident string that will be attached to messages logged via this plugin.
|
||||||
|
The default is 'qpsmtpd'.
|
||||||
|
|
||||||
|
=item B<facility>
|
||||||
|
|
||||||
|
The syslog facility to which logged mesages will be directed. See syslog(3)
|
||||||
|
for details. The default is LOG_MAIL.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Devin Carraway <qpsmtpd@devin.com>
|
||||||
|
|
||||||
|
=head1 LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2005, Devin Carraway.
|
||||||
|
|
||||||
|
This plugin is licensed under the same terms as the qpsmtpd package itself.
|
||||||
|
Please see the LICENSE file included with qpsmtpd for details.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Sys::Syslog;
|
||||||
|
|
||||||
|
sub register {
|
||||||
|
my ($self, $qp, @args) = @_;
|
||||||
|
my %args;
|
||||||
|
|
||||||
|
if (@args % 2 == 0) {
|
||||||
|
%args = @args;
|
||||||
|
} else {
|
||||||
|
warn "Malformed arguments to syslog plugin";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $ident = 'qpsmtpd';
|
||||||
|
my $logopt = 'pid';
|
||||||
|
my $facility = 'LOG_MAIL';
|
||||||
|
|
||||||
|
$self->{_loglevel} = LOGWARN;
|
||||||
|
|
||||||
|
if ($args{loglevel}) {
|
||||||
|
if ($args{loglevel} =~ /^(\d+)$/) {
|
||||||
|
$self->{_loglevel} = $1;
|
||||||
|
}
|
||||||
|
elsif ($args{loglevel} =~ /^(LOG\w+)$/) {
|
||||||
|
$self->{_loglevel} = log_level($1) || LOGWARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args{priority}) {
|
||||||
|
if ($args{priority} =~ /^(\d+|LOG\w+)$/) {
|
||||||
|
$self->{_priority} = $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args{ident} && $args{ident} =~ /^([\w\-.]+)$/) {
|
||||||
|
$ident = $1;
|
||||||
|
}
|
||||||
|
if ($args{facility} && $args{facility} =~ /^(\w+)$/) {
|
||||||
|
$facility = $1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unless (openlog $ident, $logopt, $facility) {
|
||||||
|
warn "Error opening syslog output";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my %priorities_ = (
|
||||||
|
0 => 'LOG_EMERG',
|
||||||
|
1 => 'LOG_ALERT',
|
||||||
|
2 => 'LOG_CRIT',
|
||||||
|
3 => 'LOG_ERR',
|
||||||
|
4 => 'LOG_WARNING',
|
||||||
|
5 => 'LOG_NOTICE',
|
||||||
|
6 => 'LOG_INFO',
|
||||||
|
7 => 'LOG_DEBUG',
|
||||||
|
);
|
||||||
|
|
||||||
|
sub hook_logging {
|
||||||
|
my ($self, $txn, $trace, $hook, $plugin, @log) = @_;
|
||||||
|
|
||||||
|
return DECLINED if $trace > $self->{_loglevel};
|
||||||
|
return DECLINED if defined $plugin and $plugin eq $self->plugin_name;
|
||||||
|
|
||||||
|
my $priority = $self->{_priority} ?
|
||||||
|
$self->{_priority} : $priorities_{$trace};
|
||||||
|
|
||||||
|
syslog $priority, '%s', join(' ', @log);
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: tabstop=4 shiftwidth=4 expandtab
|
Loading…
Reference in New Issue
Block a user