Skip to content

Commit

Permalink
Add a logcleanup.py to manage deletion of logs
Browse files Browse the repository at this point in the history
This script maintains a minimum of five sets of logs from each
application and a minimum of seven days of storage for each set from its
last modification date.  This allows traditional log rotate mechanisms
to continue operation, while preventing a buildup of old log sets from
previous application runs.
  • Loading branch information
wagnerrp committed Sep 22, 2011
1 parent c6162db commit 724b4e4
Showing 1 changed file with 127 additions and 0 deletions.
127 changes: 127 additions & 0 deletions mythtv/contrib/maintenance/logcleanup.py
@@ -0,0 +1,127 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#----------------------------
# Name: logrotate.py
# Python Script
# Author: Raymond Wagner
# Purpose
# This python script is intended to help manage the new logging format
# introduced to MythTV in version 0.25. Each instance of each application
# run generates a new log file, meaning log rotation applications do not
# know to treat them as a group.
# This script treats each application individually, and will delete
# anything beyond a minimum number of log sets that have aged beyond the
# minimum number of days. One log set is one instance of the application,
# and however many rotated files have been generated for it.
#----------------------------

import os
import sys
from datetime import datetime, timedelta
from optparse import OptionParser

class LogFile( object ):
path = None
application = None
datetime = None
pid = None
compressed = False
sequence = None

def __init__(self, path, filename):
self.path = path
self.filename = filename
self.lastmod = datetime.fromtimestamp(os.stat(
os.path.join(self.path, self.filename)).st_mtime)

s = filename.split('.')
self.application = s[0]
self.datetime = datetime.strptime(s[1], "%Y%m%d%H%M%S")
self.pid = int(s[2])
if len(s) > 4:
self.sequence = int(s[4])
if len(s) > 5:
self.compressed = True
self.children = []

def __repr__(self):
return "<LogFile %s, %s%s%s>" % \
(self.application, self.datetime.strftime("%b %d, %H:%M"),
" #%d" % self.sequence if self.sequence else "",
"(compressed)" if self.compressed else "")

def __cmp__(self, other):
if self.application != other.application:
return cmp(self.application, other.application)

if self.datetime != other.datetime:
return cmp(self.datetime, other.datetime)

if self.pid != other.pid:
return cmp(self.pid, other.pid)

if self.sequence != other.sequence:
if self.sequence is None:
return -1
if other.sequence is None:
return 1
return cmp(self.sequence, other.sequence)

return 0

def append(self, child):
self.children.append(child)

def delete(self):
for child in self.children:
child.delete()
#print 'deleting %s' % os.path.join(self.path, self.filename)
os.unlink(os.path.join(self.path, self.filename)

def deletelogs(instances, opts):
while len(instances) > int(opts.minfiles):
if instances[0].lastmod > (datetime.now() -\
timedelta(hours=24*int(opts.minage))):
return
instances.pop(0).delete()

def main(opts):
ls = sorted([LogFile(opts.logpath, f) for f in os.listdir(opts.logpath)])
if len(ls) == 0:
print "Warning: Empty log path!"
sys.exit(1)

cur = None
while len(ls):
f = ls.pop(0)
if cur is None:
instances = [f]
cur = f
continue

if cur.application != f.application:
ls.insert(0,f)
cur = None
deletelogs(instances, opts)
continue

if (cur.datetime != f.datetime) and (cur.pid != f.pid):
cur = f
instances.append(f)
continue

cur.append(f)

deletelogs(instances, opts)

if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-p", "--path", dest="logpath", default="/var/log/mythtv",
help="Path where log files are stored")
parser.add_option("-n", "--min-files", dest="minfiles", default="5",
help="Minimum number of logs per application to keep")
parser.add_option("-t", "--min-age", dest="minage", default="7",
help="Minimum time (days) to keep log files")

(opts, args) = parser.parse_args()
main(opts)

0 comments on commit 724b4e4

Please sign in to comment.