##// END OF EJS Templates
logtoprocess: sends the canonical command name to the subprocess...
logtoprocess: sends the canonical command name to the subprocess One of the use-case of logtoprocess is to monitor command duration. With the current code, we only get whatever command name the user typed (either abbreviated or aliased). This makes analytics on the collected data more difficult. Stores the canonical command name in the request object. Pass the stored canonical name in the `req.ui.log("commandfinish", ...)` call as keyword argument to not break potential string formatting. Pass the value as the environment variable named `LTP_COMMAND` to the called script. Differential Revision: https://phab.mercurial-scm.org/D4820

File last commit:

r40331:fa88170c default
r40438:106adc26 default
Show More
blackbox.py
257 lines | 7.3 KiB | text/x-python | PythonLexer
Bryan O'Sullivan
blackbox: fix copyright
r18676 # blackbox.py - log repository events to a file for post-mortem debugging
Durham Goode
blackbox: adds a blackbox extension...
r18669 #
Bryan O'Sullivan
blackbox: fix copyright
r18676 # Copyright 2010 Nicolas Dumazet
Durham Goode
blackbox: adds a blackbox extension...
r18669 # 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.
timeless
blackbox: log dirty state...
r28246
Takumi IINO
blackbox: fix literal block syntax
r19162 Examples::
Durham Goode
blackbox: adds a blackbox extension...
r18669
[blackbox]
track = *
timeless
blackbox: rewrite dirty documentation noting it is expensive
r28303 # dirty is *EXPENSIVE* (slow);
# each log entry indicates `+` if the repository is dirty, like :hg:`id`.
timeless
blackbox: log dirty state...
r28246 dirty = True
timeless
blackbox: optionally log event source
r28305 # record the source of log messages
logsource = True
Durham Goode
blackbox: adds a blackbox extension...
r18669
[blackbox]
track = command, commandfinish, commandexception, exthook, pythonhook
[blackbox]
track = incoming
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 [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
Durham Goode
blackbox: adds a blackbox extension...
r18669 """
Gregory Szorc
blackbox: use absolute_import
r28090 from __future__ import absolute_import
import errno
import re
Durham Goode
blackbox: adds a blackbox extension...
r18669 from mercurial.i18n import _
timeless
blackbox: log working directory version...
r28245 from mercurial.node import hex
Gregory Szorc
blackbox: use absolute_import
r28090 from mercurial import (
Gregory Szorc
py3: cast error message to localstr in blackbox.py...
r35685 encoding,
Gregory Szorc
global: use pycompat.xrange()...
r38806 pycompat,
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 registrar,
timeless
blackbox: properly replace ui class...
r28248 ui as uimod,
Gregory Szorc
blackbox: use absolute_import
r28090 util,
)
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 from mercurial.utils import (
dateutil,
procutil,
)
Durham Goode
blackbox: adds a blackbox extension...
r18669
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 testedwith = 'ships-with-hg-core'
blackbox: minor code reordering...
r33129
cmdtable = {}
command = registrar.command(cmdtable)
configitems: register 'blackbox.maxsize' as an example of 'configbytes'...
r33130 configtable = {}
configitem = registrar.configitem(configtable)
configitems: register the 'blackbox.dirty' config
r33186 configitem('blackbox', 'dirty',
default=False,
)
configitems: register 'blackbox.maxsize' as an example of 'configbytes'...
r33130 configitem('blackbox', 'maxsize',
blackbox: use a human readable version of the default...
r33131 default='1 MB',
configitems: register 'blackbox.maxsize' as an example of 'configbytes'...
r33130 )
configitems: register the 'blackbox.logsource' config
r33187 configitem('blackbox', 'logsource',
default=False,
)
Boris Feld
configitems: register the 'blackbox.maxfiles' config
r34746 configitem('blackbox', 'maxfiles',
default=7,
)
Boris Feld
configitems: register the 'blackbox.track' config
r34518 configitem('blackbox', 'track',
Boris Feld
configitems: fix registration for 'blackbox.track' config...
r34584 default=lambda: ['*'],
Boris Feld
configitems: register the 'blackbox.track' config
r34518 )
configitems: register 'blackbox.maxsize' as an example of 'configbytes'...
r33130
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 lastui = None
Durham Goode
blackbox: adds a blackbox extension...
r18669
Jun Wu
blackbox: move _openlogfile to a separate method...
r34301 def _openlogfile(ui, vfs):
def rotate(oldpath, newpath):
try:
vfs.unlink(newpath)
except OSError as err:
if err.errno != errno.ENOENT:
ui.debug("warning: cannot remove '%s': %s\n" %
(newpath, err.strerror))
try:
if newpath:
vfs.rename(oldpath, newpath)
except OSError as err:
if err.errno != errno.ENOENT:
ui.debug("warning: cannot rename '%s' to '%s': %s\n" %
(newpath, oldpath, err.strerror))
maxsize = ui.configbytes('blackbox', 'maxsize')
name = 'blackbox.log'
if maxsize > 0:
try:
st = vfs.stat(name)
except OSError:
pass
else:
if st.st_size >= maxsize:
path = vfs.join(name)
Boris Feld
configitems: register the 'blackbox.maxfiles' config
r34746 maxfiles = ui.configint('blackbox', 'maxfiles')
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(maxfiles - 1, 1, -1):
Jun Wu
blackbox: move _openlogfile to a separate method...
r34301 rotate(oldpath='%s.%d' % (path, i - 1),
newpath='%s.%d' % (path, i))
rotate(oldpath=path,
newpath=maxfiles > 0 and path + '.1')
return vfs(name, 'a')
Durham Goode
blackbox: adds a blackbox extension...
r18669 def wrapui(ui):
class blackboxui(ui.__class__):
Jun Wu
blackbox: remove _bbvfs state...
r34112 @property
def _bbvfs(self):
Jun Wu
blackbox: do not prevent 'chg init' from working...
r34300 vfs = None
Jun Wu
blackbox: remove _bbvfs state...
r34112 repo = getattr(self, '_bbrepo', None)
if repo:
Jun Wu
blackbox: do not prevent 'chg init' from working...
r34300 vfs = repo.vfs
if not vfs.isdir('.'):
vfs = None
return vfs
Jun Wu
blackbox: remove _bbvfs state...
r34112
Durham Goode
blackbox: adds a blackbox extension...
r18669 @util.propertycache
def track(self):
Boris Feld
configitems: register the 'blackbox.track' config
r34518 return self.configlist('blackbox', 'track')
Durham Goode
blackbox: adds a blackbox extension...
r18669
Joerg Sonnenberger
blackbox: if --debug is used, also trace ui.debug() calls...
r35742 def debug(self, *msg, **opts):
super(blackboxui, self).debug(*msg, **opts)
if self.debugflag:
Pulkit Goyal
blackbox: don't unpack the list while passing into str.join()...
r35745 self.log('debug', '%s', ''.join(msg))
Joerg Sonnenberger
blackbox: if --debug is used, also trace ui.debug() calls...
r35742
Durham Goode
blackbox: adds a blackbox extension...
r18669 def log(self, event, *msg, **opts):
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 global lastui
Durham Goode
blackbox: adds a blackbox extension...
r18669 super(blackboxui, self).log(event, *msg, **opts)
if not '*' in self.track and not event in self.track:
return
Jun Wu
blackbox: do not cache file objects...
r34111 if self._bbvfs:
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ui = self
Durham Goode
blackbox: adds a blackbox extension...
r18669 else:
# certain ui instances exist outside the context of
# a repo, so just default to the last blackbox that
# was seen.
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ui = lastui
Durham Goode
blackbox: adds a blackbox extension...
r18669
Jun Wu
blackbox: do not cache file objects...
r34111 if not ui:
timeless
blackbox: guard against recursion from dirty check
r28407 return
Jun Wu
blackbox: do not prevent 'chg init' from working...
r34300 vfs = ui._bbvfs
if not vfs:
return
Jun Wu
blackbox: simplify ui states...
r34275 repo = getattr(ui, '_bbrepo', None)
if not lastui or repo:
timeless
blackbox: guard against recursion from dirty check
r28407 lastui = ui
Jun Wu
blackbox: simplify ui states...
r34275 if getattr(ui, '_bbinlog', False):
Jun Wu
blackbox: do not cache file objects...
r34111 # recursion and failure guard
timeless
blackbox: guard against recursion from dirty check
r28407 return
Jun Wu
blackbox: unindent a try block...
r34276 ui._bbinlog = True
default = self.configdate('devel', 'default-date')
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 date = dateutil.datestr(default, '%Y/%m/%d %H:%M:%S')
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 user = procutil.getuser()
pid = '%d' % procutil.getpid()
Jun Wu
blackbox: unindent a try block...
r34276 formattedmsg = msg[0] % msg[1:]
rev = '(unknown)'
changed = ''
if repo:
ctx = repo[None]
parents = ctx.parents()
rev = ('+'.join([hex(p.node()) for p in parents]))
if (ui.configbool('blackbox', 'dirty') and
ctx.dirty(missing=True, merge=False, branch=False)):
changed = '+'
if ui.configbool('blackbox', 'logsource'):
src = ' [%s]' % event
else:
src = ''
timeless
blackbox: guard against recursion from dirty check
r28407 try:
Jun Wu
blackbox: unindent a try block...
r34276 fmt = '%s %s @%s%s (%s)%s> %s'
args = (date, user, rev, changed, pid, src, formattedmsg)
Jun Wu
blackbox: move _openlogfile to a separate method...
r34301 with _openlogfile(ui, vfs) as fp:
Jun Wu
blackbox: unindent a try block...
r34276 fp.write(fmt % args)
except (IOError, OSError) as err:
self.debug('warning: cannot write to blackbox.log: %s\n' %
Gregory Szorc
py3: cast error message to localstr in blackbox.py...
r35685 encoding.strtolocal(err.strerror))
Jun Wu
blackbox: unindent a try block...
r34276 # do not restore _bbinlog intentionally to avoid failed
# logging again
else:
ui._bbinlog = False
Durham Goode
blackbox: adds a blackbox extension...
r18669
def setrepo(self, repo):
timeless
blackbox: properly replace ui class...
r28248 self._bbrepo = repo
Durham Goode
blackbox: adds a blackbox extension...
r18669
ui.__class__ = blackboxui
timeless
blackbox: properly replace ui class...
r28248 uimod.ui = blackboxui
Durham Goode
blackbox: adds a blackbox extension...
r18669
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
Durham Goode
blackbox: fix blackbox causing exceptions in tests...
r19230 if util.safehasattr(ui, 'setrepo'):
ui.setrepo(repo)
Jun Wu
blackbox: set lastui even if ui.log is not called (issue5518)...
r34277
# Set lastui even if ui.log is not called. This gives blackbox a
# fallback place to log.
global lastui
if lastui is None:
lastui = ui
Boris Feld
repovfs: add a ward to check if locks are properly taken...
r33436 repo._wlockfreeprefix.add('blackbox.log')
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673
Rodrigo Damazio
help: adding a proper declaration for shortlist/basic commands (API)...
r40331 @command('blackbox',
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673 [('l', 'limit', 10, _('the number of events to show')),
],
rdamazio@google.com
help: assigning categories to existing commands...
r40329 _('hg blackbox [OPTION]...'),
Rodrigo Damazio
help: adding a proper declaration for shortlist/basic commands (API)...
r40331 helpcategory=command.CATEGORY_MAINTENANCE,
helpbasic=True)
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673 def blackbox(ui, repo, *revs, **opts):
'''view the recent repository events
'''
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 if not repo.vfs.exists('blackbox.log'):
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673 return
Pulkit Goyal
py3: handle keyword arguments in hgext/blackbox.py...
r34973 limit = opts.get(r'limit')
timeless
blackbox: rename fp variable
r28244 fp = repo.vfs('blackbox.log', 'r')
lines = fp.read().split('\n')
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673
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)))