##// END OF EJS Templates
lfs: improve the hints for common errors in the Batch API...
lfs: improve the hints for common errors in the Batch API The previous message was too debug-ish and less action oriented than a hint should be. The remaining errors that aren't handled are more along the lines of programming errors (not using POST, bad accept type, etc), so I'm not bothering with that. The friendly errors purposely use `self.baseurl` instead of the full Batch API endpoint because I'd expect some copy/paste/modify on the part of the user here, and it would be more confusing if '/objects/batch' magically appeared, but shouldn't be used in the config setting. It still seems like the right thing for debugging in the catchall case.

File last commit:

r40684:85372dc0 default
r40696:9f78d107 default
Show More
blackbox.py
278 lines | 8.0 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
Matt DeVore
blackbox: add configitem for format of log timestamps...
r40466 [blackbox]
# Include nanoseconds in log entries with %f (see Python function
# datetime.datetime.strftime)
date-format = '%Y-%m-%d @ %H:%M:%S.%f'
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 )
Matt DeVore
blackbox: add configitem for format of log timestamps...
r40466 configitem('blackbox', 'date-format',
default='%Y/%m/%d %H:%M:%S',
)
configitems: register 'blackbox.maxsize' as an example of 'configbytes'...
r33130
Yuya Nishihara
blackbox: rename variables to prepare extracting core logic from ui wrapper...
r40679 _lastlogger = 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')
Yuya Nishihara
blackbox: unindent "if True" block
r40681 class blackboxlogger(object):
def __init__(self, ui):
Yuya Nishihara
blackbox: initialize repo attribute properly...
r40682 self._repo = None
Yuya Nishihara
blackbox: initialize inlog flag properly...
r40683 self._inlog = False
Yuya Nishihara
blackbox: extract function to test if log event is tracked...
r40684 self._trackedevents = set(ui.configlist('blackbox', 'track'))
Yuya Nishihara
blackbox: extract logger class from ui wrapper...
r40680
Yuya Nishihara
blackbox: unindent "if True" block
r40681 @property
def _bbvfs(self):
vfs = None
Yuya Nishihara
blackbox: initialize repo attribute properly...
r40682 if self._repo:
vfs = self._repo.vfs
Yuya Nishihara
blackbox: unindent "if True" block
r40681 if not vfs.isdir('.'):
vfs = None
return vfs
Jun Wu
blackbox: remove _bbvfs state...
r34112
Yuya Nishihara
blackbox: extract function to test if log event is tracked...
r40684 def tracked(self, event):
return b'*' in self._trackedevents or event in self._trackedevents
Yuya Nishihara
blackbox: unindent "if True" block
r40681 def log(self, ui, event, msg, opts):
global _lastlogger
Yuya Nishihara
blackbox: extract function to test if log event is tracked...
r40684 if not self.tracked(event):
Yuya Nishihara
blackbox: unindent "if True" block
r40681 return
Durham Goode
blackbox: adds a blackbox extension...
r18669
Yuya Nishihara
blackbox: unindent "if True" block
r40681 if self._bbvfs:
_lastlogger = self
elif _lastlogger and _lastlogger._bbvfs:
# certain logger instances exist outside the context of
# a repo, so just default to the last blackbox logger that
# was seen.
pass
else:
return
_lastlogger._log(ui, event, msg, opts)
Jun Wu
blackbox: do not prevent 'chg init' from working...
r34300
Yuya Nishihara
blackbox: unindent "if True" block
r40681 def _log(self, ui, event, msg, opts):
Yuya Nishihara
blackbox: initialize inlog flag properly...
r40683 if self._inlog:
Yuya Nishihara
blackbox: unindent "if True" block
r40681 # recursion and failure guard
return
Yuya Nishihara
blackbox: initialize inlog flag properly...
r40683 self._inlog = True
Yuya Nishihara
blackbox: unindent "if True" block
r40681 default = ui.configdate('devel', 'default-date')
date = dateutil.datestr(default, ui.config('blackbox', 'date-format'))
user = procutil.getuser()
pid = '%d' % procutil.getpid()
formattedmsg = msg[0] % msg[1:]
rev = '(unknown)'
changed = ''
Yuya Nishihara
blackbox: initialize repo attribute properly...
r40682 ctx = self._repo[None]
Yuya Nishihara
blackbox: unindent "if True" block
r40681 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 = ''
try:
fmt = '%s %s @%s%s (%s)%s> %s'
args = (date, user, rev, changed, pid, src, formattedmsg)
with _openlogfile(ui, self._bbvfs) as fp:
fp.write(fmt % args)
except (IOError, OSError) as err:
ui.debug('warning: cannot write to blackbox.log: %s\n' %
encoding.strtolocal(err.strerror))
Yuya Nishihara
blackbox: initialize inlog flag properly...
r40683 # do not restore _inlog intentionally to avoid failed
Yuya Nishihara
blackbox: unindent "if True" block
r40681 # logging again
else:
Yuya Nishihara
blackbox: initialize inlog flag properly...
r40683 self._inlog = False
Durham Goode
blackbox: adds a blackbox extension...
r18669
Yuya Nishihara
blackbox: unindent "if True" block
r40681 def setrepo(self, repo):
Yuya Nishihara
blackbox: initialize repo attribute properly...
r40682 self._repo = repo
Durham Goode
blackbox: adds a blackbox extension...
r18669
Yuya Nishihara
blackbox: extract logger class from ui wrapper...
r40680 def wrapui(ui):
class blackboxui(ui.__class__):
def __init__(self, src=None):
super(blackboxui, self).__init__(src)
if src and r'_bblogger' in src.__dict__:
self._bblogger = src._bblogger
# trick to initialize logger after configuration is loaded, which
# can be replaced later with blackboxlogger(ui) in uisetup(), where
# both user and repo configurations should be available.
@util.propertycache
def _bblogger(self):
return blackboxlogger(self)
def debug(self, *msg, **opts):
super(blackboxui, self).debug(*msg, **opts)
if self.debugflag:
self.log('debug', '%s', ''.join(msg))
def log(self, event, *msg, **opts):
super(blackboxui, self).log(event, *msg, **opts)
self._bblogger.log(self, event, msg, opts)
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
Yuya Nishihara
blackbox: extract logger class from ui wrapper...
r40680 logger = getattr(ui, '_bblogger', None)
if logger:
Yuya Nishihara
blackbox: rename variables to prepare extracting core logic from ui wrapper...
r40679 logger.setrepo(repo)
Jun Wu
blackbox: set lastui even if ui.log is not called (issue5518)...
r34277
Yuya Nishihara
blackbox: rename variables to prepare extracting core logic from ui wrapper...
r40679 # Set _lastlogger even if ui.log is not called. This gives blackbox a
Jun Wu
blackbox: set lastui even if ui.log is not called (issue5518)...
r34277 # fallback place to log.
Yuya Nishihara
blackbox: rename variables to prepare extracting core logic from ui wrapper...
r40679 global _lastlogger
if _lastlogger is None:
_lastlogger = logger
Jun Wu
blackbox: set lastui even if ui.log is not called (issue5518)...
r34277
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)))