Welcome, guest | Sign In | My Account | Store | Cart

Provides a class for creating and manipulating plain-text logfiles. MD5 checksums are used to verify the integrity of logfiles between sessions. A file of metadata for each log is also maintained. Includes an example of how to create and use a logpyl object.

Python, 220 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
class logpyl:
    """The logpyl class implements basic logging functionality for Python programs.

    A logpyl log consists of three files.  The log file contains the events logged
    to a logpyl object.  The metadata file contains information about the log, such
    as creation date, modification date, and name.  The checksum file contains an MD5
    hash created from the log and metadata files, and is used to check the integrity
    of log files."""

    def __init__(self,path='logs',name='NewLog',debug='off'):
        "Initialize or open logs as log objects are instantiated."
        import sys        
        import os.path
        self.events = []    # list of events written to this log
        self.debug = debug  # flag to activate/deactive debugging messages

        # First try the metadata file
        self.metadatafile = name + '.lmd'
        self.mdf = path + '/' + self.metadatafile
        mfn = os.path.isfile(self.mdf)
        if ( mfn ):
            if ( debug == 'on' ):
                print 'DEBUG: Metadata file',self.metadatafile,'exists.'
            # Since the file exists, get the metadata
            mfn = open(self.mdf,'r')
            self.name = mfn.readline().strip()
            self.path = mfn.readline().strip()
            self.logfile = mfn.readline().strip()
            self.metadatafile = mfn.readline().strip()
            self.checksumfile = mfn.readline().strip()
            self.created = mfn.readline().strip()
            self.modified = mfn.readline().strip()
            mfn.close()
            self.ldf = path + '/' + self.logfile
            self.cdf = path + '/' + self.checksumfile            
        else:
            if ( debug == 'on' ):
                print 'DEBUG: Metadata file',metadatafile,'does not exist.'
            self.name = name
            self.path = path
            self.metadatafile = name + '.lmd'
            self.logfile = name + '.log'
            self.checksumfile = name + '.md5'
            import time
            self.created = time.asctime(time.localtime(time.time()))
            self.mdf = path + '/' + self.metadatafile
            self.ldf = path + '/' + self.logfile
            self.cdf = path + '/' + self.checksumfile            
        
        # Then try the log file
        lfn = os.path.isfile(self.ldf)
        if ( lfn ):
            if ( debug == 'on' ):
                print 'DEBUG: Log file',self.logfile,'exists.'
            lfn = open(self.ldf,'r')
            for line in lfn.readlines():
                self.events.append(line.strip())
            lfn.close()
        else:
            if ( debug == 'on' ):
                print 'DEBUG: Log file',self.logfile,'does not exist.'

        # Finally, try the checksum file
        cfn = os.path.isfile(self.cdf)
        if ( cfn ):
            if ( debug == 'on' ):
                print 'DEBUG: Checksum file',self.checksumfile,'exists.'            
            cfn = open(self.cdf, 'r')
            self.md5 = cfn.read().strip()
            if ( debug == 'on' ):
                print 'DEBUG: MD5 checksum',self.md5,'read from',self.checksumfile
            cfn.close()
        else:
            if ( debug == 'on' ):
                print 'DEBUG: Checksum file',self.checksumfile,'does not exist.'
            pass

        # Once we have read the metadata, verify the integrity of the logfiles.
        self.verify()
            
    def add(self, eventclass="note", message="Your message here"):
        "Compose a log entry from the elements passed to add() and append it to the list of events."
        import time
        event = self.datetime() + ' ' + eventclass + ' ' + message
        if ( self.debug == "on" ):
            print 'DEBUG: Adding', event, 'to log', self.name
        self.modified = time.asctime(time.localtime(time.time()))            
        self.events.append(event)
        return

    def close(self):
        "Close the log by writing all log and metadata to the proper files.  Also update the checksum file."
        import sys
        import os
        "Write the current version of the log to a file and free the variables used by the log."
        if ( self.debug == 'on' ):            
            print "DEBUG: Closing log", self.name
        # If self.path does not exist, create the directory for the logfiles.
        if ( not os.path.exists(self.path ) ):
            if ( self.debug == 'on' ):
                print 'DEBUG: Directory ',self.path,' does not exist.  I am creating it now.'
            try:                
                os.makedirs(self.path)
                if ( self.debug == 'on' ):
                    print 'DEBUG: Created log file directory',self.path
            except OSERROR:
                print 'ERROR: Could not create log file directory',self.path                
        # Make sure that the metadata file is opened (created) and written.
        import time
        mfn = open(self.mdf, 'w+')
        mfn.write(self.name+'\n')
        mfn.write(self.path+'\n')
        mfn.write(self.logfile+'\n')
        mfn.write(self.metadatafile+'\n')        
        mfn.write(self.checksumfile+'\n')
        mfn.write(self.created+'\n')
        if ( not hasattr(self,'modified') ):
            mfn.write(self.created+'\n')
        else:
            mfn.write(self.modified+'\n')
        mfn.close()

        # Make sure that the log entries are written.
        lfn = open(self.ldf, 'w+')
        for event in self.events:
            lfn.write(event+'\n')
        lfn.close()

        # Create the MD5 checksum from the log file and metadata file
        import md5
        checksum = md5.new()
        mfn = open(self.mdf, 'r')
        for line in mfn.readlines():
            checksum.update(line)
        mfn.close()
        lfn = open(self.ldf, 'r')
        for line in lfn.readlines():
            checksum.update(line)
        lfn.close()
        cs = checksum.hexdigest()        
        if ( self.debug == 'on' ):
            print 'DEBUG: The MD5 digest of the metadata and log files is',cs
        
        # Make sure that the checksum file is opened (created) and written to.
        cfn = open(self.cdf,'w+')        
        cfn.write(cs+'\n')
        cfn.close()

    def datetime(self):
        "Generate the date/time stamp used in our log entries"
        import time
        datestamp = time.asctime(time.localtime(time.time()))
        return datestamp

    def info(self):
        print 'Info about log', self.name, ':'
        print '\tName:', self.name
        print '\tPath:', self.path
        print '\tLog file:', self.logfile
        print '\tMetadata file:', self.metadatafile
        print '\tChecksum file:', self.checksumfile
        if ( hasattr(self,'md5') ):
            print '\t\tMD5 Checksum:',self.md5
        print '\tNo. of entries:', len(self.events)
        if ( hasattr(self,'created') ):
            print '\tCreated:',self.created
        if ( hasattr(self,'modified') ) :
            print '\tModified:',self.modified                

    def printlog(self):
        print '\nPrinting log', self.name
        for event in self.events:
            print event
        print '\n'   

    def verify(self):
        "Compute the MD5 checksum for this log to see if the logfiles have been corrupted."
        # If there is no self.md5, no checksum exists for this log yet...
        if ( not hasattr(self,'md5') ):
            print 'WARNING: No MD5 checksum was found for log',self.name
            print 'WARNING: Log',self.name,'may be newly created, or it may be corrupt!'
            return
        
        # Otherwise, create the MD5 checksum from the log file and metadata file for verification
        import md5
        checksum = md5.new()
        mfn = open(self.mdf, 'r')
        for line in mfn.readlines():
            checksum.update(line)
        mfn.close()
        lfn = open(self.ldf, 'r')
        for line in lfn.readlines():
            checksum.update(line)
        lfn.close()
        cs = checksum.hexdigest()
        if ( self.debug == 'on' ):
            print 'DEBUG: The MD5 digest of the metadata and log files is',cs   
        if ( self.md5 == cs ):
            if ( self.debug == 'on' ):
                print 'DEBUG: The calculated MD5 checksum',cs,'matches the stored MD5 checksum',self.md5
        else:
            if ( self.debug == 'on' ):
                print 'DEBUG: The calculated MD5 checksum',cs,'does not match the stored MD5 checksum',self.md5                
            print 'ERROR: The MD5 checksum for log',self.name,'is inconsistent!'
            print 'ERROR: Log',self.name,'may be corrupt!'                

if __name__ == '__main__':

    # create a new log or open an existing log with debugging turned on
    # (disable debugging messages by passing 'off' as the third parm to logpyl())
    mylog = logpyl('logs','testlog','on')
    # add a couple of events to the log
    mylog.add("spam","Spam is US$1.95 per can.")
    mylog.add("eggs","Eggs are US$0.89 per dozen.")
    # print some summary information about the log
    mylog.info()
    # print the log entries
    mylog.printlog()
    # close the log
    mylog.close()

I wanted to have a basic tool for maintaining a log file for an application during the development process. This class provides such a tool.

The error noted by Sam Spade (see comments) has been corrected.

2 comments

Sam Spade 20 years, 10 months ago  # | flag

Error in line. The following line is incorrect:

print 'DEBUG: Metadata file',metadatafile,'does not exist.'

Instead it should read:

print 'DEBUG: Metadata file',self.metadatafile,'does not exist.'
john cole (author) 20 years, 9 months ago  # | flag

Thank you. Sam - Thanks! I missed that when I was rewriting part of the script. I hope that you found this code useful.