Mercurial > hg
view hgext/blackbox.py @ 20217:33394f2e331e
revlog: move file writing to a separate function
Moves the code that actually writes to a file to a separate function in
revlog.py. This allows extensions to intercept and use the data being written to
disk. For example, an extension might want to replicate these writes elsewhere.
When cloning the Mercurial repo on /dev/shm with --pull, I see about a 0.3% perf change.
It goes from 28.2 to 28.3 seconds.
author | Durham Goode <durham@fb.com> |
---|---|
date | Tue, 26 Nov 2013 12:58:27 -0800 |
parents | 6d6f148cada9 |
children | 7cc77030c557 |
line wrap: on
line source
# blackbox.py - log repository events to a file for post-mortem debugging # # Copyright 2010 Nicolas Dumazet # Copyright 2013 Facebook, Inc. # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """log repository events to a blackbox for debugging Logs event information to .hg/blackbox.log to help debug and diagnose problems. The events that get logged can be configured via the blackbox.track config key. Examples:: [blackbox] track = * [blackbox] track = command, commandfinish, commandexception, exthook, pythonhook [blackbox] track = incoming [blackbox] # limit the size of a log file maxsize = 1.5 MB # rotate up to N log files when the current one gets too big maxfiles = 3 """ from mercurial import util, cmdutil from mercurial.i18n import _ import errno, os, re cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'internal' lastblackbox = None def wrapui(ui): class blackboxui(ui.__class__): @util.propertycache def track(self): return self.configlist('blackbox', 'track', ['*']) def _openlogfile(self): def rotate(oldpath, newpath): try: os.unlink(newpath) except OSError, err: if err.errno != errno.ENOENT: self.debug("warning: cannot remove '%s': %s\n" % (newpath, err.strerror)) try: if newpath: os.rename(oldpath, newpath) except OSError, err: if err.errno != errno.ENOENT: self.debug("warning: cannot rename '%s' to '%s': %s\n" % (newpath, oldpath, err.strerror)) fp = self._bbopener('blackbox.log', 'a') maxsize = self.configbytes('blackbox', 'maxsize', 1048576) if maxsize > 0: st = os.fstat(fp.fileno()) if st.st_size >= maxsize: path = fp.name fp.close() maxfiles = self.configint('blackbox', 'maxfiles', 7) for i in xrange(maxfiles - 1, 1, -1): rotate(oldpath='%s.%d' % (path, i - 1), newpath='%s.%d' % (path, i)) rotate(oldpath=path, newpath=maxfiles > 0 and path + '.1') fp = self._bbopener('blackbox.log', 'a') return fp def log(self, event, *msg, **opts): global lastblackbox super(blackboxui, self).log(event, *msg, **opts) if not '*' in self.track and not event in self.track: return if util.safehasattr(self, '_blackbox'): blackbox = self._blackbox elif util.safehasattr(self, '_bbopener'): try: self._blackbox = self._openlogfile() except (IOError, OSError), err: self.debug('warning: cannot write to blackbox.log: %s\n' % err.strerror) del self._bbopener self._blackbox = None blackbox = self._blackbox else: # certain ui instances exist outside the context of # a repo, so just default to the last blackbox that # was seen. blackbox = lastblackbox if blackbox: date = util.datestr(None, '%Y/%m/%d %H:%M:%S') user = util.getuser() formattedmsg = msg[0] % msg[1:] try: blackbox.write('%s %s> %s' % (date, user, formattedmsg)) except IOError, err: self.debug('warning: cannot write to blackbox.log: %s\n' % err.strerror) lastblackbox = blackbox def setrepo(self, repo): self._bbopener = repo.opener ui.__class__ = blackboxui def uisetup(ui): wrapui(ui) def reposetup(ui, repo): # During 'hg pull' a httppeer repo is created to represent the remote repo. # It doesn't have a .hg directory to put a blackbox in, so we don't do # the blackbox setup for it. if not repo.local(): return if util.safehasattr(ui, 'setrepo'): ui.setrepo(repo) @command('^blackbox', [('l', 'limit', 10, _('the number of events to show')), ], _('hg blackbox [OPTION]...')) def blackbox(ui, repo, *revs, **opts): '''view the recent repository events ''' if not os.path.exists(repo.join('blackbox.log')): return limit = opts.get('limit') blackbox = repo.opener('blackbox.log', 'r') lines = blackbox.read().split('\n') count = 0 output = [] for line in reversed(lines): if count >= limit: break # count the commands by matching lines like: 2013/01/23 19:13:36 root> if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line): count += 1 output.append(line) ui.status('\n'.join(reversed(output)))