Re: problems with logging
Steven Armstrong <[email protected]>
| Newsgroups | gmane.comp.web.pyblosxom.devel |
|---|---|
| Message-ID | <[email protected]> |
ups ... just learned that testing _all_ changes before sending patches would be a good idea. so, once again ... cheers Steven
logging.diff
(text/plain, 14.5 KB)
Index: Pyblosxom/pyblosxom.py
===================================================================
RCS file: /cvsroot/pyblosxom/pyblosxom/Pyblosxom/pyblosxom.py,v
retrieving revision 1.55
diff -u -r1.55 pyblosxom.py
--- Pyblosxom/pyblosxom.py 1 Feb 2005 01:07:26 -0000 1.55
+++ Pyblosxom/pyblosxom.py 5 Feb 2005 00:41:31 -0000
@@ -56,6 +56,9 @@
pyhttp = self._request.getHttp()
config = self._request.getConfiguration()
+ # initialize the tools module
+ tools.initialize(config)
+
data["pyblosxom_version"] = VERSION_DATE
data['pi_bl'] = ''
@@ -83,6 +86,15 @@
mappingfunc=lambda x,y:y,
defaultfunc=lambda x:x)
+ def cleanup(self):
+ """
+ Cleanup everything.
+ This should be called when Pyblosxom has done all its work.
+ Right before exiting.
+ """
+ tools.cleanup()
+
+
def getRequest(self):
"""
Returns the L{Request} object.
@@ -129,6 +141,8 @@
# do end callback
tools.run_callback("end", {'request': self._request})
+
+ self.cleanup()
def runCallback(self, callback="help"):
@@ -298,6 +312,8 @@
print "rendering '%s' ..." % url
tools.render_url(config, url, q)
+ self.cleanup()
+
def testInstallation(self):
test_installation(self._request)
@@ -706,11 +722,20 @@
'request': request})
else:
renderer.addHeader('Status', '404 Not Found')
- renderer.setContent(
- {'title': 'The page you are looking for is not available',
- 'body': 'Somehow I cannot find the page you want. ' +
- 'Go Back to <a href="%s">%s</a>?'
- % (config["base_url"], config["blog_title"])})
+ # use a custom 404-story template.
+ # it is sort of silly to have a 404 message with permalink, trackback, comment etc.
+ from Pyblosxom import entries
+ entry = entries.base.EntryBase(request)
+ entry["title"] = 'The page you are looking for is not available'
+ entry["body"] = """Somehow I cannot find the page you want.
+ Go Back to <a href="%s">%s</a>?""" % (config["base_url"], config["blog_title"])
+ entry["template_name"] = "404-story"
+ renderer.setContent([entry])
+# renderer.setContent(
+# {'title': 'The page you are looking for is not available',
+# 'body': 'Somehow I cannot find the page you want. ' +
+# 'Go Back to <a href="%s">%s</a>?'
+# % (config["base_url"], config["blog_title"])})
# Log it as failure
tools.run_callback("logrequest",
{'filename':config.get('logfile',''),
Index: Pyblosxom/tools.py
===================================================================
RCS file: /cvsroot/pyblosxom/pyblosxom/Pyblosxom/tools.py,v
retrieving revision 1.39
diff -u -r1.39 tools.py
--- Pyblosxom/tools.py 1 Feb 2005 01:07:26 -0000 1.39
+++ Pyblosxom/tools.py 5 Feb 2005 00:41:33 -0000
@@ -8,9 +8,14 @@
@var num2month: A dict of number month format to its literal format
@var MONTHS: A list of valid literal and numeral months
@var VAR_REGEXP: Regular expression for detection and substituion of variables
+@var _logger_registry: A dict of created loggers. Prevents multiple loggers/handlers using the same file.
+@var _logdir: The directory in which logfiles are created by default
+@var _config: A reference to the pyblosxom config dict.
"""
import plugin_utils
-import sgmllib, re, os, string, types, time, os.path, StringIO, sys
+import sgmllib, re, os, string, types, time, os.path, sys
+try: from cStringIO import StringIO
+except ImportError: from StringIO import StringIO
month2num = { 'nil' : '00',
'Jan' : '01',
@@ -39,6 +44,33 @@
# regular expression for detection and substituion of variables.
VAR_REGEXP = re.compile(ur'(?<!\\)\$((?:\w|\-|::\w)+(?:\(.*?(?<!\\)\))?)')
+# see module docstring for infos
+_logger_registry = {}
+_logdir = "/tmp"
+_config = None
+
+def initialize(config):
+ """
+ Initialize the tools module.
+ This gives the module a chance to use the pyblosxom config.py file
+ without having to change sys.path and importing it manually.
+ This should be called from Pyblosxom.pyblosxom.PyBlosxom.initialize.
+ """
+ global _logdir, _config
+ _logdir = config.get('logdir', _logdir)
+ _config = config
+
+def cleanup():
+ """
+ Cleanup the tools module.
+ This should be called from Pyblosxom.pyblosxom.PyBlosxom.cleanup.
+ """
+ # close log files
+ for l in _logger_registry:
+ if hasattr(_logger_registry[l], 'cleanup'):
+ _logger_registry[l].cleanup()
+
+
class Stripper(sgmllib.SGMLParser):
"""
Strips HTML
@@ -488,51 +520,6 @@
return mycache
-_logger_registry = {}
-def make_logger(filename):
- """
- Create a logging function called log, which logs to the supplied filename
- usage is:
-
- -->>> tools.make_logger('/tmp/pybloxom.log')
- -->>> tools.log('log message')
-
- @param filename: the name of a file to log to
- @type filename: string
- """
- global log
- try:
- import logging
- except ImportError:
- def log(*args):
- f = open(filename, "a")
- for i in args:
- f.write("%s INFO %s" % (time.asctime(), repr(i)))
- f.write("\n")
- f.close()
- else:
- global _logger_registry
- # if all loggers have the same name,
- # everything is logged to all files.
- logger_name = os.path.splitext(os.path.basename(filename))[0]
- # only add one handler per logger
- if not logger_name in _logger_registry:
- _logger = logging.getLogger(logger_name)
- hdlr = logging.FileHandler(filename)
- formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
- hdlr.setFormatter(formatter)
- _logger.addHandler(hdlr)
- _logger.setLevel(logging.INFO)
- _logger_registry[logger_name] = _logger
-
- logger = _logger_registry[logger_name]
-
- def log(*args):
- # adjusted to match the 'manual' log func
- for i in args:
- logger.info(repr(i))
-
-
def update_static_entry(cdict, entry_filename):
"""
This is a utility function that allows plugins to easily update
@@ -620,6 +607,261 @@
f.write(response.read())
f.close()
+
+#******************************
+# Logging
+#******************************
+
+class _Logger:
+ """
+ Emulates some functionality of the logging module for older Python installations.
+ """
+ CRITICAL = 50
+ FATAL = CRITICAL
+ ERROR = 40
+ WARNING = 30
+ WARN = WARNING
+ INFO = 20
+ DEBUG = 10
+ NOTSET = 0
+
+ # order matters!
+ _names = ['FATAL', 'CRITICAL', 'ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
+
+ # populated in __init__
+ _levels = {}
+ _level_names = {}
+
+ def __init__(self, filename, level):
+ self._populate_levels()
+ self.filename = filename
+ self.setLevel(level)
+ self.format = "%Y-%m-%d %H:%M:%S"
+ self._file = None
+
+ def _populate_levels(self):
+ for n in self._names:
+ self._levels[n] = getattr(self, n)
+ self._level_names[getattr(self, n)] = n
+
+ def _getLevel(self, level):
+ """
+ Return the numeric representation of logging level 'level'.
+ Defaults to 0 (self.NOTSET).
+ """
+ return self._levels.get(level.upper(), self.NOTSET)
+
+ def _getMessage(self, msg, args):
+ """
+ Return the message for this log record after merging any user-supplied
+ arguments with the message.
+ """
+ import types
+ if not hasattr(types, "UnicodeType"): #if no unicode support...
+ msg = str(msg)
+ else:
+ try:
+ msg = str(msg)
+ except UnicodeError:
+ pass
+ if args:
+ msg = msg % args
+ return msg
+
+ def _getExcInfo(self):
+ import traceback
+ (exc_type, exc_value, tb) = sys.exc_info()
+ exc_file = StringIO()
+ traceback.print_exception(exc_type, exc_value, tb, file=exc_file)
+ exc_string = exc_file.getvalue()
+ return exc_string
+
+ def _log(self, level, msg, args, exc_info=None):
+ if self.level <= level:
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ time_string = "%s,%03d" % (time.strftime(self.format, time.localtime(ct)), msecs)
+ level = self.getLevelName(level)
+ msg = self._getMessage(msg, args)
+
+ if self._file == None or self._file.closed:
+ self._file = open(self.filename, "a")
+ self._file.write("%s %s %s\n" % (time_string, level, msg))
+ if exc_info: self._file.write(self._getExcInfo())
+
+ def cleanup(self):
+ """
+ Closes open log files.
+ """
+ if self._file != None:
+ self._file.close()
+
+ # public methods
+
+ def getLevelName(self, level):
+ """
+ Return the textual representation of logging level 'level'.
+
+ If the level is one of the predefined levels (CRITICAL, ERROR, WARNING,
+ INFO, DEBUG) then you get the corresponding string.
+ Otherwise, the string "Level %s" % level is returned.
+ """
+ return self._level_names.get(level, ("Level %s" % level))
+
+ def setLevel(self, level):
+ if isinstance(level, int):
+ self.level = level
+ else:
+ self.level = self._getLevel(level.upper())
+
+ def critical(self, msg, *args, **kwargs):
+ apply(self._log, (self.CRITICAL, msg, args), kwargs)
+ fatal = critical
+
+ def error(self, msg, *args, **kwargs):
+ apply(self._log, (self.ERROR, msg, args), kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ apply(self._log, (self.WARNING, msg, args), kwargs)
+ warn = warning
+
+ def info(self, msg, *args, **kwargs):
+ apply(self._log, (self.INFO, msg, args), kwargs)
+
+ def debug(self, msg, *args, **kwargs):
+ apply(self._log, (self.DEBUG, msg, args), kwargs)
+
+def getLogger(logname="pyblosxom", level="info", filename=None):
+ """
+ log = tools.getLogger("logname", "info")
+ # this creates a logger that logs to 'logdir'/logname.log
+ log.info("We %s a %s", "have", "good time")
+ log.debug("We %s a %s", "are testing", "new function")
+ log.setLevel(log.ERROR)
+ log.error("We %s a %s", "have", "mysterious problem", exc_info=True)
+
+ or:
+ log = tools.getLogger(filename="/path/to/logfile", level="info")
+
+ @param logname: the name of this logger. used to figure out the logfile
+ @type logname: string
+
+ @param level: the default loglevel
+ @type level: string
+
+ @param filename: the file to log to
+ @type filename: string
+ """
+ if filename:
+ logname = os.path.splitext(os.path.basename(filename))[0]
+ else:
+ filename = "%s%s%s.log" % (_logdir, os.sep, logname)
+ filename = os.path.normpath(filename)
+
+ global _logger_registry
+ try:
+ import logging
+ #raise ImportError, "whatever"
+ if not filename in _logger_registry:
+ int_level = getattr(logging, level.upper())
+ _logger = logging.getLogger(logname)
+ hdlr = logging.FileHandler(filename)
+ hdlr.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
+ _logger.addHandler(hdlr)
+ _logger.setLevel(int_level)
+ for l in ['CRITICAL', 'FATAL', 'ERROR', 'WARNING', 'WARN', 'INFO', 'DEBUG', 'getLevelName']:
+ setattr(_logger, l, getattr(logging, l))
+ _logger_registry[filename] = _logger
+ except ImportError:
+ if not filename in _logger_registry:
+ _logger_registry[filename] = _Logger(filename, level)
+
+ # update loglevel if necessary
+ logger = _logger_registry[filename]
+ new_level = getattr(logger, level.upper())
+ if new_level < logger.level:
+ logger.setLevel(new_level)
+ return logger
+
+def log_exception(logname="error", level="error", filename=None):
+ """
+ Logs a exception. By default to a file named error.log in
+ your 'logdir' directory. Uses /tmp if 'logdir' is not set.
+ You can also pass your own logfile. See below.
+
+ Usage:
+ try:
+ whatever
+ except:
+ import tools
+ tools.log_exception()
+ # or:
+ #tools.log_exception(filename="/path/to/logfile")
+
+ @param logname: the name of this logger. used to figure out the logfile
+ @type logname: string
+
+ @param level: the default loglevel
+ @type level: string
+
+ @param filename: the file to log to
+ @type filename: string
+ """
+ log = getLogger(logname, level, filename)
+ log.error("Exception occured:", exc_info=True)
+
+def log_frame(_log, num):
+ """
+ Logs some info about the calling function/method.
+ Usefull for debugging.
+ Usage:
+ import tools
+ log = tools.getLogger(filename="/path/to/logfile")
+ tool.log_frame(log, 3)
+ tool.log_frame(log, 2)
+ tool.log_frame(log, 1)
+
+ @param _log: instance of a logger
+ @type _log: L{_Logger} or logging module logger
+
+ @param num: index of the frame
+ @type num: int
+ """
+ f = sys._getframe(num)
+ module = f.f_globals["__name__"]
+ filename = f.f_code.co_filename
+ line = f.f_lineno
+ subr = f.f_code.co_name
+ _log.info("module: %s\nfilename: %s\nline: %s\nsubroutine: %s",
+ module, filename, line, subr)
+
+
+def make_logger(filename):
+ """
+ DEPRECATED
+ Use this instead:
+ -->>> log = tools.getLogger(filename='/tmp/pybloxom.log')
+ -->>> log.info('log message')
+ -->>> log.error('the programm made %s', 'a bubu')
+ ----------
+
+ Create a logging function called log, which logs to the supplied filename
+ usage is:
+
+ -->>> tools.make_logger('/tmp/pybloxom.log')
+ -->>> tools.log('log message')
+
+ @param filename: the name of a file to log to
+ @type filename: string
+ """
+ global log
+
+ _logger = getLogger(filename=filename)
+
+ def log(*args):
+ for i in args:
+ _logger.info(repr(i))
+
# %<-------------------------
# BEGIN portalocking block from Python Cookbook.