# Perforce Defect Tracking Integration Project # # # LOGGER.PY -- PROGRAM LOGGING CLASSES # # Gareth Rees, Ravenbrook Limited, 2000-10-16 # # # 1. INTRODUCTION # # This Python module implements classes for program logging -- recording # information about the activity of a program. # # Logging is primarily intended to record what the integration does so that # the administrator can: # # 1. undo it in an emergency [Requirements, 67]; # # 2. debug their configuration [Requirements, 63]; # # 3. remove the integration [Requirements, 64]; # # and so that developers can: # # 4. debug modififcations of the system [Requirements, 25]; # # 5. debug their extensions to new defect trackers [Requirements, 21]. # # The intended readership of this document is project developers. # # This document is not confidential. import message import os.path import sys import time import types # 2. ABSTRACT LOGGER CLASS # # "logger" is an abstract class for message logs, providing uniform message # formatting and control of logging based on the priority of messages, but no # actual logging. # # When constructing an instance of this class or a subclass, pass the minimum # priority of message that should be written to the log (e.g., message.DEBUG to # see all debug-level messages, or message.ERROR to only see errors). class logger: # Minimum priority of messages to log. priority = message.INFO def __init__(self, priority = message.INFO): assert isinstance(priority, types.IntType) self.priority = priority # 2.1. Format a message with date # # format_with_date(msg). Format log message and return the message as a # string. Messages are prefixed with the current date and time in UTC and # the message id. See message.py for details of the formatting of message # ids. # # The reason for the existence of this method is that not all logging # methods record the date (in particular, file_logger doesn't). def format_with_date(self, msg): assert isinstance(msg, message.message) date = time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime(time.time())) return "%s %s" % (date, msg) # 2.2 Log a message # # log(msg). Write the message to the log (using the write() method), but # only if its priority is higher that self.priority. def log(self, msg): assert isinstance(msg, message.message) # Higher priorities have lower numbers, hence the sense of this test. if msg.priority <= self.priority: self.write(msg) # 2.4. Write a message to the log # # write(msg). Write the message to the log. There is no implementation in # the logger class (it's an abstract class). Subclasses should provide the # appopriate mechanism. def write(self, msg): assert isinstance(msg, message.message) assert 0 # logger is an abstract class; no implementation of write(). # 3. FILE LOGGER CLASS # # This subclass of logger appends messages to a file stream, or to the standard # output if no file is specified when the instance is created. The output # buffer is flushed after each message is written so that the log can be # recovered even if the program crashes. class file_logger(logger): file = None def __init__(self, file = sys.stdout, priority = message.INFO): assert isinstance(file, types.FileType) logger.__init__(self, priority) self.file = file def write(self, msg): assert isinstance(msg, message.message) self.file.write(self.format_with_date(msg)) self.file.write('\n') self.file.flush() # 4. SYSTEM LOGGER CLASS # # This subclass of logger logs messages to the system log on Unix (using # syslog). On other operating systems, it does nothing. class sys_logger(logger): def __init__(self, priority = message.INFO): logger.__init__(self, priority) import os if os.name == 'posix': import syslog self.syslog = syslog.syslog syslog.openlog('p4dti', syslog.LOG_PID, syslog.LOG_DAEMON) syslog.setlogmask(syslog.LOG_UPTO(priority)) def syslog(self, priority, text): pass # 4.1. Log a message to the system log # # We override the log() method rather than the write() method because we # use syslog's logmask feature instead of checking the priority ourselves. def log(self, msg): assert isinstance(msg, message.message) self.syslog(msg.priority, str(msg)) # 5. LOGGER CLASS FOR MULTIPLE LOGS # # This is a meta-logger that writes the message to each of a list of other # loggers. This is so that the administrator can arrange for messages to go to # several places in the integration configuration [AG, 5.1]. class multi_logger(logger): loggers = [] def __init__(self, loggers = [], priority = message.INFO): assert isinstance(loggers, types.ListType) for l in loggers: assert(isinstance(l, logger)) logger.__init__(self, priority) self.loggers = loggers def log(self, msg): assert isinstance(msg, message.message) for l in self.loggers: l.log(msg) # 6. WINDOWS EVENT LOGGER CLASS class win32_event_logger(logger): # Application name. application = None # Map from message priority to Windows event type. event_type = None def __init__(self, rid, priority = message.INFO): logger.__init__(self, priority) self.application = "P4DTI-" + rid # The "Event Message File" is eventlog.dll, in the same # directory as the message.py module. emf = os.path.join(os.path.dirname(os.path.abspath(message.__file__)), "eventlog.dll") import win32evtlogutil win32evtlogutil.AddSourceToRegistry(self.application, emf) import win32evtlog self.event_type = { message.EMERG: win32evtlog.EVENTLOG_ERROR_TYPE, message.ALERT: win32evtlog.EVENTLOG_ERROR_TYPE, message.CRIT: win32evtlog.EVENTLOG_ERROR_TYPE, message.ERR: win32evtlog.EVENTLOG_ERROR_TYPE, message.WARNING: win32evtlog.EVENTLOG_WARNING_TYPE, message.NOTICE: win32evtlog.EVENTLOG_WARNING_TYPE, message.INFO: win32evtlog.EVENTLOG_INFORMATION_TYPE, message.DEBUG: win32evtlog.EVENTLOG_INFORMATION_TYPE, } def log(self, msg): assert isinstance(msg, message.message) # Higher priorities have lower numbers, hence the sense of this test. if msg.priority <= self.priority: import win32evtlogutil win32evtlogutil.ReportEvent(self.application, 0, 0, self.event_type[msg.priority], [str(msg)]) # A. REFERENCES # # [AG] "Perforce Defect Tracking Integration Administrator's Guide" (living # document); Richard Brooksby; Ravenbrook Limited; 2000-08-10. # # [Requirements] "Perforce Defect Tracking Integration Project Requirements" # (living document); Gareth Rees; Ravenbrook Limited; 2000-05-24; # . # # # B. DOCUMENT HISTORY # # 2000-10-16 GDR Created. # # 2000-11-30 GDR Added some type checking. # # 2001-01-19 NB Added sys_logger. # # 2001-01-26 NB Removed extra \n. # # 2001-03-01 NB Fix for job000237. # # 2001-03-02 RB Transferred copyright to Perforce under their license. # # 2001-03-11 GDR Formatted as a document. Uses the message class. Check digit # computation moved to message.py. Loggers pay attention to message priority. # # 2001-04-10 NB job000292: Added call to str() as our message objects # were upsetting syslog.syslog. # # 2001-09-12 GDR Added logger class for the Windows event log. # # # C. COPYRIGHT AND LICENCE # # This file is copyright (c) 2001 Perforce Software, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # $Id: //info.ravenbrook.com/project/p4dti/branch/2001-08-07/migrate-bugzilla/code/replicator/logger.py#3 $