Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 184 lines (159 sloc) 6.648 kB
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 #----------------------------
4 # Name: logrotate.py
5 # Python Script
6 # Author: Raymond Wagner
7 # Purpose
8 # This python script is intended to help manage the new logging format
9 # introduced to MythTV in version 0.25. Each instance of each application
10 # run generates a new log file, meaning log rotation applications do not
11 # know to treat them as a group.
12 # This script treats each application individually, and will delete
13 # anything beyond a minimum number of log sets that have aged beyond the
14 # minimum number of days. One log set is one instance of the application,
15 # and however many rotated files have been generated for it.
16 #----------------------------
17
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
18 from __future__ import print_function
19
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
20 import os
21 import re
22 import sys
23 from datetime import datetime, timedelta
24 from optparse import OptionParser
25
26 class LogFile( object ):
362a42a @wagnerrp Update logcleanup for better filename parsing
wagnerrp authored
27 _re = re.compile("(?P<appname>[a-z]*(\.[a-z]+)?).(?P<date>[0-9]{14}).(?P<pid>[0-9]{1,6}).log((?P<sequence>.[0-9]+)\.(?P<compression>[a-zA-Z0-9]+)?)?")
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
28 path = None
29 application = None
30 datetime = None
31 pid = None
32 compressed = False
33 sequence = None
34
35 @classmethod
36 def filter(cls, path, filelist):
362a42a @wagnerrp Update logcleanup for better filename parsing
wagnerrp authored
37 return [cls(path, f) for f in filelist if cls._re.match(f)]
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
38
39 def __init__(self, path, filename):
40 self.path = path
41 self.filename = filename
42 self.lastmod = datetime.fromtimestamp(os.stat(
43 os.path.join(self.path, self.filename)).st_mtime)
44
362a42a @wagnerrp Update logcleanup for better filename parsing
wagnerrp authored
45 m = self._re.match(filename)
46 self.application = m.group('appname')
47 self.datetime = datetime.strptime(m.group('date'), "%Y%m%d%H%M%S")
48 self.pid = int(m.group('pid'))
49 if m.group('sequence'):
50 self.sequence = int(m.group('sequence'))
51 if m.group('compression'):
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
52 self.compressed = True
53 self.children = []
54
55 def __repr__(self):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
56 return "<LogFile {0}, {1}{2}{3}>".format(
57 self.application, self.datetime.strftime("%b %d, %H:%M"),
58 " #{0}".format(self.sequence) if self.sequence is not None else "",
59 " (compressed)" if self.compressed else "")
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
60
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
61 def __lt__(self, other):
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
62 if self.application != other.application:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
63 return (self.application < other.application)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
64 if self.datetime != other.datetime:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
65 return (self.datetime < other.datetime)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
66 if self.pid != other.pid:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
67 return (self.pid < other.pid)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
68
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
69 if self.sequence == other.sequence:
70 return False
71 if self.sequence is None:
72 return True
73 if other.sequence is None:
74 return False
75 return (self.sequence < other.sequence)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
76
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
77 def __gt__(self, other):
78 if self.application != other.application:
79 return (self.application > other.application)
80 if self.datetime != other.datetime:
81 return (self.datetime > other.datetime)
82 if self.pid != other.pid:
83 return (self.pid > other.pid)
84
85 if self.sequence == other.sequence:
86 return False
87 if self.sequence is None:
88 return False
89 if other.sequence is None:
90 return True
91 return (self.sequence > other.sequence)
92
93 def __eq__(self, other):
94 return (self.application == other.application) and \
95 (self.datetime == other.datetime) and \
96 (self.pid == other.pid) and \
97 (self.sequence == other.sequence)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
98
99 def append(self, child):
100 self.children.append(child)
101
102 def delete(self):
103 for child in self.children:
104 child.delete()
105 os.unlink(os.path.join(self.path, self.filename))
106
107 def deletelogs(instances, opts):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
108 deletelist = []
109 instances.sort(reverse=True)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
110 while len(instances) > int(opts.minfiles):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
111 cur = instances.pop()
112 if instances[-1].lastmod < (datetime.now() -\
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
113 timedelta(hours=24*int(opts.minage))):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
114 deletelist.append(cur)
115 if len(deletelist):
116 print("Deleting {0} log sets ({1} files) for {2}".format(
117 len(deletelist),
118 sum([len(item.children)+1 for item in deletelist]),
119 deletelist[0].application))
120 for item in deletelist:
121 # print(" deleting {0}".format(item))
122 item.delete()
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
123
124 def main(opts):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
125 ls = LogFile.filter(opts.logpath, os.listdir(opts.logpath))
126 ls.sort(reverse=True)
127
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
128 if len(ls) == 0:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
129 print("Warning: Empty log path!")
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
130 sys.exit(1)
131
132 cur = None
133 while len(ls):
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
134 f = ls.pop()
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
135 if cur is None:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
136 # first run of a new application name
137 # start collecting instances
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
138 instances = [f]
139 cur = f
140 continue
141
142 if cur.application != f.application:
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
143 # new application name, run existing instances and restart loop
144 ls.append(f)
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
145 cur = None
146 deletelogs(instances, opts)
147 continue
148
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
149 if (cur.datetime != f.datetime) or (cur.pid != f.pid):
150 # new instance of existing application
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
151 cur = f
152 instances.append(f)
153 continue
154
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
155 # logrotate copy of current instance
156 # mark as child for collective handling
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
157 cur.append(f)
158
159 deletelogs(instances, opts)
160
161 if __name__ == "__main__":
2caa47f @wagnerrp Big round of ebuild updates.
wagnerrp authored
162 try:
163 from argparse import ArgumentParser
164 except ImportError:
165 parser = OptionParser()
166 parser.add_option("-p", "--path", dest="logpath", default="/var/log/mythtv",
167 help="Path where log files are stored")
168 parser.add_option("-n", "--min-files", dest="minfiles", default="5",
169 help="Minimum number of logs per application to keep")
170 parser.add_option("-t", "--min-age", dest="minage", default="7",
171 help="Minimum time (days) to keep log files")
172 (opts, args) = parser.parse_args()
173 else:
174 parser = ArgumentParser()
175 parser.add_argument('-p', "--path", dest="logpath", default="/var/log/mythtv",
176 help="Path where log files are stored")
177 parser.add_argument('-n', "--min-files", dest="minfiles", type=int, default=5,
178 help="Minimum number of logs per application to keep")
179 parser.add_argument("-t", "--min-age", dest="minage", type=int, default=7,
180 help="Minimum time (days) to keep log files")
181 opts = parser.parse_args()
182
0262158 @wagnerrp Bump 0.24.1 and 0.25 ebuilds
wagnerrp authored
183 main(opts)
Something went wrong with that request. Please try again.