Port header matching previously described by the misnamed KNONW_SPAMMERS
[mailman.git] / Mailman / loginit.py
blobc3b3aac054e9c1ce66760af960a96d6f92c67d91
1 # Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
16 # USA.
18 """Logging initialization, using Python's standard logging package.
20 This module cannot be called 'logging' because that would interfere with the
21 import below. Ah, for Python 2.5 and absolute imports.
22 """
24 import os
25 import sys
26 import codecs
27 import logging
28 import ConfigParser
30 from Mailman.configuration import config
34 FMT = '%(asctime)s (%(process)d) %(message)s'
35 DATEFMT = '%b %d %H:%M:%S %Y'
37 LOGGERS = (
38 'bounce', # All bounce processing logs go here
39 'config', # Configuration issues
40 'debug', # Only used for development
41 'error', # All exceptions go to this log
42 'fromusenet', # Information related to the Usenet to Mailman gateway
43 'http', # Internal wsgi-based web interface
44 'locks', # Lock state changes
45 'mischief', # Various types of hostile activity
46 'post', # Information about messages posted to mailing lists
47 'qrunner', # qrunner start/stops
48 'smtp', # Successful SMTP activity
49 'smtp-failure', # Unsuccessful SMTP activity
50 'subscribe', # Information about leaves/joins
51 'vette', # Information related to admindb activity
54 _handlers = []
58 class ReallySafeConfigParser(ConfigParser.SafeConfigParser):
59 def getstring(self, section, option, default=None):
60 try:
61 return ConfigParser.SafeConfigParser.get(self, section, option)
62 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
63 return default
65 def getboolean(self, section, option, default=None):
66 try:
67 return ConfigParser.SafeConfigParser.getboolean(
68 self, section, option)
69 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
70 return default
74 class ReopenableFileHandler(logging.Handler):
75 def __init__(self, filename):
76 self._filename = filename
77 self._stream = self._open()
78 logging.Handler.__init__(self)
80 def _open(self):
81 return codecs.open(self._filename, 'a', 'utf-8')
83 def flush(self):
84 if self._stream:
85 self._stream.flush()
87 def emit(self, record):
88 # It's possible for the stream to have been closed by the time we get
89 # here, due to the shut down semantics. This mostly happens in the
90 # test suite, but be defensive anyway.
91 stream = self._stream or sys.stderr
92 try:
93 msg = self.format(record)
94 fs = '%s\n'
95 try:
96 stream.write(fs % msg)
97 except UnicodeError:
98 stream.write(fs % msg.encode('string-escape'))
99 self.flush()
100 except:
101 self.handleError(record)
103 def close(self):
104 self.flush()
105 self._stream.close()
106 self._stream = None
107 logging.Handler.close(self)
109 def reopen(self):
110 self._stream.close()
111 self._stream = self._open()
115 def initialize(propagate=False):
116 # Initialize the root logger, then create a formatter for all the sublogs.
117 logging.basicConfig(format=FMT, datefmt=DATEFMT, level=logging.INFO)
118 # If a custom log configuration file was specified, load it now. Note
119 # that we don't use logging.config.fileConfig() because it requires that
120 # all loggers, formatters, and handlers be defined. We want to support
121 # minimal overloading of our logger configurations.
122 cp = ReallySafeConfigParser()
123 if config.LOG_CONFIG_FILE:
124 path = os.path.join(config.ETC_DIR, config.LOG_CONFIG_FILE)
125 cp.read(os.path.normpath(path))
126 # Create the subloggers
127 for logger in LOGGERS:
128 log = logging.getLogger('mailman.' + logger)
129 # Get settings from log configuration file (or defaults).
130 log_format = cp.getstring(logger, 'format', FMT)
131 log_datefmt = cp.getstring(logger, 'datefmt', DATEFMT)
132 # Propagation to the root logger is how we handle logging to stderr
133 # when the qrunners are not run as a subprocess of mailmanctl.
134 log.propagate = cp.getboolean(logger, 'propagate', propagate)
135 # Set the logger's level. Note that if the log configuration file
136 # does not set an override, the default level will be INFO except for
137 # the 'debug' logger. It doesn't make much sense for the debug logger
138 # to ignore debug level messages!
139 level_str = cp.getstring(logger, 'level', 'INFO').upper()
140 level_def = (logging.DEBUG if logger == 'debug' else logging.INFO)
141 level_int = getattr(logging, level_str, level_def)
142 log.setLevel(level_int)
143 # Create a formatter for this logger, then a handler, and link the
144 # formatter to the handler.
145 formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt)
146 path_str = cp.getstring(logger, 'path', logger)
147 path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str))
148 handler = ReopenableFileHandler(path_abs)
149 _handlers.append(handler)
150 handler.setFormatter(formatter)
151 log.addHandler(handler)
155 def reopen():
156 for handler in _handlers:
157 handler.reopen()