comparison hgext/blackbox.py @ 19066:2cad301a7f06

blackbox: automatically rotate log files If enabled, log rotation prevents the amount of space used by the blackbox log from growing without bound. This becomes important in cases where there are a lot of busy repositories managed by humans and automation on many machines. In large deployments, we cannot reasonably track all the repos where blackbox logs need to be managed, so it is safer to have blackbox manage its own logs than to move responsibility to an external tool such as logrotate. This change adds two configuration keys: * blackbox.maxsize is the maximum allowable size of the current log * blackbox.maxfiles is the number of log files to maintain
author Bryan O'Sullivan <bryano@fb.com>
date Thu, 18 Apr 2013 16:17:59 -0700
parents 63a783d1ac85
children 27013ace80eb
comparison
equal deleted inserted replaced
19065:2c4cd1c42365 19066:2cad301a7f06
19 track = command, commandfinish, commandexception, exthook, pythonhook 19 track = command, commandfinish, commandexception, exthook, pythonhook
20 20
21 [blackbox] 21 [blackbox]
22 track = incoming 22 track = incoming
23 23
24 [blackbox]
25 # limit the size of a log file
26 maxsize = 1.5 MB
27 # rotate up to N log files when the current one gets too big
28 maxfiles = 3
29
24 """ 30 """
25 31
26 from mercurial import util, cmdutil 32 from mercurial import util, cmdutil
27 from mercurial.i18n import _ 33 from mercurial.i18n import _
28 import os, re 34 import errno, os, re
29 35
30 cmdtable = {} 36 cmdtable = {}
31 command = cmdutil.command(cmdtable) 37 command = cmdutil.command(cmdtable)
32 testedwith = 'internal' 38 testedwith = 'internal'
33 lastblackbox = None 39 lastblackbox = None
36 class blackboxui(ui.__class__): 42 class blackboxui(ui.__class__):
37 @util.propertycache 43 @util.propertycache
38 def track(self): 44 def track(self):
39 return self.configlist('blackbox', 'track', ['*']) 45 return self.configlist('blackbox', 'track', ['*'])
40 46
47 def _openlogfile(self):
48 def rotate(oldpath, newpath):
49 try:
50 os.unlink(newpath)
51 except OSError, err:
52 if err.errno != errno.ENOENT:
53 self.debug("warning: cannot remove '%s': %s\n" %
54 (newpath, err.strerror))
55 try:
56 if newpath:
57 os.rename(oldpath, newpath)
58 except OSError, err:
59 if err.errno != errno.ENOENT:
60 self.debug("warning: cannot rename '%s' to '%s': %s\n" %
61 (newpath, oldpath, err.strerror))
62
63 fp = self._bbopener('blackbox.log', 'a')
64 maxsize = self.configbytes('blackbox', 'maxsize', 1048576)
65 if maxsize > 0:
66 st = os.fstat(fp.fileno())
67 if st.st_size >= maxsize:
68 path = fp.name
69 fp.close()
70 maxfiles = self.configint('blackbox', 'maxfiles', 7)
71 for i in xrange(maxfiles - 1, 1, -1):
72 rotate(oldpath='%s.%d' % (path, i - 1),
73 newpath='%s.%d' % (path, i))
74 rotate(oldpath=path,
75 newpath=maxfiles > 0 and path + '.1')
76 fp = self._bbopener('blackbox.log', 'a')
77 return fp
78
41 def log(self, event, *msg, **opts): 79 def log(self, event, *msg, **opts):
42 global lastblackbox 80 global lastblackbox
43 super(blackboxui, self).log(event, *msg, **opts) 81 super(blackboxui, self).log(event, *msg, **opts)
44 82
45 if not '*' in self.track and not event in self.track: 83 if not '*' in self.track and not event in self.track:
47 85
48 if util.safehasattr(self, '_blackbox'): 86 if util.safehasattr(self, '_blackbox'):
49 blackbox = self._blackbox 87 blackbox = self._blackbox
50 elif util.safehasattr(self, '_bbopener'): 88 elif util.safehasattr(self, '_bbopener'):
51 try: 89 try:
52 self._blackbox = self._bbopener('blackbox.log', 'a') 90 self._blackbox = self._openlogfile()
53 except (IOError, OSError), err: 91 except (IOError, OSError), err:
54 self.debug('warning: cannot write to blackbox.log: %s\n' % 92 self.debug('warning: cannot write to blackbox.log: %s\n' %
55 err.strerror) 93 err.strerror)
56 del self._bbopener 94 del self._bbopener
57 self._blackbox = None 95 self._blackbox = None