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 | |
| parent | 9934c9b2b0e76a0b77b7869ecf68cd960d4d5bd7 (diff) | |
| download | mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.tar.gz mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.tar.zst mailman-0ed815a216c7bb6f820cfdf99fc8d31bcfd19fc0.zip | |
63 files changed, 640 insertions, 991 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()) diff --git a/Makefile.in b/Makefile.in index fac0321e7..0328d0c2d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -49,7 +49,7 @@ VAR_DIRS= \ ARCH_INDEP_DIRS= \ bin templates scripts cron pythonlib \ - Mailman Mailman/Cgi Mailman/Logging Mailman/Archiver \ + Mailman Mailman/Cgi Mailman/Archiver \ Mailman/Handlers Mailman/Queue Mailman/Bouncers \ Mailman/MTA Mailman/Gui Mailman/Commands messages icons \ tests tests/bounces tests/msgs diff --git a/bin/qrunner b/bin/qrunner index 20fe830d1..c076b00cd 100644 --- a/bin/qrunner +++ b/bin/qrunner @@ -75,11 +75,12 @@ operation. It is only useful for debugging if it is run separately. import sys import getopt import signal +import logging import paths from Mailman import mm_cfg +from Mailman import loginit from Mailman.i18n import _ -from Mailman.Logging.Syslog import syslog from Mailman.Logging.Utils import LogStdErr PROGRAM = sys.argv[0] @@ -88,6 +89,8 @@ COMMASPACE = ', ' # Flag which says whether we're running under mailmanctl or not. AS_SUBPROC = 0 +log = logging.getLogger('mailman.qrunner') + def usage(code, msg=''): @@ -133,7 +136,7 @@ def set_signals(loop): # Exit the qrunner cleanly loop.stop() loop.status = signal.SIGTERM - syslog('qrunner', '%s qrunner caught SIGTERM. Stopping.', loop.name()) + log.info('%s qrunner caught SIGTERM. Stopping.', loop.name()) signal.signal(signal.SIGTERM, sigterm_handler) # Set up the SIGINT handler for stopping the loop. For us, SIGINT is # the same as SIGTERM, but our parent treats the exit statuses @@ -142,14 +145,12 @@ def set_signals(loop): # Exit the qrunner cleanly loop.stop() loop.status = signal.SIGINT - syslog('qrunner', '%s qrunner caught SIGINT. Stopping.', loop.name()) + log.info('%s qrunner caught SIGINT. Stopping.', loop.name()) signal.signal(signal.SIGINT, sigint_handler) - # SIGHUP just tells us to close our log files. They'll be - # automatically reopened at the next log print :) + # SIGHUP just tells us to rotate our log files. def sighup_handler(signum, frame, loop=loop): - syslog.close() - syslog('qrunner', '%s qrunner caught SIGHUP. Reopening logs.', - loop.name()) + loginit.reopen() + log.info('%s qrunner caught SIGHUP. Reopening logs.', loop.name()) signal.signal(signal.SIGHUP, sighup_handler) @@ -214,11 +215,11 @@ def main(): if len(runners) == 0: usage(1, _('No runner name given.')) - # Before we startup qrunners, we redirect the stderr to mailman syslog. - # We assume !AS_SUBPROC is running for debugging purpose and don't - # log errors in mailman logs/error but keep printing to stderr. - if AS_SUBPROC: - LogStdErr('error', 'qrunner', manual_reprime=0, tee_to_real_stderr=0) + # If we're not running as a subprocess of mailmanctl, then we'll log to + # stderr in addition to logging to the log files. We do this by passing a + # value of True to propagate, which allows the 'mailman' root logger to + # see the log messages. + loginit.initialize(not AS_SUBPROC) # Fast track for one infinite runner if len(runners) == 1 and not once: @@ -234,9 +235,9 @@ def main(): loop = Loop(qrunner) set_signals(loop) # Now start up the main loop - syslog('qrunner', '%s qrunner started.', loop.name()) + log.info('%s qrunner started.', loop.name()) qrunner.run() - syslog('qrunner', '%s qrunner exiting.', loop.name()) + log.info('%s qrunner exiting.', loop.name()) else: # Anything else we have to handle a bit more specially qrunners = [] @@ -256,19 +257,19 @@ def main(): return self.__isdone loop = Loop() set_signals(loop) - syslog('qrunner', 'Main qrunner loop started.') + log.info('Main qrunner loop started.') while not loop.isdone(): for qrunner in qrunners: # In case the SIGTERM came in the middle of this iteration if loop.isdone(): break if verbose: - syslog('qrunner', 'Now doing a %s qrunner iteration', - qrunner.__class__.__bases__[0].__name__) + log.info('Now doing a %s qrunner iteration', + qrunner.__class__.__bases__[0].__name__) qrunner.run() if once: break - syslog('qrunner', 'Main qrunner loop exiting.') + log.info('Main qrunner loop exiting.') # All done sys.exit(loop.status) @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 2.45 . +# From configure.in Revision: 7627 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # @@ -4322,7 +4322,7 @@ build/cron/senddigests:cron/senddigests \ # scripts. They're removed on a make distclean, so we make them here. mkdir -p build/bin build/contrib build/cron - ac_config_files="$ac_config_files misc/paths.py Mailman/Defaults.py Mailman/mm_cfg.py.dist src/Makefile misc/Makefile bin/Makefile Mailman/Makefile Mailman/Cgi/Makefile Mailman/Logging/Makefile Mailman/Archiver/Makefile Mailman/Commands/Makefile Mailman/Handlers/Makefile Mailman/Bouncers/Makefile Mailman/Queue/Makefile Mailman/MTA/Makefile Mailman/Gui/Makefile templates/Makefile cron/Makefile scripts/Makefile messages/Makefile cron/crontab.in misc/mailman Makefile tests/Makefile tests/bounces/Makefile tests/msgs/Makefile $SCRIPTS" + ac_config_files="$ac_config_files misc/paths.py Mailman/Defaults.py Mailman/mm_cfg.py.dist src/Makefile misc/Makefile bin/Makefile Mailman/Makefile Mailman/Cgi/Makefile Mailman/Archiver/Makefile Mailman/Commands/Makefile Mailman/Handlers/Makefile Mailman/Bouncers/Makefile Mailman/Queue/Makefile Mailman/MTA/Makefile Mailman/Gui/Makefile templates/Makefile cron/Makefile scripts/Makefile messages/Makefile cron/crontab.in misc/mailman Makefile tests/Makefile tests/bounces/Makefile tests/msgs/Makefile $SCRIPTS" ac_config_commands="$ac_config_commands default" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -4886,7 +4886,6 @@ do "bin/Makefile" ) CONFIG_FILES="$CONFIG_FILES bin/Makefile" ;; "Mailman/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Makefile" ;; "Mailman/Cgi/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Cgi/Makefile" ;; - "Mailman/Logging/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Logging/Makefile" ;; "Mailman/Archiver/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Archiver/Makefile" ;; "Mailman/Commands/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Commands/Makefile" ;; "Mailman/Handlers/Makefile" ) CONFIG_FILES="$CONFIG_FILES Mailman/Handlers/Makefile" ;; diff --git a/configure.in b/configure.in index bb57b83e1..dd3d9a547 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2004 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 @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. dnl Process this file with autoconf to produce a configure script. -AC_REVISION($Revision: 7627 $) +AC_REVISION($Revision: 7858 $) AC_PREREQ(2.0) AC_INIT(src/common.h) @@ -666,7 +666,7 @@ mkdir -p build/bin build/contrib build/cron dnl Output everything AC_OUTPUT([misc/paths.py Mailman/Defaults.py Mailman/mm_cfg.py.dist src/Makefile misc/Makefile bin/Makefile - Mailman/Makefile Mailman/Cgi/Makefile Mailman/Logging/Makefile + Mailman/Makefile Mailman/Cgi/Makefile Mailman/Archiver/Makefile Mailman/Commands/Makefile Mailman/Handlers/Makefile Mailman/Bouncers/Makefile Mailman/Queue/Makefile Mailman/MTA/Makefile Mailman/Gui/Makefile diff --git a/scripts/driver b/scripts/driver index ea43c40eb..08bee07bd 100644 --- a/scripts/driver +++ b/scripts/driver @@ -1,6 +1,6 @@ # -*- python -*- -# Copyright (C) 1998-2004 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 @@ -14,7 +14,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. # This better succeed. If this fails, Python is royally screwed so we might # as well let the Web server give us a fatal and obtrusive error. @@ -25,15 +26,17 @@ import sys # The driver script prints out a lot of information when a Mailman bug is # encountered. This really helps for development, but it also reveals # information about the host system that some administrators are not -# comfortable with. By setting STEALTH_MODE to 1, you disable the printing of -# this information to the web pages. This information is still, and always, -# printed in the error logs. -STEALTH_MODE = 1 +# comfortable with. By setting STEALTH_MODE to True, you disable the printing +# of this information to the web pages. This information is still, and +# always, printed in the error logs. +STEALTH_MODE = True # This will be set to the entity escaper. def websafe(s): return s +SPACE = ' ' + # This standard driver script is used to run CGI programs, wrapped in code @@ -62,7 +65,7 @@ def run_main(): # These will ensure that even if something between now and the # creation of the real logger below fails, we can still get # *something* meaningful. - logger = None + log = None try: import paths # When running in non-stealth mode, we need to escape entities, @@ -71,19 +74,18 @@ def run_main(): if not STEALTH_MODE: from Mailman.Utils import websafe except: - STEALTH_MODE = 1 + STEALTH_MODE = True raise - # Map stderr to a logger, if possible. - from Mailman.Logging.StampedLogger import StampedLogger - logger = StampedLogger('error', - label='admin', - manual_reprime=1, - nofail=0, - immediate=1) - # Collect stdout in a cStringIO so that if /any/ errors occur during - # printing it won't mess up our diagnostics page. + # Initialize the standard loggers + from Mailman.loginit import initialize + initialize() + import logging + log = logging.getLogger('mailman.error') + # Collect stdout and stderr in cStringIOs so that if /any/ errors + # occur during printing it won't mess up our diagnostics page. from cStringIO import StringIO tempstdout = StringIO() + tempstderr = StringIO() # The name of the module to run is passed in argv[1]. What we # actually do is import the module named by argv[1] that lives in the # Mailman.Cgi package. That module must have a main() function, which @@ -96,10 +98,11 @@ def run_main(): main = getattr(module, 'main') try: try: - sys.stderr = logger sys.stdout = tempstdout + sys.stderr = tempstderr main() sys.__stdout__.write(tempstdout.getvalue()) + sys.__stderr__.write(tempstderr.getvalue()) finally: sys.stderr = sys.__stderr__ sys.stdout = sys.__stdout__ @@ -108,19 +111,15 @@ def run_main(): # produced is still written out to the browser. sys.stdout.write(tempstdout.getvalue()) except: - print_traceback(logger) - print_environment(logger) + print_traceback(log) + print_environment(log) -# We are printing error reporting to two places. One will always be stdout -# and the other will always be the log file. It is assumed that stdout is an -# HTML sink and the log file is a plain text sink. - -def print_traceback(logfp=None): - if logfp is None: - logfp = sys.__stderr__ - +# If possible, we print the error to two places. One will always be stdout +# and the other will be the log file if a log file was created. It is assumed +# that stdout is an HTML sink. +def print_traceback(log=None): try: import traceback except ImportError: @@ -131,14 +130,22 @@ def print_traceback(logfp=None): VERSION = '<undetermined>' # Write to the log file first. - print >> logfp, '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' - print >> logfp, '[----- Mailman Version: %s -----]' % VERSION - print >> logfp, '[----- Traceback ------]' - if traceback: - traceback.print_exc(file=logfp) - else: - print >> logfp, '[failed to import module traceback]' - print >> logfp, '[exc: %s, var: %s]' % sys.exc_info()[0:2] + if log: + from cStringIO import StringIO + outfp = StringIO() + + print >> outfp, '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' + print >> outfp, '[----- Mailman Version: %s -----]' % VERSION + print >> outfp, '[----- Traceback ------]' + if traceback: + traceback.print_exc(file=outfp) + else: + print >> outfp, '[failed to import module traceback]' + print >> outfp, '[exc: %s, var: %s]' % sys.exc_info()[0:2] + # Don't use .exception() since that'll give us the exception twice. + # IWBNI we could print directly to the log's stream, or treat a log + # like an output stream. + log.error('%s', outfp.getvalue()) # Write to the HTML sink. print """\ @@ -170,23 +177,24 @@ Mailman error logs.''' -def print_environment(logfp=None): - if logfp is None: - logfp = sys.__stderr__ - +def print_environment(log=None): try: import os except ImportError: os = None - # Write some information about our Python executable to the log file. - print >> logfp, '[----- Python Information -----]' - print >> logfp, 'sys.version =', sys.version - print >> logfp, 'sys.executable =', sys.executable - print >> logfp, 'sys.prefix =', sys.prefix - print >> logfp, 'sys.exec_prefix =', sys.exec_prefix - print >> logfp, 'sys.path =', sys.exec_prefix - print >> logfp, 'sys.platform =', sys.platform + if log: + from cStringIO import StringIO + outfp = StringIO() + + # Write some information about our Python executable to the log file. + print >> outfp, '[----- Python Information -----]' + print >> outfp, 'sys.version =', sys.version + print >> outfp, 'sys.executable =', sys.executable + print >> outfp, 'sys.prefix =', sys.prefix + print >> outfp, 'sys.exec_prefix =', sys.exec_prefix + print >> outfp, 'sys.path =', sys.exec_prefix + print >> outfp, 'sys.platform =', sys.platform # Write the same information to the HTML sink. if not STEALTH_MODE: @@ -211,12 +219,13 @@ def print_environment(logfp=None): print '</table>' # Write environment variables to the log file. - print >> logfp, '[----- Environment Variables -----]' - if os: - for k, v in os.environ.items(): - print >> logfp, '\t%s: %s' % (k, v) - else: - print >> logfp, '[failed to import module os]' + if log: + print >> logfp, '[----- Environment Variables -----]' + if os: + for k, v in os.environ.items(): + print >> outfp, '\t%s: %s' % (k, v) + else: + print >> outfp, '[failed to import module os]' # Write environment variables to the HTML sink. if not STEALTH_MODE: @@ -235,6 +244,10 @@ def print_environment(logfp=None): else: print '<p><hr>[failed to import module os]' + # Dump the log output + if log: + log.error('%s', outfp.getvalue()) + try: |
