diff options
| author | bwarsaw | 2006-04-17 04:08:17 +0000 |
|---|---|---|
| committer | bwarsaw | 2006-04-17 04:08:17 +0000 |
| commit | 0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0 (patch) | |
| tree | 7b710a785331abfe28b5b46a7695e6cbd81b7794 /Mailman | |
| parent | 9934c9b2b0e76a0b77b7869ecf68cd960d4d5bd7 (diff) | |
| download | mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.tar.gz mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.tar.zst mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.zip | |
- Convert all logging to Python's standard logging module. Get rid of all
traces of our crufty old Syslog. Most of this work was purely mechanical,
except for:
1) Initializing the loggers. For this, there's a new module
Mailman/loginit.py (yes all modules from now on will use PEP 8
names). We can't call this 'logging.py' because that will
interfere with importing the stdlib module of the same name (can
you say Python 2.5 and absolute imports?).
If you want to write log messages both to the log file and to
stderr, pass True to loginit.initialize(). This will turn on
propagation of log messages to the parent 'mailman' logger, which
is set up to print to stderr. This is how bin/qrunner works when
not running as a subprocess of mailmanctl.
2) The driver script. I had to untwist the StampedLogger stuff and
implement differently printing exceptions and such to log/error
because standard logging objects don't have a write() method. So
we write to a cStringIO and then pass that to the logger.
3) SMTPDirect.py because of the configurability of the log messages.
This required changing SafeDict into a dict subclass (which is
better than using UserDicts anyway -- yay Python 2.3!). It's
probably still possible to flummox things up if you change the
name of the loggers in the SMTP_LOG_* variables in mm_cfg.py.
However, the worst you can do is cause output to go to stderr and
not go to a log file.
Note too that all entry points into the Mailman system must call
Mailman.loginit.initialize() or the log output will go to stderr
(which may occasionally be what you want). Currently all CGIs and
qrunners should be working properly.
I wish I could have tested all code paths that touch the logger, but
that's infeasible. I have tested this, but it's possible that there
were some mistakes in the translation.
- Mailman.Bouncers.BounceAPI.Stop is a singleton, but not a class
instance any more.
- True/False code cleanup, PEP 8 import restructuring, whitespace
normalization, and copyright year updates, as appropriate.
Diffstat (limited to 'Mailman')
58 files changed, 547 insertions, 911 deletions
diff --git a/Mailman/Archiver/Archiver.py b/Mailman/Archiver/Archiver.py index 0a680fb0f..0089ee4dc 100644 --- a/Mailman/Archiver/Archiver.py +++ b/Mailman/Archiver/Archiver.py @@ -25,6 +25,7 @@ archival. import os import re import errno +import logging import traceback from cStringIO import StringIO @@ -34,9 +35,10 @@ from Mailman import Utils from Mailman import mm_cfg from Mailman import Mailbox from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog from Mailman.SafeDict import SafeDict +log = logging.getLogger('mailman.error') + def makelink(old, new): @@ -165,7 +167,7 @@ class Archiver: mbox.AppendMessage(post) mbox.fp.close() except IOError, msg: - syslog('error', 'Archive file access failure:\n\t%s %s', afn, msg) + log.error('Archive file access failure:\n\t%s %s', afn, msg) raise def ExternalArchive(self, ar, txt): @@ -177,8 +179,8 @@ class Archiver: extarch.write(txt) status = extarch.close() if status: - syslog('error', 'external archiver non-zero exit status: %d\n', - (status & 0xff00) >> 8) + log.error('external archiver non-zero exit status: %d\n', + (status & 0xff00) >> 8) # # archiving in real time this is called from list.post(msg) diff --git a/Mailman/Archiver/HyperArch.py b/Mailman/Archiver/HyperArch.py index 3c5e7c1d6..e38d35e16 100644 --- a/Mailman/Archiver/HyperArch.py +++ b/Mailman/Archiver/HyperArch.py @@ -33,6 +33,7 @@ import time import errno import types import urllib +import logging import weakref import binascii @@ -48,10 +49,10 @@ from Mailman import LockFile from Mailman import MailList from Mailman.Archiver import HyperDatabase from Mailman.Archiver import pipermail -from Mailman.Logging.Syslog import syslog from Mailman.Mailbox import ArchiverMailbox from Mailman.SafeDict import SafeDict +log = logging.getLogger('mailman.error') # Set up i18n. Assume the current language has already been set in the caller. _ = i18n._ @@ -329,7 +330,7 @@ class Article(pipermail.Article): try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: - syslog('error', 'error opening list: %s\n%s', listname, e) + log.error('error opening list: %s\n%s', listname, e) return None else: self._listcache[listname] = mlist @@ -854,10 +855,9 @@ class HyperArchive(pipermail.T): # crashed during archiving. Save it, log an error, and move on. try: wf = open(wname) - syslog('error', - 'Archive working file %s present. ' - 'Check %s for possibly unarchived msgs', - wname, ename) + log.error('Archive working file %s present. ' + 'Check %s for possibly unarchived msgs', + wname, ename) omask = os.umask(007) try: ef = open(ename, 'a+') diff --git a/Mailman/Archiver/pipermail.py b/Mailman/Archiver/pipermail.py index ce23ec222..1483b5c97 100644 --- a/Mailman/Archiver/pipermail.py +++ b/Mailman/Archiver/pipermail.py @@ -4,6 +4,7 @@ import os import re import sys import time +import logging import mailbox import cPickle as pickle @@ -17,12 +18,13 @@ VERSION = __version__ CACHESIZE = 100 # Number of slots in the cache from Mailman import Errors -from Mailman.Logging.Syslog import syslog from Mailman.Mailbox import ArchiverMailbox from Mailman.i18n import _ SPACE = ' ' +log = logging.getLogger('mailman.error') + msgid_pat = re.compile(r'(<.*>)') @@ -558,8 +560,7 @@ class T: except Errors.DiscardMessage: continue except Exception: - syslog('error', 'uncaught archiver exception at filepos: %s', - pos) + log.error('uncaught archiver exception at filepos: %s', pos) raise if m is None: break diff --git a/Mailman/BDBMemberAdaptor.py b/Mailman/BDBMemberAdaptor.py index 75424aba2..4cd8eaf30 100644 --- a/Mailman/BDBMemberAdaptor.py +++ b/Mailman/BDBMemberAdaptor.py @@ -1,4 +1,4 @@ -# Copyright (C) 2003 by the Free Software Foundation, Inc. +# Copyright (C) 2003-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -45,7 +45,6 @@ from Mailman import Utils from Mailman import Errors from Mailman import MemberAdaptor from Mailman.MailList import MailList -from Mailman.Logging.Syslog import syslog STORAGE_VERSION = 'BA01' FMT = '>BHB' diff --git a/Mailman/Bouncer.py b/Mailman/Bouncer.py index ce647a1db..caa4e0bb1 100644 --- a/Mailman/Bouncer.py +++ b/Mailman/Bouncer.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,18 +19,18 @@ import sys import time -from types import StringType +import logging -from email.MIMEText import MIMEText from email.MIMEMessage import MIMEMessage +from email.MIMEText import MIMEText +from types import StringType -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import Message from Mailman import MemberAdaptor +from Mailman import Message from Mailman import Pending -from Mailman.Logging.Syslog import syslog +from Mailman import Utils from Mailman import i18n +from Mailman import mm_cfg EMPTYSTRING = '' @@ -49,6 +49,9 @@ REASONS = {MemberAdaptor.BYBOUNCE: _('due to excessive bounces'), _ = i18n._ +log = logging.getLogger('mailman.bounce') +slog = logging.getLogger('mailman.subscribe') + class _BounceInfo: @@ -114,21 +117,21 @@ class Bouncer: info = _BounceInfo(member, weight, day, self.bounce_you_are_disabled_warnings) self.setBounceInfo(member, info) - syslog('bounce', '%s: %s bounce score: %s', self.internal_name(), - member, info.score) + log.info('%s: %s bounce score: %s', self.internal_name(), + member, info.score) # Continue to the check phase below elif self.getDeliveryStatus(member) <> MemberAdaptor.ENABLED: # The user is already disabled, so we can just ignore subsequent # bounces. These are likely due to residual messages that were # sent before disabling the member, but took a while to bounce. - syslog('bounce', '%s: %s residual bounce received', - self.internal_name(), member) + log.info('%s: %s residual bounce received', + self.internal_name(), member) return elif info.date == day: # We've already scored any bounces for this day, so ignore it. - syslog('bounce', '%s: %s already scored a bounce for date %s', - self.internal_name(), member, - time.strftime('%d-%b-%Y', day + (0,0,0,0,1,0))) + log.info('%s: %s already scored a bounce for date %s', + self.internal_name(), member, + time.strftime('%d-%b-%Y', day + (0,0,0,0,1,0))) # Continue to check phase below else: # See if this member's bounce information is stale. @@ -137,25 +140,24 @@ class Bouncer: if lastbounce + self.bounce_info_stale_after < now: # Information is stale, so simply reset it info.reset(weight, day, self.bounce_you_are_disabled_warnings) - syslog('bounce', '%s: %s has stale bounce info, resetting', - self.internal_name(), member) + log.info('%s: %s has stale bounce info, resetting', + self.internal_name(), member) else: # Nope, the information isn't stale, so add to the bounce # score and take any necessary action. info.score += weight info.date = day - syslog('bounce', '%s: %s current bounce score: %s', - self.internal_name(), member, info.score) + log.info('%s: %s current bounce score: %s', + self.internal_name(), member, info.score) # Continue to the check phase below # # Now that we've adjusted the bounce score for this bounce, let's # check to see if the disable-by-bounce threshold has been reached. if info.score >= self.bounce_score_threshold: if mm_cfg.VERP_PROBES: - syslog('bounce', - 'sending %s list probe to: %s (score %s >= %s)', - self.internal_name(), member, info.score, - self.bounce_score_threshold) + log.info('sending %s list probe to: %s (score %s >= %s)', + self.internal_name(), member, info.score, + self.bounce_score_threshold) self.sendProbe(member, msg) info.reset(0, info.date, info.noticesleft) else: @@ -168,12 +170,12 @@ class Bouncer: info.cookie = cookie # Disable them if mm_cfg.VERP_PROBES: - syslog('bounce', '%s: %s disabling due to probe bounce received', - self.internal_name(), member) + log.info('%s: %s disabling due to probe bounce received', + self.internal_name(), member) else: - syslog('bounce', '%s: %s disabling due to bounce score %s >= %s', - self.internal_name(), member, - info.score, self.bounce_score_threshold) + log.info('%s: %s disabling due to bounce score %s >= %s', + self.internal_name(), member, + info.score, self.bounce_score_threshold) self.setDeliveryStatus(member, MemberAdaptor.BYBOUNCE) self.sendNextNotification(member) if self.bounce_notify_owner_on_disable: @@ -226,14 +228,14 @@ class Bouncer: # returned data. self.pend_confirm(info.cookie) if reason == MemberAdaptor.BYBOUNCE: - syslog('bounce', '%s: %s deleted after exhausting notices', - self.internal_name(), member) - syslog('subscribe', '%s: %s auto-unsubscribed [reason: %s]', - self.internal_name(), member, - {MemberAdaptor.BYBOUNCE: 'BYBOUNCE', - MemberAdaptor.BYUSER: 'BYUSER', - MemberAdaptor.BYADMIN: 'BYADMIN', - MemberAdaptor.UNKNOWN: 'UNKNOWN'}.get( + log.info('%s: %s deleted after exhausting notices', + self.internal_name(), member) + slog.info('%s: %s auto-unsubscribed [reason: %s]', + self.internal_name(), member, + {MemberAdaptor.BYBOUNCE: 'BYBOUNCE', + MemberAdaptor.BYUSER: 'BYUSER', + MemberAdaptor.BYADMIN: 'BYADMIN', + MemberAdaptor.UNKNOWN: 'UNKNOWN'}.get( reason, 'invalid value')) return # Send the next notification diff --git a/Mailman/Bouncers/BouncerAPI.py b/Mailman/Bouncers/BouncerAPI.py index e19c53f7e..2684af6b5 100644 --- a/Mailman/Bouncers/BouncerAPI.py +++ b/Mailman/Bouncers/BouncerAPI.py @@ -20,20 +20,15 @@ This module can also be used as the basis for a bounce detection testing framework. When run as a script, it expects two arguments, the listname and the filename containing the bounce message. - """ import sys -from Mailman.Logging.Syslog import syslog - # If a bounce detector returns Stop, that means to just discard the message. # An example is warning messages for temporary delivery problems. These # shouldn't trigger a bounce notification, but we also don't want to send them # on to the list administrator. -class _Stop: - pass -Stop = _Stop() +Stop = object() BOUNCE_PIPELINE = [ diff --git a/Mailman/Cgi/__init__.py b/Mailman/Cgi/__init__.py index f569e43f4..e69de29bb 100644 --- a/Mailman/Cgi/__init__.py +++ b/Mailman/Cgi/__init__.py @@ -1,15 +0,0 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py index c78a68ac1..74aba72fa 100644 --- a/Mailman/Cgi/admin.py +++ b/Mailman/Cgi/admin.py @@ -24,6 +24,7 @@ import sha import sys import signal import urllib +import logging from email.Utils import unquote, parseaddr, formataddr from string import lowercase, digits @@ -38,7 +39,6 @@ from Mailman import Utils from Mailman.Cgi import Auth from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog from Mailman.UserDesc import UserDesc # Set up i18n @@ -48,6 +48,8 @@ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) NL = '\n' OPTCOLUMNS = 11 +log = logging.getLogger('mailman.error') + def main(): @@ -65,8 +67,7 @@ def main(): # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) admin_overview(_('No such list <em>%(safelistname)s</em>')) - syslog('error', 'admin.py access for non-existent list: %s', - listname) + log.error('admin.py access for non-existent list: %s', listname) return # Now that we know what list has been requested, all subsequent admin # pages are shown in that list's preferred language. diff --git a/Mailman/Cgi/admindb.py b/Mailman/Cgi/admindb.py index 7f42c8acd..9add54e0c 100644 --- a/Mailman/Cgi/admindb.py +++ b/Mailman/Cgi/admindb.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,27 +17,28 @@ """Produce and process the pending-approval items for a list.""" -import sys import os import cgi +import sys +import time +import email import errno import signal -import email -import time + from types import ListType from urllib import quote_plus, unquote_plus -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import MailList from Mailman import Errors -from Mailman import Message from Mailman import i18n -from Mailman.Handlers.Moderate import ModeratedMemberPost -from Mailman.ListAdmin import readMessage +from Mailman import MailList +from Mailman import Message +from Mailman import mm_cfg +from Mailman import Utils + from Mailman.Cgi import Auth +from Mailman.Handlers.Moderate import ModeratedMemberPost from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog +from Mailman.ListAdmin import readMessage EMPTYSTRING = '' NL = '\n' @@ -50,6 +51,8 @@ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) EXCERPT_HEIGHT = 10 EXCERPT_WIDTH = 76 +log = logging.getLogger('mailman.error') + def helds_by_sender(mlist): @@ -88,7 +91,7 @@ def main(): # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) handle_no_list(_('No such list <em>%(safelistname)s</em>')) - syslog('error', 'No such list "%s": %s\n', listname, e) + log.error('No such list "%s": %s\n', listname, e) return # Now that we know which list to use, set the system's language to it. diff --git a/Mailman/Cgi/confirm.py b/Mailman/Cgi/confirm.py index c9f4135a1..352cb81ad 100644 --- a/Mailman/Cgi/confirm.py +++ b/Mailman/Cgi/confirm.py @@ -28,13 +28,14 @@ from Mailman import mm_cfg from Mailman import Pending from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog from Mailman.UserDesc import UserDesc # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') + def main(): @@ -57,7 +58,7 @@ def main(): bad_confirmation(doc, _('No such list <em>%(safelistname)s</em>')) doc.AddItem(MailmanLogo()) print doc.Format() - syslog('error', 'No such list "%s": %s', listname, e) + log.error('No such list "%s": %s', listname, e) return # Set the language for the list diff --git a/Mailman/Cgi/create.py b/Mailman/Cgi/create.py index 0667ac3fb..0c517c2cd 100644 --- a/Mailman/Cgi/create.py +++ b/Mailman/Cgi/create.py @@ -22,6 +22,7 @@ import cgi import sha import sys import signal +import logging from types import ListType @@ -32,12 +33,13 @@ from Mailman import Message from Mailman import mm_cfg from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') + def main(): @@ -52,7 +54,7 @@ def main(): doc.SetTitle(title) doc.AddItem( Header(3, Bold(FontAttr(title, color='#ff0000', size='+2')))) - syslog('error', 'Bad URL specification: %s', parts) + log.error('Bad URL specification: %s', parts) elif cgidata.has_key('doit'): # We must be processing the list creation request process_request(doc, cgidata) diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py index 9ec54ceff..b0275de75 100644 --- a/Mailman/Cgi/edithtml.py +++ b/Mailman/Cgi/edithtml.py @@ -18,21 +18,23 @@ """Script which implements admin editing of the list's html templates.""" import os +import re import cgi import errno -import re +import logging -from Mailman import Utils +from Mailman import Errors +from Mailman import i18n from Mailman import MailList +from Mailman import Utils +from Mailman.Cgi import Auth from Mailman.htmlformat import * from Mailman.HTMLFormatter import HTMLFormatter -from Mailman import Errors -from Mailman.Cgi import Auth -from Mailman.Logging.Syslog import syslog -from Mailman import i18n _ = i18n._ +log = logging.getLogger('mailman.error') + def main(): @@ -69,7 +71,7 @@ def main(): safelistname = Utils.websafe(listname) doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>'))) print doc.Format() - syslog('error', 'No such list "%s": %s', listname, e) + log.error('No such list "%s": %s', listname, e) return # Now that we have a valid list, set the language to its default diff --git a/Mailman/Cgi/listinfo.py b/Mailman/Cgi/listinfo.py index abbf570b9..fc96f5d29 100644 --- a/Mailman/Cgi/listinfo.py +++ b/Mailman/Cgi/listinfo.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,28 +12,30 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. -"""Produce listinfo page, primary web entry-point to mailing lists. -""" +"""Produce listinfo page, primary web entry-point to mailing lists.""" # No lock needed in this script, because we don't change data. import os import cgi +import logging -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import MailList from Mailman import Errors from Mailman import i18n +from Mailman import MailList +from Mailman import mm_cfg +from Mailman import Utils from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') + def main(): @@ -49,7 +51,7 @@ def main(): # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) listinfo_overview(_('No such list <em>%(safelistname)s</em>')) - syslog('error', 'No such list "%s": %s', listname, e) + log.error('No such list "%s": %s', listname, e) return # See if the user want to see this page in other language diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py index 98e1ae958..b2d63735f 100644 --- a/Mailman/Cgi/options.py +++ b/Mailman/Cgi/options.py @@ -22,6 +22,7 @@ import cgi import sys import signal import urllib +import logging from types import ListType @@ -33,7 +34,6 @@ from Mailman import mm_cfg from Mailman import Utils from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog SLASH = '/' SETLANGUAGE = -1 @@ -42,6 +42,9 @@ SETLANGUAGE = -1 _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') +mlog = logging.getLogger('mailman.mischief') + def main(): @@ -75,7 +78,7 @@ def main(): doc.AddItem('<hr>') doc.AddItem(MailmanLogo()) print doc.Format() - syslog('error', 'No such list "%s": %s\n', listname, e) + log.error('No such list "%s": %s\n', listname, e) return # The total contents of the user's response @@ -183,9 +186,8 @@ def main(): # Public rosters doc.addError(_('No such member: %(safeuser)s.')) else: - syslog('mischief', - 'Unsub attempt of non-member w/ private rosters: %s', - user) + mlog.error('Unsub attempt of non-member w/ private rosters: %s', + user) doc.addError(_('The confirmation email has been sent.'), tag='') loginpage(mlist, doc, user, language) @@ -205,9 +207,9 @@ def main(): # Public rosters doc.addError(_('No such member: %(safeuser)s.')) else: - syslog('mischief', - 'Reminder attempt of non-member w/ private rosters: %s', - user) + mlog.error( + 'Reminder attempt of non-member w/ private rosters: %s', + user) doc.addError( _('A reminder of your password has been emailed to you.'), tag='') @@ -242,9 +244,7 @@ def main(): # So as not to allow membership leakage, prompt for the email # address and the password here. if mlist.private_roster <> 0: - syslog('mischief', - 'Login failure with private rosters: %s', - user) + mlog.error('Login failure with private rosters: %s', user) user = None loginpage(mlist, doc, user, language) print doc.Format() diff --git a/Mailman/Cgi/private.py b/Mailman/Cgi/private.py index 866084187..e71f878fc 100644 --- a/Mailman/Cgi/private.py +++ b/Mailman/Cgi/private.py @@ -20,6 +20,7 @@ import os import sys import cgi +import logging import mimetypes from Mailman import mm_cfg @@ -28,7 +29,6 @@ from Mailman import MailList from Mailman import Errors from Mailman import i18n from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog # Set up i18n. Until we know which list is being requested, we use the # server's default. @@ -37,6 +37,9 @@ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) SLASH = '/' +log = logging.getLogger('mailman.error') +mlog = logging.getLogger('mailman.mischief') + def true_path(path): @@ -73,7 +76,7 @@ def main(): doc.SetTitle(msg) doc.AddItem(Header(2, msg)) print doc.Format() - syslog('mischief', 'Private archive hostile path: %s', path) + mlog.error('Private archive hostile path: %s', path) return # BAW: This needs to be converted to the Site module abstraction true_filename = os.path.join( @@ -109,7 +112,7 @@ def main(): doc.SetTitle(_("Private Archive Error - %(msg)s")) doc.AddItem(Header(2, msg)) print doc.Format() - syslog('error', 'No such list "%s": %s\n', listname, e) + log.error('No such list "%s": %s\n', listname, e) return i18n.set_language(mlist.preferred_language) @@ -180,7 +183,7 @@ def main(): doc.SetTitle(msg) doc.AddItem(Header(2, msg)) print doc.Format() - syslog('error', 'Private archive file not found: %s', true_filename) + log.error('Private archive file not found: %s', true_filename) else: print 'Content-type: %s\n' % ctype sys.stdout.write(f.read()) diff --git a/Mailman/Cgi/rmlist.py b/Mailman/Cgi/rmlist.py index 4628f5fd3..78a3e0a12 100644 --- a/Mailman/Cgi/rmlist.py +++ b/Mailman/Cgi/rmlist.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,7 +12,8 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Remove/delete mailing lists through the web.""" @@ -21,19 +22,22 @@ import cgi import sys import errno import shutil +import logging -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import MailList from Mailman import Errors from Mailman import i18n +from Mailman import MailList +from Mailman import mm_cfg +from Mailman import Utils from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') +mlog = logging.getLogger('mailman.mischief') + def main(): @@ -52,7 +56,7 @@ def main(): doc.AddItem('<hr>') doc.AddItem(MailmanLogo()) print doc.Format() - syslog('error', 'Bad URL specification: %s', parts) + log.error('Bad URL specification: %s', parts) return listname = parts[0].lower() @@ -69,7 +73,7 @@ def main(): doc.AddItem('<hr>') doc.AddItem(MailmanLogo()) print doc.Format() - syslog('error', 'No such list "%s": %s\n', listname, e) + log.error('No such list "%s": %s\n', listname, e) return # Now that we have a valid mailing list, set the language @@ -84,7 +88,7 @@ def main(): Header(3, Bold(FontAttr(title, color='#ff0000', size='+2')))) doc.AddItem(mlist.GetMailmanFooter()) print doc.Format() - syslog('mischief', 'Attempt to sneakily delete a list: %s', listname) + mlog.error('Attempt to sneakily delete a list: %s', listname) return if cgidata.has_key('doit'): @@ -144,18 +148,15 @@ def process_request(doc, cgidata, mlist): except OSError, e: if e.errno not in (errno.EACCES, errno.EPERM): raise problems += 1 - syslog('error', - 'link %s not deleted due to permission problems', - dir) + log.error('link %s not deleted due to permission problems', dir) elif os.path.isdir(dir): try: shutil.rmtree(dir) except OSError, e: if e.errno not in (errno.EACCES, errno.EPERM): raise problems += 1 - syslog('error', - 'directory %s not deleted due to permission problems', - dir) + log.error('directory %s not deleted due to permission problems', + dir) title = _('Mailing list deletion results') doc.SetTitle(title) diff --git a/Mailman/Cgi/roster.py b/Mailman/Cgi/roster.py index a67e51002..0ce590398 100644 --- a/Mailman/Cgi/roster.py +++ b/Mailman/Cgi/roster.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,23 +23,25 @@ Takes listname in PATH_INFO. # We don't need to lock in this script, because we're never going to change # data. -import sys import os import cgi +import sys import urllib +import logging -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import MailList from Mailman import Errors from Mailman import i18n +from Mailman import MailList +from Mailman import mm_cfg +from Mailman import Utils from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') + def main(): @@ -55,7 +57,7 @@ def main(): # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) error_page(_('No such list <em>%(safelistname)s</em>')) - syslog('error', 'roster: no such list "%s": %s', listname, e) + log.error('roster: no such list "%s": %s', listname, e) return cgidata = cgi.FieldStorage() diff --git a/Mailman/Cgi/subscribe.py b/Mailman/Cgi/subscribe.py index 3661dcde5..6aee0e6f8 100644 --- a/Mailman/Cgi/subscribe.py +++ b/Mailman/Cgi/subscribe.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,24 +12,25 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Process subscription or roster requests from listinfo form.""" -import sys import os import cgi +import sys import signal +import logging -from Mailman import mm_cfg -from Mailman import Utils -from Mailman import MailList from Mailman import Errors from Mailman import i18n +from Mailman import MailList from Mailman import Message -from Mailman.UserDesc import UserDesc +from Mailman import mm_cfg +from Mailman import Utils from Mailman.htmlformat import * -from Mailman.Logging.Syslog import syslog +from Mailman.UserDesc import UserDesc SLASH = '/' ERRORSEP = '\n\n<p>' @@ -38,6 +39,9 @@ ERRORSEP = '\n\n<p>' _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) +log = logging.getLogger('mailman.error') +mlog = logging.getLogger('mailman.mischief') + def main(): @@ -60,7 +64,7 @@ def main(): doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('No such list <em>%(safelistname)s</em>'))) print doc.Format() - syslog('error', 'No such list "%s": %s\n', listname, e) + log.error('No such list "%s": %s\n', listname, e) return # See if the form data has a preferred language set, in which case, use it @@ -119,7 +123,7 @@ def process_form(mlist, doc, cgidata, lang): 'unidentified origin')) # Was an attempt made to subscribe the list to itself? if email == mlist.GetListEmail(): - syslog('mischief', 'Attempt to self subscribe %s: %s', email, remote) + mlog.error('Attempt to self subscribe %s: %s', email, remote) results.append(_('You may not subscribe a list to itself!')) # If the user did not supply a password, generate one for him password = cgidata.getvalue('pw') diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in index be7a3c616..97e262ddc 100644 --- a/Mailman/Defaults.py.in +++ b/Mailman/Defaults.py.in @@ -495,7 +495,7 @@ OWNER_PIPELINE = [ ] -# This defines syslog() format strings for the SMTPDirect delivery module (see +# This defines log format strings for the SMTPDirect delivery module (see # DELIVERY_MODULE above). Valid %()s string substitutions include: # # time -- the time in float seconds that it took to complete the smtp @@ -524,10 +524,10 @@ OWNER_PIPELINE = [ # below. # # The format of the entries is a 2-tuple with the first element naming the -# file in logs/ to print the message to, and the second being a format string -# appropriate for Python's %-style string interpolation. The file name is -# arbitrary; qfiles/<name> will be created automatically if it does not -# exist. +# logger (as a child of the root 'mailman' logger) to print the message to, +# and the second being a format string appropriate for Python's %-style string +# interpolation. The file name is arbitrary; qfiles/<name> will be created +# automatically if it does not exist. # The format of the message printed for every delivered message, regardless of # whether the delivery was successful or not. Set to None to disable the diff --git a/Mailman/Deliverer.py b/Mailman/Deliverer.py index 63f98db73..e1007e135 100644 --- a/Mailman/Deliverer.py +++ b/Mailman/Deliverer.py @@ -18,20 +18,23 @@ """Mixin class with message delivery routines.""" -from email.MIMEText import MIMEText +import logging + from email.MIMEMessage import MIMEMessage +from email.MIMEText import MIMEText from Mailman import i18n from Mailman import Errors from Mailman import Message from Mailman import mm_cfg -from Mailman import Utils from Mailman import Pending - -from Mailman.Logging.Syslog import syslog +from Mailman import Utils _ = i18n._ +log = logging.getLogger('mailman.error') +mlog = logging.getLogger('mailman.mischief') + class Deliverer: @@ -93,8 +96,8 @@ your membership administrative address, %(addr)s.''')) if not self.getMemberPassword(user): # The user's password somehow got corrupted. Generate a new one # for him, after logging this bogosity. - syslog('error', 'User %s had a false password for list %s', - user, self.internal_name()) + log.error('User %s had a false password for list %s', + user, self.internal_name()) waslocked = self.Locked() if not waslocked: self.Lock() @@ -154,8 +157,8 @@ your membership administrative address, %(addr)s.''')) # list. We inform both list owners of the bogosity, but be careful # not to reveal too much information. selfname = self.internal_name() - syslog('mischief', '%s was invited to %s but confirmed to %s', - address, listname, selfname) + mlog.error('%s was invited to %s but confirmed to %s', + address, listname, selfname) # First send a notice to the attacked list msg = Message.OwnerNotification( self, diff --git a/Mailman/Gui/Language.py b/Mailman/Gui/Language.py index e9400fca3..65fe4c4a6 100644 --- a/Mailman/Gui/Language.py +++ b/Mailman/Gui/Language.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,17 +12,16 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. -"""MailList mixin class managing the language options. -""" +"""MailList mixin class managing the language options.""" import codecs +from Mailman import i18n from Mailman import mm_cfg from Mailman import Utils -from Mailman import i18n -from Mailman.Logging.Syslog import syslog from Mailman.Gui.GUIBase import GUIBase _ = i18n._ diff --git a/Mailman/Gui/Topics.py b/Mailman/Gui/Topics.py index 5e250b245..282930b7c 100644 --- a/Mailman/Gui/Topics.py +++ b/Mailman/Gui/Topics.py @@ -20,7 +20,6 @@ import re from Mailman import mm_cfg from Mailman import Utils from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog from Mailman.Gui.GUIBase import GUIBase diff --git a/Mailman/Handlers/CalcRecips.py b/Mailman/Handlers/CalcRecips.py index e065ad686..e5acd23fb 100644 --- a/Mailman/Handlers/CalcRecips.py +++ b/Mailman/Handlers/CalcRecips.py @@ -1,18 +1,19 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Calculate the regular (i.e. non-digest) recipients of the message. @@ -22,13 +23,12 @@ on the `recips' attribute of the message. This attribute is used by the SendmailDeliver and BulkDeliver modules. """ +from Mailman import Errors +from Mailman import Message from Mailman import mm_cfg from Mailman import Utils -from Mailman import Message -from Mailman import Errors -from Mailman.MemberAdaptor import ENABLED from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog +from Mailman.MemberAdaptor import ENABLED @@ -130,4 +130,3 @@ def do_topic_filters(mlist, msg, msgdata, recips): # Prune out the non-receiving users for user in zaprecips: recips.remove(user) - diff --git a/Mailman/Handlers/Cleanse.py b/Mailman/Handlers/Cleanse.py index 8033d41f3..14c0e583c 100644 --- a/Mailman/Handlers/Cleanse.py +++ b/Mailman/Handlers/Cleanse.py @@ -17,12 +17,16 @@ """Cleanse certain headers from all messages.""" +import logging + from email.Utils import formataddr -from Mailman.Logging.Syslog import syslog from Mailman.Handlers.CookHeaders import uheader +log = logging.getLogger('mailman.post') + + def process(mlist, msg, msgdata): # Always remove this header from any outgoing messages. Be sure to do # this after the information on the header is actually used, but before a @@ -34,8 +38,8 @@ def process(mlist, msg, msgdata): del msg['urgent'] # We remove other headers from anonymous lists if mlist.anonymous_list: - syslog('post', 'post to %s from %s anonymized', - mlist.internal_name(), msg.get('from')) + log.info('post to %s from %s anonymized', + mlist.internal_name(), msg.get('from')) del msg['from'] del msg['reply-to'] del msg['sender'] diff --git a/Mailman/Handlers/CookHeaders.py b/Mailman/Handlers/CookHeaders.py index d85b9b877..458f9d362 100644 --- a/Mailman/Handlers/CookHeaders.py +++ b/Mailman/Handlers/CookHeaders.py @@ -28,7 +28,6 @@ from email.Utils import parseaddr, formataddr, getaddresses from Mailman import mm_cfg from Mailman import Utils from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog CONTINUATION = ',\n\t' COMMASPACE = ', ' diff --git a/Mailman/Handlers/Decorate.py b/Mailman/Handlers/Decorate.py index 09b6d6c1d..770114a49 100644 --- a/Mailman/Handlers/Decorate.py +++ b/Mailman/Handlers/Decorate.py @@ -17,22 +17,19 @@ """Decorate a message by sticking the header and footer around it.""" -from types import ListType +import logging + from email.MIMEText import MIMEText +from types import ListType +from Mailman import Errors from Mailman import mm_cfg from Mailman import Utils -from Mailman import Errors -from Mailman.Message import Message from Mailman.i18n import _ +from Mailman.Message import Message from Mailman.SafeDict import SafeDict -from Mailman.Logging.Syslog import syslog -try: - True, False -except: - True = 1 - False = 0 +log = logging.getLogger('mailman.error') @@ -213,7 +210,7 @@ def decorate(mlist, template, what, extradict={}): try: text = (template % d).replace('\r\n', '\n') except (ValueError, TypeError), e: - syslog('error', 'Exception while calculating %s:\n%s', what, e) + log.error('Exception while calculating %s:\n%s', what, e) what = what.upper() text = template return text diff --git a/Mailman/Handlers/Hold.py b/Mailman/Handlers/Hold.py index fdfaa09d2..a044b67ee 100644 --- a/Mailman/Handlers/Hold.py +++ b/Mailman/Handlers/Hold.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,7 +12,8 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Determine whether this message should be held for approval. @@ -28,21 +29,24 @@ message handling should stop. """ import email -from email.MIMEText import MIMEText -from email.MIMEMessage import MIMEMessage +import logging import email.Utils + +from email.MIMEMessage import MIMEMessage +from email.MIMEText import MIMEText from types import ClassType -from Mailman import mm_cfg -from Mailman import Utils from Mailman import Errors -from Mailman import Message from Mailman import i18n +from Mailman import mm_cfg +from Mailman import Message from Mailman import Pending -from Mailman.Logging.Syslog import syslog +from Mailman import Utils + +log = logging.getLogger('mailman.vette') -# First, play footsie with _ so that the following are marked as translated, -# but aren't actually translated until we need the text later on. +# Play footsie with _ so that the following are marked as translated, but +# aren't actually translated until we need the text later on. def _(s): return s @@ -284,8 +288,8 @@ also appear in the first line of the body of the reply.""")), finally: i18n.set_translation(otranslation) # Log the held message - syslog('vette', '%s post from %s held, message-id=%s: %s', - listname, sender, message_id, reason) + log.info('%s post from %s held, message-id=%s: %s', + listname, sender, message_id, reason) # raise the specific MessageHeld exception to exit out of the message # delivery pipeline raise exc diff --git a/Mailman/Handlers/MimeDel.py b/Mailman/Handlers/MimeDel.py index 906b12c3d..5533f54e3 100644 --- a/Mailman/Handlers/MimeDel.py +++ b/Mailman/Handlers/MimeDel.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2005 by the Free Software Foundation, Inc. +# Copyright (C) 2002-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,19 +26,21 @@ contents. import os import errno +import logging import tempfile -from os.path import splitext from email.Iterators import typed_subpart_iterator +from os.path import splitext -from Mailman import mm_cfg from Mailman import Errors +from Mailman import mm_cfg +from Mailman.i18n import _ from Mailman.Message import UserNotification from Mailman.Queue.sbcache import get_switchboard -from Mailman.Logging.Syslog import syslog -from Mailman.Version import VERSION -from Mailman.i18n import _ from Mailman.Utils import oneline +from Mailman.Version import VERSION + +log = logging.getLogger('mailman.error') @@ -204,7 +206,7 @@ def to_plaintext(msg): plaintext = cmd.read() rtn = cmd.close() if rtn: - syslog('error', 'HTML->text/plain error: %s', rtn) + log.error('HTML->text/plain error: %s', rtn) finally: try: os.unlink(filename) diff --git a/Mailman/Handlers/Moderate.py b/Mailman/Handlers/Moderate.py index 97d998b24..b96bd84e8 100644 --- a/Mailman/Handlers/Moderate.py +++ b/Mailman/Handlers/Moderate.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2003 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,22 +12,22 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. -"""Posting moderation filter. -""" +"""Posting moderation filter.""" import re + from email.MIMEMessage import MIMEMessage from email.MIMEText import MIMEText +from Mailman import Errors +from Mailman import Message from Mailman import mm_cfg from Mailman import Utils -from Mailman import Message -from Mailman import Errors from Mailman.i18n import _ from Mailman.Handlers import Hold -from Mailman.Logging.Syslog import syslog diff --git a/Mailman/Handlers/Replybot.py b/Mailman/Handlers/Replybot.py index 6cd74ca88..43cf75402 100644 --- a/Mailman/Handlers/Replybot.py +++ b/Mailman/Handlers/Replybot.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,18 +12,20 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. -"""Handler for auto-responses. -""" +"""Handler for auto-responses.""" import time +import logging -from Mailman import Utils from Mailman import Message +from Mailman import Utils from Mailman.i18n import _ from Mailman.SafeDict import SafeDict -from Mailman.Logging.Syslog import syslog + +log = logging.getLogger('mailman.error') @@ -97,8 +99,8 @@ def process(mlist, msg, msgdata): try: text = rtext % d except Exception: - syslog('error', 'Bad autoreply text for list: %s\n%s', - mlist.internal_name(), rtext) + log.error('Bad autoreply text for list: %s\n%s', + mlist.internal_name(), rtext) text = rtext # Wrap the response. text = Utils.wrap(text) diff --git a/Mailman/Handlers/SMTPDirect.py b/Mailman/Handlers/SMTPDirect.py index 702e78312..c68d269bf 100644 --- a/Mailman/Handlers/SMTPDirect.py +++ b/Mailman/Handlers/SMTPDirect.py @@ -30,22 +30,29 @@ import copy import time import email import socket +import logging import smtplib -from email.Utils import formataddr -from email.Header import Header from email.Charset import Charset +from email.Header import Header +from email.Utils import formataddr from types import UnicodeType from Mailman import Errors from Mailman import mm_cfg from Mailman import Utils from Mailman.Handlers import Decorate -from Mailman.Logging.Syslog import syslog from Mailman.SafeDict import MsgSafeDict DOT = '.' +log = logging.getLogger('mailman.smtp') +flog = logging.getLogger('mailman.smtp-failure') +every_log = logging.getLogger('mailman.' + mm_cfg.SMTP_LOG_EVERY_MESSAGE[0]) +success_log = logging.getLogger('mailman.' + mm_cfg.SMTP_LOG_SUCCESS[0]) +refused_log = logging.getLogger('mailman.' + mm_cfg.SMTP_LOG_REFUSED[0]) +failure_log = logging.getLogger('mailman.' + mm_cfg.SMTP_LOG_EACH_FAILURE[0]) + # Manage a connection to the SMTP server @@ -173,17 +180,14 @@ def process(mlist, msg, msgdata): 'sender' : origsender, }) # We have to use the copy() method because extended call syntax requires a - # concrete dictionary object; it does not allow a generic mapping. It's - # still worthwhile doing the interpolation in syslog() because it'll catch - # any catastrophic exceptions due to bogus format strings. + # concrete dictionary object; it does not allow a generic mapping (XXX is + # this still true in Python 2.3?). if mm_cfg.SMTP_LOG_EVERY_MESSAGE: - syslog.write_ex(mm_cfg.SMTP_LOG_EVERY_MESSAGE[0], - mm_cfg.SMTP_LOG_EVERY_MESSAGE[1], kws=d) + every_log.info('%s', mm_cfg.SMTP_LOG_EVERY_MESSAGE[1] % d) if refused: if mm_cfg.SMTP_LOG_REFUSED: - syslog.write_ex(mm_cfg.SMTP_LOG_REFUSED[0], - mm_cfg.SMTP_LOG_REFUSED[1], kws=d) + refused_log.info('%s', mm_cfg.SMTP_LOG_REFUSED[1] % d) elif msgdata.get('tolist'): # Log the successful post, but only if it really was a post to the @@ -192,8 +196,7 @@ def process(mlist, msg, msgdata): # the other messages, but in that case, we should probably have a # separate configuration variable to control that. if mm_cfg.SMTP_LOG_SUCCESS: - syslog.write_ex(mm_cfg.SMTP_LOG_SUCCESS[0], - mm_cfg.SMTP_LOG_SUCCESS[1], kws=d) + success_log.info('%s', mm_cfg.SMTP_LOG_SUCCESS[1] % d) # Process any failed deliveries. tempfailures = [] @@ -219,8 +222,7 @@ def process(mlist, msg, msgdata): d.update({'recipient': recip, 'failcode' : code, 'failmsg' : smtpmsg}) - syslog.write_ex(mm_cfg.SMTP_LOG_EACH_FAILURE[0], - mm_cfg.SMTP_LOG_EACH_FAILURE[1], kws=d) + failure_log.info('%s', mm_cfg.SMTP_LOG_EACH_FAILURE[1] % d) # Return the results if tempfailures or permfailures: raise Errors.SomeRecipientsFailed(tempfailures, permfailures) @@ -293,8 +295,7 @@ def verpdeliver(mlist, msg, msgdata, envsender, failures, conn): # deliver it to this person, nor can we craft a valid verp # header. I don't think there's much we can do except ignore # this recipient. - syslog('smtp', 'Skipping VERP delivery to unqual recip: %s', - recip) + log.info('Skipping VERP delivery to unqual recip: %s', recip) continue d = {'bounces': bmailbox, 'mailbox': rmailbox, @@ -362,12 +363,11 @@ def bulkdeliver(mlist, msg, msgdata, envsender, failures, conn): # Send the message refused = conn.sendmail(envsender, recips, msgtext) except smtplib.SMTPRecipientsRefused, e: - syslog('smtp-failure', 'All recipients refused: %s, msgid: %s', - e, msgid) + flog.error('All recipients refused: %s, msgid: %s', e, msgid) refused = e.recipients except smtplib.SMTPResponseException, e: - syslog('smtp-failure', 'SMTP session failure: %s, %s, msgid: %s', - e.smtp_code, e.smtp_error, msgid) + flog.error('SMTP session failure: %s, %s, msgid: %s', + e.smtp_code, e.smtp_error, msgid) # If this was a permanent failure, don't add the recipients to the # refused, because we don't want them to be added to failures. # Otherwise, if the MTA rejects the message because of the message @@ -382,7 +382,7 @@ def bulkdeliver(mlist, msg, msgdata, envsender, failures, conn): # MTA not responding, or other socket problems, or any other kind of # SMTPException. In that case, nothing got delivered, so treat this # as a temporary failure. - syslog('smtp-failure', 'Low level smtp error: %s, msgid: %s', e, msgid) + flog.error('Low level smtp error: %s, msgid: %s', e, msgid) error = str(e) for r in recips: refused[r] = (-1, error) diff --git a/Mailman/Handlers/Scrubber.py b/Mailman/Handlers/Scrubber.py index 606338e95..a96bb0b39 100644 --- a/Mailman/Handlers/Scrubber.py +++ b/Mailman/Handlers/Scrubber.py @@ -22,6 +22,7 @@ import re import sha import time import errno +import logging import binascii import tempfile @@ -40,7 +41,6 @@ from Mailman import LockFile from Mailman import Utils from Mailman.Errors import DiscardMessage from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog # Path characters for common platforms pre = re.compile(r'[/\\:]') @@ -53,6 +53,8 @@ dre = re.compile(r'^\.*') BR = '<br>\n' SPACE = ' ' +log = logging.getLogger('mailman.error') + def guess_extension(ctype, ext): @@ -464,9 +466,8 @@ def save_attachment(mlist, msg, dir, filter_html=True): decodedpayload = progfp.read() status = progfp.close() if status: - syslog('error', - 'HTML sanitizer exited with non-zero status: %s', - status) + log.error('HTML sanitizer exited with non-zero status: %s', + status) finally: os.unlink(tmppath) # BAW: Since we've now sanitized the document, it should be plain diff --git a/Mailman/Handlers/Tagger.py b/Mailman/Handlers/Tagger.py index 270f6611a..f384356d1 100644 --- a/Mailman/Handlers/Tagger.py +++ b/Mailman/Handlers/Tagger.py @@ -1,21 +1,21 @@ -# Copyright (C) 2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. -"""Extract topics from the original mail message. -""" +"""Extract topics from the original mail message.""" import re import email @@ -23,8 +23,6 @@ import email.Errors import email.Iterators import email.Parser -from Mailman.Logging.Syslog import syslog - CRNL = '\r\n' EMPTYSTRING = '' NLTAB = '\n\t' @@ -61,7 +59,7 @@ def process(mlist, msg, msgdata): if hits: msgdata['topichits'] = hits.keys() msg['X-Topics'] = NLTAB.join(hits.keys()) - + def scanbody(msg, numlines=None): diff --git a/Mailman/Handlers/ToDigest.py b/Mailman/Handlers/ToDigest.py index 80bc2877e..21acb10c1 100644 --- a/Mailman/Handlers/ToDigest.py +++ b/Mailman/Handlers/ToDigest.py @@ -29,6 +29,7 @@ import os import re import copy import time +import logging from cStringIO import StringIO from email.Charset import Charset @@ -42,13 +43,12 @@ from email.Utils import getaddresses, formatdate from types import ListType from Mailman import Errors -from Mailman import Message -from Mailman import Utils from Mailman import i18n +from Mailman import Message from Mailman import mm_cfg +from Mailman import Utils from Mailman.Handlers.Decorate import decorate from Mailman.Handlers.Scrubber import process as scrubber -from Mailman.Logging.Syslog import syslog from Mailman.Mailbox import Mailbox from Mailman.Mailbox import Mailbox from Mailman.MemberAdaptor import ENABLED @@ -59,6 +59,8 @@ _ = i18n._ UEMPTYSTRING = u'' EMPTYSTRING = '' +log = logging.getLogger('mailman.error') + def process(mlist, msg, msgdata): @@ -93,7 +95,7 @@ def process(mlist, msg, msgdata): except Exception, errmsg: # Bare except is generally prohibited in Mailman, but we can't # forecast what exceptions can occur here. - syslog('error', 'send_digests() failed: %s', errmsg) + log.error('send_digests() failed: %s', errmsg) mboxfp.close() diff --git a/Mailman/Handlers/ToUsenet.py b/Mailman/Handlers/ToUsenet.py index 805d7f11d..b51c00a65 100644 --- a/Mailman/Handlers/ToUsenet.py +++ b/Mailman/Handlers/ToUsenet.py @@ -1,27 +1,32 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Move the message to the mail->news queue.""" +import logging + from Mailman import mm_cfg from Mailman.Queue.sbcache import get_switchboard -from Mailman.Logging.Syslog import syslog COMMASPACE = ', ' +log = logging.getLogger('mailman.error') + + def process(mlist, msg, msgdata): # short circuits @@ -36,8 +41,8 @@ def process(mlist, msg, msgdata): if not mlist.nntp_host: error.append('no NNTP host') if error: - syslog('error', 'NNTP gateway improperly configured: %s', - COMMASPACE.join(error)) + log.error('NNTP gateway improperly configured: %s', + COMMASPACE.join(error)) return # Put the message in the news runner's queue newsq = get_switchboard(mm_cfg.NEWSQUEUE_DIR) diff --git a/Mailman/ListAdmin.py b/Mailman/ListAdmin.py index f7035b5aa..2e8fc1a42 100644 --- a/Mailman/ListAdmin.py +++ b/Mailman/ListAdmin.py @@ -28,6 +28,7 @@ import time import email import errno import cPickle +import logging import marshal from cStringIO import StringIO @@ -36,11 +37,10 @@ from email.MIMEMessage import MIMEMessage from email.Utils import getaddresses from Mailman import Errors -from Mailman import Message -from Mailman import Utils from Mailman import i18n +from Mailman import Message from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog +from Mailman import Utils from Mailman.Queue.sbcache import get_switchboard from Mailman.UserDesc import UserDesc @@ -60,6 +60,8 @@ LOST = 2 DASH = '-' NL = '\n' +log = logging.getLogger('mailman.vette') + class ListAdmin: @@ -277,8 +279,8 @@ class ListAdmin: # message directly here can lead to a huge delay in web # turnaround. Log the moderation and add a header. msg['X-Mailman-Approved-At'] = email.Utils.formatdate(localtime=1) - syslog('vette', 'held message approved, message-id: %s', - msg.get('message-id', 'n/a')) + log.info('held message approved, message-id: %s', + msg.get('message-id', 'n/a')) # Stick the message back in the incoming queue for further # processing. inq = get_switchboard(mm_cfg.INQUEUE_DIR) @@ -344,7 +346,7 @@ class ListAdmin: } if comment: note += '\n\tReason: ' + comment.replace('%', '%%') - syslog('vette', note) + log.info('%s', note) # Always unlink the file containing the message text. It's not # necessary anymore, regardless of the disposition of the message. if status <> DEFER: @@ -376,8 +378,8 @@ class ListAdmin: # # TBD: this really shouldn't go here but I'm not sure where else is # appropriate. - syslog('vette', '%s: held subscription request from %s', - self.internal_name(), addr) + log.info('%s: held subscription request from %s', + self.internal_name(), addr) # Possibly notify the administrator in default list language if self.admin_immed_notify: realname = self.real_name @@ -428,8 +430,8 @@ class ListAdmin: id = self.__nextid() # All we need to do is save the unsubscribing address self.__db[id] = (UNSUBSCRIPTION, addr) - syslog('vette', '%s: held unsubscription request from %s', - self.internal_name(), addr) + log.info('%s: held unsubscription request from %s', + self.internal_name(), addr) # Possibly notify the administrator of the hold if self.admin_immed_notify: realname = self.real_name diff --git a/Mailman/Logging/Logger.py b/Mailman/Logging/Logger.py deleted file mode 100644 index 617347d49..000000000 --- a/Mailman/Logging/Logger.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -"""File-based logger, writes to named category files in mm_cfg.LOG_DIR.""" - -import sys -import os -import codecs -from types import StringType - -from Mailman import mm_cfg -from Mailman.Logging.Utils import _logexc - -# Set this to the encoding to be used for your log file output. If set to -# None, then it uses your system's default encoding. Otherwise, it must be an -# encoding string appropriate for codecs.open(). -LOG_ENCODING = 'iso-8859-1' - - - -class Logger: - def __init__(self, category, nofail=1, immediate=0): - """nofail says to fallback to sys.__stderr__ if write fails to - category file - a complaint message is emitted, but no exception is - raised. Set nofail=0 if you want to handle the error in your code, - instead. - - immediate=1 says to create the log file on instantiation. - Otherwise, the file is created only when there are writes pending. - """ - self.__filename = os.path.join(mm_cfg.LOG_DIR, category) - self.__fp = None - self.__nofail = nofail - self.__encoding = LOG_ENCODING or sys.getdefaultencoding() - if immediate: - self.__get_f() - - def __del__(self): - self.close() - - def __repr__(self): - return '<%s to %s>' % (self.__class__.__name__, `self.__filename`) - - def __get_f(self): - if self.__fp: - return self.__fp - else: - try: - ou = os.umask(002) - try: - try: - f = codecs.open( - self.__filename, 'a+', self.__encoding, 'replace', - 1) - except LookupError: - f = open(self.__filename, 'a+', 1) - self.__fp = f - finally: - os.umask(ou) - except IOError, e: - if self.__nofail: - _logexc(self, e) - f = self.__fp = sys.__stderr__ - else: - raise - return f - - def flush(self): - f = self.__get_f() - if hasattr(f, 'flush'): - f.flush() - - def write(self, msg): - if isinstance(msg, StringType): - msg = unicode(msg, self.__encoding, 'replace') - f = self.__get_f() - try: - f.write(msg) - except IOError, msg: - _logexc(self, msg) - - def writelines(self, lines): - for l in lines: - self.write(l) - - def close(self): - if not self.__fp: - return - self.__get_f().close() - self.__fp = None diff --git a/Mailman/Logging/Makefile.in b/Mailman/Logging/Makefile.in deleted file mode 100644 index e185775fc..000000000 --- a/Mailman/Logging/Makefile.in +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# NOTE: Makefile.in is converted into Makefile by the configure script -# in the parent directory. Once configure has run, you can recreate -# the Makefile by running just config.status. - -# Variables set by configure - -VPATH= @srcdir@ -srcdir= @srcdir@ -bindir= @bindir@ -prefix= @prefix@ -exec_prefix= @exec_prefix@ -DESTDIR= - -CC= @CC@ -CHMOD= @CHMOD@ -INSTALL= @INSTALL@ - -DEFS= @DEFS@ - -# Customizable but not set by configure - -OPT= @OPT@ -CFLAGS= $(OPT) $(DEFS) -PACKAGEDIR= $(prefix)/Mailman/Logging -SHELL= /bin/sh - -MODULES= *.py - -# Modes for directories and executables created by the install -# process. Default to group-writable directories but -# user-only-writable for executables. -DIRMODE= 775 -EXEMODE= 755 -FILEMODE= 644 -INSTALL_PROGRAM=$(INSTALL) -m $(EXEMODE) - - -# Rules - -all: - -install: - for f in $(MODULES); \ - do \ - $(INSTALL) -m $(FILEMODE) $(srcdir)/$$f $(DESTDIR)$(PACKAGEDIR); \ - done - -finish: - -clean: - -distclean: - -rm *.pyc - -rm Makefile diff --git a/Mailman/Logging/MultiLogger.py b/Mailman/Logging/MultiLogger.py deleted file mode 100644 index 7d9435e90..000000000 --- a/Mailman/Logging/MultiLogger.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -"""A mutiple sink logger. Any message written goes to all sub-loggers.""" - -import sys -from Mailman.Logging.Utils import _logexc - - - -class MultiLogger: - def __init__(self, *args): - self.__loggers = [] - for logger in args: - self.__loggers.append(logger) - - def add_logger(self, logger): - if logger not in self.__loggers: - self.__loggers.append(logger) - - def del_logger(self, logger): - if logger in self.__loggers: - self.__loggers.remove(logger) - - def write(self, msg): - for logger in self.__loggers: - # you want to be sure that a bug in one logger doesn't prevent - # logging to all the other loggers - try: - logger.write(msg) - except: - _logexc(logger, msg) - - def writelines(self, lines): - for line in lines: - self.write(line) - - def flush(self): - for logger in self.__loggers: - if hasattr(logger, 'flush'): - # you want to be sure that a bug in one logger doesn't prevent - # logging to all the other loggers - try: - logger.flush() - except: - _logexc(logger) - - def close(self): - for logger in self.__loggers: - # you want to be sure that a bug in one logger doesn't prevent - # logging to all the other loggers - try: - if logger <> sys.__stderr__ and logger <> sys.__stdout__: - logger.close() - except: - _logexc(logger) - - def reprime(self): - for logger in self.__loggers: - try: - logger.reprime() - except AttributeError: - pass diff --git a/Mailman/Logging/StampedLogger.py b/Mailman/Logging/StampedLogger.py deleted file mode 100644 index ca313c537..000000000 --- a/Mailman/Logging/StampedLogger.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os -import time - -from Mailman.Logging.Logger import Logger - - - -class StampedLogger(Logger): - """Record messages in log files, including date stamp and optional label. - - If manual_reprime is on (off by default), then timestamp prefix will - included only on first .write() and on any write immediately following a - call to the .reprime() method. This is useful for when StampedLogger is - substituting for sys.stderr, where you'd like to see the grouping of - multiple writes under a single timestamp (and there is often is one group, - for uncaught exceptions where a script is bombing). - - In any case, the identifying prefix will only follow writes that start on - a new line. - - Nofail (by default) says to fallback to sys.stderr if write fails to - category file. A message is emitted, but the IOError is caught. - Initialize with nofail=0 if you want to handle the error in your code, - instead. - - """ - def __init__(self, category, label=None, manual_reprime=0, nofail=1, - immediate=1): - """If specified, optional label is included after timestamp. - Other options are passed to the Logger class initializer. - """ - self.__label = label - self.__manual_reprime = manual_reprime - self.__primed = 1 - self.__bol = 1 - Logger.__init__(self, category, nofail, immediate) - - def reprime(self): - """Reset so timestamp will be included with next write.""" - self.__primed = 1 - - def write(self, msg): - if not self.__bol: - prefix = "" - else: - if not self.__manual_reprime or self.__primed: - stamp = time.strftime("%b %d %H:%M:%S %Y ", - time.localtime(time.time())) - self.__primed = 0 - else: - stamp = "" - if self.__label is None: - label = "(%d)" % os.getpid() - else: - label = "%s(%d):" % (self.__label, os.getpid()) - prefix = stamp + label - Logger.write(self, "%s %s" % (prefix, msg)) - if msg and msg[-1] == '\n': - self.__bol = 1 - else: - self.__bol = 0 - - def writelines(self, lines): - first = 1 - for l in lines: - if first: - self.write(l) - first = 0 - else: - if l and l[0] not in [' ', '\t', '\n']: - Logger.write(self, ' ' + l) - else: - Logger.write(self, l) diff --git a/Mailman/Logging/Syslog.py b/Mailman/Logging/Syslog.py deleted file mode 100644 index 531ab1d7b..000000000 --- a/Mailman/Logging/Syslog.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -"""Central logging class for the Mailman system. - -This might eventually be replaced by a syslog based logger, hence the name. -""" - -import quopri - -from Mailman.Logging.StampedLogger import StampedLogger - - - -# Global, shared logger instance. All clients should use this object. -syslog = None - - - -# Don't instantiate except below. -class _Syslog: - def __init__(self): - self._logfiles = {} - - def __del__(self): - self.close() - - def write(self, kind, msg, *args, **kws): - self.write_ex(kind, msg, args, kws) - - # We need this because SMTPDirect tries to pass in a special dict-like - # object, which is not a concrete dictionary. This is not allowed by - # Python's extended call syntax. :( - def write_ex(self, kind, msg, args=None, kws=None): - origmsg = msg - logf = self._logfiles.get(kind) - if not logf: - logf = self._logfiles[kind] = StampedLogger(kind) - try: - if args: - msg %= args - if kws: - msg %= kws - # It's really bad if exceptions in the syslogger cause other crashes - except Exception, e: - msg = 'Bad format "%s": %s: %s' % (origmsg, repr(e), e) - try: - logf.write(msg + '\n') - except UnicodeError: - # Python 2.4 may fail to write 8bit (non-ascii) characters - logf.write(quopri.encodestring(msg) + '\n') - - # For the ultimate in convenience - __call__ = write - - def close(self): - for kind, logger in self._logfiles.items(): - logger.close() - self._logfiles.clear() - - -syslog = _Syslog() diff --git a/Mailman/Logging/Utils.py b/Mailman/Logging/Utils.py deleted file mode 100644 index 46bf487af..000000000 --- a/Mailman/Logging/Utils.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import sys -import traceback - - -def _logexc(logger=None, msg=''): - sys.__stderr__.write('Logging error: %s\n' % logger) - traceback.print_exc(file=sys.__stderr__) - sys.__stderr__.write('Original log message:\n%s\n' % msg) - - -def LogStdErr(category, label, manual_reprime=1, tee_to_real_stderr=1): - """Establish a StampedLogger on sys.stderr if possible. - - If tee_to_real_stderr is true, then the real standard error also gets - output, via a MultiLogger. - - Returns the MultiLogger if successful, None otherwise. - """ - from StampedLogger import StampedLogger - from MultiLogger import MultiLogger - try: - logger = StampedLogger(category, - label=label, - manual_reprime=manual_reprime, - nofail=0) - if tee_to_real_stderr: - if hasattr(sys, '__stderr__'): - stderr = sys.__stderr__ - else: - stderr = sys.stderr - logger = MultiLogger(stderr, logger) - sys.stderr = logger - return sys.stderr - except IOError: - return None - diff --git a/Mailman/Logging/__init__.py b/Mailman/Logging/__init__.py deleted file mode 100644 index f569e43f4..000000000 --- a/Mailman/Logging/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/Mailman/MTA/Postfix.py b/Mailman/MTA/Postfix.py index 172b22ea8..46143c549 100644 --- a/Mailman/MTA/Postfix.py +++ b/Mailman/MTA/Postfix.py @@ -22,20 +22,22 @@ import grp import pwd import time import errno +import logging from stat import * from Mailman import LockFile -from Mailman import Utils from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog -from Mailman.MTA.Utils import makealiases +from Mailman import Utils from Mailman.i18n import _ +from Mailman.MTA.Utils import makealiases LOCKFILE = os.path.join(mm_cfg.LOCK_DIR, 'creator') ALIASFILE = os.path.join(mm_cfg.DATA_DIR, 'aliases') VIRTFILE = os.path.join(mm_cfg.DATA_DIR, 'virtual-mailman') +log = logging.getLogger('mailman.error') + def _update_maps(): @@ -44,15 +46,15 @@ def _update_maps(): status = (os.system(acmd) >> 8) & 0xff if status: errstr = os.strerror(status) - syslog('error', msg, acmd, status, errstr) - raise RuntimeError, msg % (acmd, status, errstr) + log.error(msg, acmd, status, errstr) + raise RuntimeError(msg % (acmd, status, errstr)) if os.path.exists(VIRTFILE): vcmd = mm_cfg.POSTFIX_MAP_CMD + ' ' + VIRTFILE status = (os.system(vcmd) >> 8) & 0xff if status: errstr = os.strerror(status) - syslog('error', msg, vcmd, status, errstr) - raise RuntimeError, msg % (vcmd, status, errstr) + log.error(msg, vcmd, status, errstr) + raise RuntimeError(msg % (vcmd, status, errstr)) diff --git a/Mailman/MailList.py b/Mailman/MailList.py index f1703a50e..a47a94316 100644 --- a/Mailman/MailList.py +++ b/Mailman/MailList.py @@ -30,6 +30,7 @@ import shutil import socket import urllib import cPickle +import logging import marshal import email.Iterators @@ -64,17 +65,21 @@ from Mailman.TopicMgr import TopicMgr from Mailman import Gui # Other useful classes +from Mailman import i18n from Mailman import MemberAdaptor from Mailman import Message from Mailman import Site -from Mailman import i18n -from Mailman.Logging.Syslog import syslog from Mailman.OldStyleMemberships import OldStyleMemberships _ = i18n._ EMPTYSTRING = '' +clog = logging.getLogger('mailman.config') +elog = logging.getLogger('mailman.error') +vlog = logging.getLogger('mailman.vette') +slog = logging.getLogger('mailman.subscribe') + # Use mixins here just to avoid having any one chunk be too large. @@ -111,7 +116,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, if e.errno == errno.ENOENT: pass else: - syslog('error', 'IOError reading list extension: %s', e) + elog.error('IOError reading list extension: %s', e) else: func = dict.get('extend') if func: @@ -515,8 +520,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, os.fsync(fp.fileno()) fp.close() except IOError, e: - syslog('error', - 'Failed config.pck write, retaining old state.\n%s', e) + elog.error('Failed config.pck write, retaining old state.\n%s', e) if fp is not None: os.unlink(fname_tmp) raise @@ -624,8 +628,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, if dict is None: if e is not None: # Had problems with this file; log it and try the next one. - syslog('error', "couldn't load config file %s\n%s", - file, e) + elog.error("couldn't load config file %s\n%s", file, e) else: # We already have the most up-to-date state return @@ -633,15 +636,15 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, break else: # Nothing worked, so we have to give up - syslog('error', 'All %s fallbacks were corrupt, giving up', - self.internal_name()) + elog.error('All %s fallbacks were corrupt, giving up', + self.internal_name()) raise Errors.MMCorruptListDatabaseError, e # Now, if we didn't end up using the primary database file, we want to # copy the fallback into the primary so that the logic in Save() will # still work. For giggles, we'll copy it to a safety backup. Note we # MUST do this with the underlying list lock acquired. if file == plast or file == dlast: - syslog('error', 'fixing corrupt config file, using: %s', file) + elog.error('fixing corrupt config file, using: %s', file) unlock = True try: try: @@ -733,8 +736,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, if self.reply_to_address.strip() and self.reply_goes_to_list: Utils.ValidateEmail(self.reply_to_address) except Errors.EmailAddressError: - syslog('error', 'Bad reply_to_address "%s" cleared for list: %s', - self.reply_to_address, self.internal_name()) + elog.error('Bad reply_to_address "%s" cleared for list: %s', + self.reply_to_address, self.internal_name()) self.reply_to_address = '' self.reply_goes_to_list = 0 # Legacy topics may have bad regular expressions in their patterns @@ -743,8 +746,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, try: re.compile(pattern) except (re.error, TypeError): - syslog('error', 'Bad topic pattern "%s" for list: %s', - pattern, self.internal_name()) + elog.error('Bad topic pattern "%s" for list: %s', + pattern, self.internal_name()) else: goodtopics.append((name, pattern, desc, emptyflag)) self.topics = goodtopics @@ -842,8 +845,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, # Is the subscribing address banned from this list? pattern = self.GetBannedPattern(email) if pattern: - syslog('vette', '%s banned subscription: %s (matched: %s)', - realname, email, pattern) + vlog.error('%s banned subscription: %s (matched: %s)', + realname, email, pattern) raise Errors.MembershipIsBanned, pattern # Sanity check the digest flag if digest and not self.digestable: @@ -896,8 +899,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, msg['Reply-To'] = self.GetRequestEmail(cookie) msg.send(self) who = formataddr((name, email)) - syslog('subscribe', '%s: pending %s %s', - self.internal_name(), who, by) + slog.info('%s: pending %s %s', self.internal_name(), who, by) raise Errors.MMSubscribeNeedsConfirmation elif self.HasAutoApprovedSender(email): # no approval necessary: @@ -965,8 +967,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, kind = ' (digest)' else: kind = '' - syslog('subscribe', '%s: new%s %s, %s', self.internal_name(), - kind, formataddr((name, email)), whence) + slog.info('%s: new%s %s, %s', self.internal_name(), + kind, formataddr((name, email)), whence) if ack: self.SendSubscribeAck(email, self.getMemberPassword(email), digest, text) @@ -1027,8 +1029,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, whence = "; %s" % whence else: whence = "" - syslog('subscribe', '%s: deleted %s%s', - self.internal_name(), name, whence) + slog.info('%s: deleted %s%s', self.internal_name(), name, whence) def ChangeMemberName(self, addr, name, globally): self.setMemberName(addr, name) @@ -1071,9 +1072,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, # too harsh. pattern = self.GetBannedPattern(newaddr) if pattern: - syslog('vette', - '%s banned address change: %s -> %s (matched: %s)', - realname, oldaddr, newaddr, pattern) + vlog.error('%s banned address change: %s -> %s (matched: %s)', + realname, oldaddr, newaddr, pattern) raise Errors.MembershipIsBanned, pattern # Pend the subscription change cookie = self.pend_new(Pending.CHANGE_OF_ADDRESS, @@ -1153,8 +1153,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, def log_and_notify_admin(self, oldaddr, newaddr): """Log member address change and notify admin if requested.""" - syslog('subscribe', '%s: changed member address from %s to %s', - self.internal_name(), oldaddr, newaddr) + slog.info('%s: changed member address from %s to %s', + self.internal_name(), oldaddr, newaddr) if self.admin_notify_mchanges: lang = self.preferred_language otrans = i18n.get_translation() @@ -1299,7 +1299,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, except KeyError: # Most likely because the message has already been disposed of # via the admindb page. - syslog('error', 'Could not process HELD_MESSAGE: %s', id) + elog.error('Could not process HELD_MESSAGE: %s', id) return (op,) elif op == Pending.RE_ENABLE: member = data[1] @@ -1417,8 +1417,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, if i < 0: # This didn't look like a header line. BAW: should do a # better job of informing the list admin. - syslog('config', 'bad bounce_matching_header line: %s\n%s', - self.real_name, line) + clog.error('bad bounce_matching_header line: %s\n%s', + self.real_name, line) else: header = line[:i] value = line[i+1:].lstrip() @@ -1427,9 +1427,9 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, except re.error, e: # The regexp was malformed. BAW: should do a better # job of informing the list admin. - syslog('config', '''\ + clog.error("""\ bad regexp in bounce_matching_header line: %s -\n%s (cause: %s)''', self.real_name, value, e) +\n%s (cause: %s)""", self.real_name, value, e) else: all.append((header, cre, line)) return all @@ -1471,12 +1471,10 @@ bad regexp in bounce_matching_header line: %s date, count = info if count < 0: # They've already hit the limit for today. - syslog('vette', '-request/hold autoresponse discarded for: %s', - sender) + vlog.info('-request/hold autoresponse discarded for: %s', sender) return 0 if count >= mm_cfg.MAX_AUTORESPONSES_PER_DAY: - syslog('vette', '-request/hold autoresponse limit hit for: %s', - sender) + vlog.info('-request/hold autoresponse limit hit for: %s', sender) self.hold_and_cmd_autoresponses[sender] = (today, -1) # Send this notification message instead text = Utils.maketext( @@ -1509,8 +1507,8 @@ bad regexp in bounce_matching_header line: %s auto_approve = False if self.GetPattern(sender, self.subscribe_auto_approval): auto_approve = True - syslog('vette', '%s: auto approved subscribe from %s', - self.internal_name(), sender) + vlog.info('%s: auto approved subscribe from %s', + self.internal_name(), sender) return auto_approve def GetPattern(self, email, pattern_list): diff --git a/Mailman/Queue/BounceRunner.py b/Mailman/Queue/BounceRunner.py index cb125ef58..0063635ac 100644 --- a/Mailman/Queue/BounceRunner.py +++ b/Mailman/Queue/BounceRunner.py @@ -21,23 +21,26 @@ import os import re import time import cPickle +import logging from email.MIMEMessage import MIMEMessage from email.MIMEText import MIMEText from email.Utils import parseaddr from Mailman import LockFile -from Mailman import Utils from Mailman import mm_cfg +from Mailman import Utils from Mailman.Bouncers import BouncerAPI -from Mailman.Logging.Syslog import syslog +from Mailman.i18n import _ from Mailman.Message import UserNotification from Mailman.Queue.Runner import Runner from Mailman.Queue.sbcache import get_switchboard -from Mailman.i18n import _ COMMASPACE = ', ' +log = logging.getLogger('mailman.bounce') +elog = logging.getLogger('mailman.error') + class BounceMixin: @@ -96,8 +99,7 @@ class BounceMixin: self._bouncecnt += len(addrs) def _register_bounces(self): - syslog('bounce', '%s processing %s queued bounces', - self, self._bouncecnt) + log.info('%s processing %s queued bounces', self, self._bouncecnt) # Read all the records from the bounce file, then unlink it. Sort the # records by listname for more efficient processing. events = {} @@ -106,7 +108,7 @@ class BounceMixin: try: listname, addr, day, msg = cPickle.load(self._bounce_events_fp) except ValueError, e: - syslog('bounce', 'Error reading bounce events: %s', e) + log.error('Error reading bounce events: %s', e) except EOFError: break events.setdefault(listname, []).append((addr, day, msg)) @@ -210,8 +212,8 @@ class BounceRunner(Runner, BounceMixin): # If that still didn't return us any useful addresses, then send it on # or discard it. if not addrs: - syslog('bounce', 'bounce message w/no discernable addresses: %s', - msg.get('message-id')) + log.info('bounce message w/no discernable addresses: %s', + msg.get('message-id')) maybe_forward(mlist, msg) return # BAW: It's possible that there are None's in the list of addresses, @@ -252,9 +254,8 @@ def verp_bounce(mlist, msg): # All is good addr = '%s@%s' % mo.group('mailbox', 'host') except IndexError: - syslog('error', - "VERP_REGEXP doesn't yield the right match groups: %s", - mm_cfg.VERP_REGEXP) + elog.error("VERP_REGEXP doesn't yield the right match groups: %s", + mm_cfg.VERP_REGEXP) return [] return [addr] @@ -287,8 +288,7 @@ def verp_probe(mlist, msg): if data is not None: return token except IndexError: - syslog( - 'error', + elog.error( "VERP_PROBE_REGEXP doesn't yield the right match groups: %s", mm_cfg.VERP_PROBE_REGEXP) return None @@ -313,8 +313,8 @@ For more information see: """), subject=_('Uncaught bounce notification'), tomoderators=0) - syslog('bounce', 'forwarding unrecognized, message-id: %s', - msg.get('message-id', 'n/a')) + log.error('forwarding unrecognized, message-id: %s', + msg.get('message-id', 'n/a')) else: - syslog('bounce', 'discarding unrecognized, message-id: %s', - msg.get('message-id', 'n/a')) + log.error('discarding unrecognized, message-id: %s', + msg.get('message-id', 'n/a')) diff --git a/Mailman/Queue/CommandRunner.py b/Mailman/Queue/CommandRunner.py index 64b8f0758..978cb05cd 100644 --- a/Mailman/Queue/CommandRunner.py +++ b/Mailman/Queue/CommandRunner.py @@ -21,10 +21,9 @@ # bounce messages (i.e. -admin or -bounces), nor does it handle mail to # -owner. - - import re import sys +import logging from email.Errors import HeaderParseError from email.Header import decode_header, make_header, Header @@ -35,15 +34,16 @@ from types import StringType, UnicodeType from Mailman import LockFile from Mailman import Message -from Mailman import Utils from Mailman import mm_cfg +from Mailman import Utils from Mailman.Handlers import Replybot -from Mailman.Logging.Syslog import syslog -from Mailman.Queue.Runner import Runner from Mailman.i18n import _ +from Mailman.Queue.Runner import Runner NL = '\n' +log = logging.getLogger('mailman.vette') + class Results: @@ -202,14 +202,14 @@ class CommandRunner(Runner): precedence = msg.get('precedence', '').lower() ack = msg.get('x-ack', '').lower() if ack <> 'yes' and precedence in ('bulk', 'junk', 'list'): - syslog('vette', 'Precedence: %s message discarded by: %s', - precedence, mlist.GetRequestEmail()) + log.info('Precedence: %s message discarded by: %s', + precedence, mlist.GetRequestEmail()) return False # Do replybot for commands mlist.Load() Replybot.process(mlist, msg, msgdata) if mlist.autorespond_requests == 1: - syslog('vette', 'replied and discard') + log.info('replied and discard') # w/discard return False # Now craft the response diff --git a/Mailman/Queue/IncomingRunner.py b/Mailman/Queue/IncomingRunner.py index 71d939197..19a315040 100644 --- a/Mailman/Queue/IncomingRunner.py +++ b/Mailman/Queue/IncomingRunner.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -93,16 +93,21 @@ # performed. Results notifications are sent to the author of the message, # which all bounces pointing back to the -bounces address. + -import sys import os +import sys +import logging + from cStringIO import StringIO -from Mailman import mm_cfg from Mailman import Errors from Mailman import LockFile +from Mailman import mm_cfg from Mailman.Queue.Runner import Runner -from Mailman.Logging.Syslog import syslog + +log = logging.getLogger('mailman.error') +vlog = logging.getLogger('mailman.vette') @@ -153,12 +158,12 @@ class IncomingRunner(Runner): sys.modules[modname].process(mlist, msg, msgdata) # Failsafe -- a child may have leaked through. if pid <> os.getpid(): - syslog('error', 'child process leaked thru: %s', modname) + log.error('child process leaked thru: %s', modname) os._exit(1) except Errors.DiscardMessage: # Throw the message away; we need do nothing else with it. - syslog('vette', 'Message discarded, msgid: %s', - msg.get('message-id', 'n/a')) + vlog.info('Message discarded, msgid: %s', + msg.get('message-id', 'n/a')) return 0 except Errors.HoldMessage: # Let the approval process take it from here. The message no diff --git a/Mailman/Queue/MaildirRunner.py b/Mailman/Queue/MaildirRunner.py index 39971ae2a..97b71c7ef 100644 --- a/Mailman/Queue/MaildirRunner.py +++ b/Mailman/Queue/MaildirRunner.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002 by the Free Software Foundation, Inc. +# Copyright (C) 2002-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,7 +12,8 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Maildir pre-queue runner. @@ -51,6 +52,7 @@ mechanism. import os import re import errno +import logging from email.Parser import Parser from email.Utils import parseaddr @@ -60,7 +62,6 @@ from Mailman import Utils from Mailman.Message import Message from Mailman.Queue.Runner import Runner from Mailman.Queue.sbcache import get_switchboard -from Mailman.Logging.Syslog import syslog # We only care about the listname and the subq as in listname@ or # listname-request@ @@ -73,6 +74,8 @@ lre = re.compile(r""" )? # if it exists """, re.VERBOSE | re.IGNORECASE) +log = logging.getLogger('mailman.error') + class MaildirRunner(Runner): @@ -110,7 +113,7 @@ class MaildirRunner(Runner): if e.errno == errno.ENOENT: # Some other MaildirRunner beat us to it continue - syslog('error', 'Could not rename maildir file: %s', srcname) + log.error('Could not rename maildir file: %s', srcname) raise # Now open, read, parse, and enqueue this message try: @@ -139,8 +142,8 @@ class MaildirRunner(Runner): else: # As far as we can tell, this message isn't destined for # any list on the system. What to do? - syslog('error', 'Message apparently not for any list: %s', - xdstname) + log.error('Message apparently not for any list: %s', + xdstname) os.rename(dstname, xdstname) continue # BAW: blech, hardcoded @@ -171,14 +174,14 @@ class MaildirRunner(Runner): msgdata['torequest'] = 1 queue = get_switchboard(mm_cfg.CMDQUEUE_DIR) else: - syslog('error', 'Unknown sub-queue: %s', subq) + log.error('Unknown sub-queue: %s', subq) os.rename(dstname, xdstname) continue queue.enqueue(msg, msgdata) os.unlink(dstname) except Exception, e: os.rename(dstname, xdstname) - syslog('error', str(e)) + log.error('%s', e) def _cleanup(self): pass diff --git a/Mailman/Queue/NewsRunner.py b/Mailman/Queue/NewsRunner.py index fa0e91377..7a71f5bf2 100644 --- a/Mailman/Queue/NewsRunner.py +++ b/Mailman/Queue/NewsRunner.py @@ -19,6 +19,7 @@ import re import email import socket +import logging import nntplib from cStringIO import StringIO @@ -26,11 +27,11 @@ from email.Utils import getaddresses COMMASPACE = ', ' -from Mailman import Utils from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog +from Mailman import Utils from Mailman.Queue.Runner import Runner +log = logging.getLogger('mailman.error') # Matches our Mailman crafted Message-IDs. See Utils.unique_message_id() mcre = re.compile(r""" @@ -67,13 +68,11 @@ class NewsRunner(Runner): password=mm_cfg.NNTP_PASSWORD) conn.post(fp) except nntplib.error_temp, e: - syslog('error', - '(NNTPDirect) NNTP error for list "%s": %s', - mlist.internal_name(), e) + log.error('(NNTPDirect) NNTP error for list "%s": %s', + mlist.internal_name(), e) except socket.error, e: - syslog('error', - '(NNTPDirect) socket error for list "%s": %s', - mlist.internal_name(), e) + log.error('(NNTPDirect) socket error for list "%s": %s', + mlist.internal_name(), e) finally: if conn: conn.quit() diff --git a/Mailman/Queue/OutgoingRunner.py b/Mailman/Queue/OutgoingRunner.py index a4a83000a..3d4575ed2 100644 --- a/Mailman/Queue/OutgoingRunner.py +++ b/Mailman/Queue/OutgoingRunner.py @@ -22,12 +22,12 @@ import copy import time import email import socket +import logging from Mailman import Errors from Mailman import LockFile from Mailman import Message from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog from Mailman.Queue.BounceRunner import BounceMixin from Mailman.Queue.Runner import Runner from Mailman.Queue.Switchboard import Switchboard @@ -36,6 +36,8 @@ from Mailman.Queue.Switchboard import Switchboard # permanent failures. It is a count of calls to _doperiodic() DEAL_WITH_PERMFAILURES_EVERY = 10 +log = logging.getLogger('mailman.error') + class OutgoingRunner(Runner, BounceMixin): @@ -66,7 +68,7 @@ class OutgoingRunner(Runner, BounceMixin): self._func(mlist, msg, msgdata) # Failsafe -- a child may have leaked through. if pid <> os.getpid(): - syslog('error', 'child process leaked thru: %s', modname) + log.error('child process leaked thru: %s', modname) os._exit(1) self.__logged = False except socket.error: @@ -78,8 +80,8 @@ class OutgoingRunner(Runner, BounceMixin): port = 'smtp' # Log this just once. if not self.__logged: - syslog('error', 'Cannot connect to SMTP server %s on port %s', - mm_cfg.SMTPHOST, port) + log.error('Cannot connect to SMTP server %s on port %s', + mm_cfg.SMTPHOST, port) self.__logged = True return True except Errors.SomeRecipientsFailed, e: diff --git a/Mailman/Queue/Runner.py b/Mailman/Queue/Runner.py index 7c99c50a5..56baf431b 100644 --- a/Mailman/Queue/Runner.py +++ b/Mailman/Queue/Runner.py @@ -19,18 +19,20 @@ import time import weakref import traceback +import logging import email.Errors from cStringIO import StringIO from Mailman import Errors -from Mailman import MailList -from Mailman import Utils from Mailman import i18n +from Mailman import MailList from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog +from Mailman import Utils from Mailman.Queue.Switchboard import Switchboard +log = logging.getLogger('mailman.error') + class Runner: @@ -97,7 +99,7 @@ class Runner: # There's not much we can do (and we didn't even get the # metadata, so just log the exception and continue. self._log(e) - syslog('error', 'Ignoring unparseable message: %s', filebase) + log.error('Ignoring unparseable message: %s', filebase) continue try: self._onefile(msg, msgdata) @@ -112,7 +114,7 @@ class Runner: # Put a marker in the metadata for unshunting msgdata['whichq'] = self._switchboard.whichq() filebase = self._shunt.enqueue(msg, msgdata) - syslog('error', 'SHUNTING: %s', filebase) + log.error('SHUNTING: %s', filebase) # Other work we want to do each time through the loop Utils.reap(self._kids, once=True) self._doperiodic() @@ -133,9 +135,8 @@ class Runner: listname = mm_cfg.MAILMAN_SITE_LIST mlist = self._open_list(listname) if not mlist: - syslog('error', - 'Dequeuing message destined for missing list: %s', - listname) + log.error('Dequeuing message destined for missing list: %s', + listname) self._shunt.enqueue(msg, msgdata) return # Now process this message, keeping track of any subprocesses that may @@ -178,17 +179,17 @@ class Runner: try: mlist = MailList.MailList(listname, lock=False) except Errors.MMListError, e: - syslog('error', 'error opening list: %s\n%s', listname, e) + log.error('error opening list: %s\n%s', listname, e) return None else: self._listcache[listname] = mlist return mlist def _log(self, exc): - syslog('error', 'Uncaught runner exception: %s', exc) + log.error('Uncaught runner exception: %s', exc) s = StringIO() traceback.print_exc(file=s) - syslog('error', s.getvalue()) + log.error('%s', s.getvalue()) # # Subclasses can override these methods. diff --git a/Mailman/Queue/Switchboard.py b/Mailman/Queue/Switchboard.py index 33177ba8b..36b69283e 100644 --- a/Mailman/Queue/Switchboard.py +++ b/Mailman/Queue/Switchboard.py @@ -41,9 +41,8 @@ import cPickle import marshal from Mailman import Message -from Mailman import Utils from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog +from Mailman import Utils # 20 bytes of all bits set, maximum sha.digest() value shamax = 0xffffffffffffffffffffffffffffffffffffffffL diff --git a/Mailman/SafeDict.py b/Mailman/SafeDict.py index 12cf03f57..92f6b238e 100644 --- a/Mailman/SafeDict.py +++ b/Mailman/SafeDict.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -12,27 +12,25 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """A `safe' dictionary for string interpolation.""" -from types import StringType -from UserDict import UserDict - COMMASPACE = ', ' -class SafeDict(UserDict): +class SafeDict(dict): """Dictionary which returns a default value for unknown keys. This is used in maketext so that editing templates is a bit more robust. """ def __getitem__(self, key): try: - return self.data[key] + return super(SafeDict, self).__getitem__(key) except KeyError: - if isinstance(key, StringType): + if isinstance(key, basestring): return '%('+key+')s' else: return '<Missing key: %s>' % `key` @@ -43,9 +41,11 @@ class SafeDict(UserDict): class MsgSafeDict(SafeDict): - def __init__(self, msg, dict=None): + def __init__(self, msg, d=None): self.__msg = msg - SafeDict.__init__(self, dict) + if d is None: + d = {} + super(MsgSafeDict, self).__init__(d) def __getitem__(self, key): if key.startswith('msg_'): @@ -57,10 +57,10 @@ class MsgSafeDict(SafeDict): return 'n/a' return COMMASPACE.join(all) else: - return SafeDict.__getitem__(self, key) + return super(MsgSafeDict, self).__getitem__(key) def copy(self): - d = self.data.copy() + d = super(MsgSafeDict, self).copy() for k in self.__msg.keys(): vals = self.__msg.get_all(k) if len(vals) == 1: diff --git a/Mailman/SecurityManager.py b/Mailman/SecurityManager.py index 204daf417..91ab6f52f 100644 --- a/Mailman/SecurityManager.py +++ b/Mailman/SecurityManager.py @@ -12,8 +12,8 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. """Handle passwords and sanitize approved messages.""" @@ -53,6 +53,7 @@ import sha import time import urllib import Cookie +import logging import marshal import binascii @@ -60,15 +61,16 @@ from types import StringType, TupleType from urlparse import urlparse from Mailman import Errors -from Mailman import Utils from Mailman import mm_cfg -from Mailman.Logging.Syslog import syslog +from Mailman import Utils try: import crypt except ImportError: crypt = None +log = logging.getLogger('mailman.error') + class SecurityManager: @@ -201,8 +203,8 @@ class SecurityManager: pass else: # What is this context??? - syslog('error', 'Bad authcontext: %s', ac) - raise ValueError, 'Bad authcontext: %s' % ac + log.error('Bad authcontext: %s', ac) + raise ValueError('Bad authcontext: %s' % ac) return mm_cfg.UnAuthorized def WebAuthenticate(self, authcontexts, response, user=None): diff --git a/Mailman/Utils.py b/Mailman/Utils.py index b5b13252d..cb3444b17 100644 --- a/Mailman/Utils.py +++ b/Mailman/Utils.py @@ -31,6 +31,7 @@ import time import errno import base64 import random +import logging import urlparse import htmlentitydefs import email.Header @@ -57,6 +58,8 @@ cre = re.compile(r'%\(([_a-z]\w*?)\)s?', re.IGNORECASE) # Search for $$, $identifier, or ${identifier} dre = re.compile(r'(\${2})|\$([_a-z]\w*)|\${([_a-z]\w*)}', re.IGNORECASE) +log = logging.getLogger('mailman.error') + def list_exists(listname): @@ -311,9 +314,7 @@ def Secure_MakeRandomPassword(length): # We have no available source of cryptographically # secure random characters. Log an error and fallback # to the user friendly passwords. - from Mailman.Logging.Syslog import syslog - syslog('error', - 'urandom not available, passwords not secure') + log.error('urandom not available, passwords not secure') return UserFriendly_MakeRandomPassword(length) newbytes = os.read(fd, length - bytesread) bytes.append(newbytes) @@ -526,8 +527,7 @@ def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None): text = sdict.interpolate(utemplate) except (TypeError, ValueError), e: # The template is really screwed up - from Mailman.Logging.Syslog import syslog - syslog('error', 'broken template: %s\n%s', filename, e) + log.error('broken template: %s\n%s', filename, e) pass if raw: return text, filename diff --git a/Mailman/loginit.py b/Mailman/loginit.py new file mode 100644 index 000000000..323bd2985 --- /dev/null +++ b/Mailman/loginit.py @@ -0,0 +1,88 @@ +# Copyright (C) 2006 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +"""Logging initialization, using Python's standard logging package. + +This module cannot be called 'logging' because that would interfere with the +import below. Ah, for Python 2.5 and absolute imports. +""" + +import os +import logging +import logging.handlers + +from Mailman import mm_cfg + +FMT = '%(asctime)s (%(process)d) %(message)s' +DATEFMT = '%b %d %H:%M:%S %Y' +LOGGERS = ('bounce', 'mischief', 'post', 'vette', 'smtp', + 'smtp-failure', 'subscribe', 'config', 'error', + 'qrunner', + ) + +_handlers = [] + + + +def initialize(propagate=False): + # XXX Don't call logging.basicConfig() because in Python 2.3, it adds a + # handler to the root logger that we don't want. When Python 2.4 is the + # minimum requirement, we can use basicConfig() with keyword arguments. + # + # The current set of Mailman logs are: + # + # error - All exceptions go to this log + # bounce - All bounce processing logs go here + # mischief - Various types of hostile activity + # post - Information about messages posted to mailing lists + # vette - Information related to admindb activity + # smtp - Successful SMTP activity + # smtp-failure - Unsuccessful SMTP activity + # subscribe - Information about leaves/joins + # config - Configuration issues + # locks - Lock steals + # qrunner - qrunner start/stops + # + # There was also a 'debug' logger, but that was mostly unused, so instead + # we'll use debug level on existing loggers. + # + # Start by creating a common formatter and the root logger. + formatter = logging.Formatter(fmt=FMT, datefmt=DATEFMT) + log = logging.getLogger('mailman') + handler = logging.StreamHandler() + handler.setFormatter(formatter) + log.addHandler(handler) + log.setLevel(logging.INFO) + # Create the subloggers + for logger in LOGGERS: + log = logging.getLogger('mailman.' + logger) + # Propagation to the root logger is how we handle logging to stderr + # when the qrunners are not run as a subprocess of mailmanctl. + log.propagate = propagate + handler = logging.handlers.RotatingFileHandler( + os.path.join(mm_cfg.LOG_DIR, logger), + maxBytes=0, backupCount=0) + _handlers.append(handler) + handler.setFormatter(formatter) + log.addHandler(handler) + + + +def reopen(): + for handler in _handlers: + meth = getattr(handler, 'doRollover', None) + if meth: + meth() diff --git a/Mailman/versions.py b/Mailman/versions.py index f050c5b98..4b3990684 100644 --- a/Mailman/versions.py +++ b/Mailman/versions.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2006 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,14 +32,16 @@ number of the list, and then does a .Save(), so the transformations won't be run again until another version change is detected. """ +import logging from types import ListType, StringType from Mailman import mm_cfg -from Mailman import Utils from Mailman import Message +from Mailman import Utils from Mailman.MemberAdaptor import UNKNOWN -from Mailman.Logging.Syslog import syslog + +log = logging.getLogger('mailman.error') @@ -172,7 +174,6 @@ def UpdateOldVars(l, stored_state): if hasattr(l, 'moderated'): # We'll assume we're converting all these attributes at once if l.moderated: - #syslog('debug', 'Case 1') for addr in l.posters: if not l.isMember(addr): l.accept_these_nonmembers.append(addr) @@ -183,7 +184,6 @@ def UpdateOldVars(l, stored_state): l.generic_nonmember_action = 1 l.default_member_moderation = 1 elif l.member_posting_only: - #syslog('debug', 'Case 2') for addr in l.posters: if not l.isMember(addr): l.accept_these_nonmembers.append(addr) @@ -192,13 +192,11 @@ def UpdateOldVars(l, stored_state): l.generic_nonmember_action = 1 l.default_member_moderation = 0 elif not l.posters: - #syslog('debug', 'Case 3') for member in l.getMembers(): l.setMemberOption(member, mm_cfg.Moderate, 0) l.generic_nonmember_action = 0 l.default_member_moderation = 0 else: - #syslog('debug', 'Case 4') for addr in l.posters: if not l.isMember(addr): l.accept_these_nonmembers.append(addr) @@ -506,6 +504,6 @@ def NewRequestsDatabase(l): mm_cfg.DEFAULT_SERVER_LANGUAGE) del r[k] else: - syslog('error', """\ + log.error("""\ VERY BAD NEWS. Unknown pending request type `%s' found for list: %s""", k, l.internal_name()) |
