sapling/edenscm/hgext/errorredirect.py

100 lines
3.1 KiB
Python
Raw Normal View History

# errorredirect.py
#
# Copyright 2015 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.
"""redirect error message
Redirect error message, the stack trace, of an uncaught exception to
2016-04-04 22:48:01 +03:00
a custom shell script. This is useful for further handling the error,
for example posting it to a support group and logging it somewhere.
The config option errorredirect.script is the shell script to execute.
If it's empty, the extension will do nothing and fallback to the old
behavior.
Two environment variables are set: TRACE is the stack trace, which
is the same as piped content. WARNING is the warning message, which
usually contains contact message and software versions, etc.
Examples::
[errorredirect]
script = tee hgerr.log && echo 'Error written to hgerr.log'
[errorredirect]
script = echo "$WARNING$TRACE" >&2
[errorredirect]
script = (echo "$WARNING"; cat) | cat >&2
"""
from __future__ import absolute_import
import signal
import subprocess
errorredirect: modify errorredirect extension to send crash log to scuba table Summary: Modify error redirect extension to send crash log, type and traceback to scuba table hgerrors Test Plan: Sample that is being sent to Scuba: ``` { "int":{"time":1498528572}, "normal":{ "host":"devvm26497.prn1", "metrics_type":"hgerrors", "msg":"exception has occurred: ** unknown exception encountered, please report by visiting\n** Source Control @ FB group\n** Python 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]\n** Mercurial Distributed SCM (version 4.2.1+842-79c3f212a9c9)\n** Extensions loaded: tweakdefaults, hgk, absorb, arcdiff, automv, blackbox, chistedit, color, debugcommitmessage, debuginhibit, evolve, fbhistedit, githelp, hiddenerror, histedit, inhibit, journal, logginghelper, lz4revlog, morestatus, moreversion, myparent, pager, patchrmdir, perftweaks, phabdiff, phabstatus, phrevset, pullcreatemarkers, purge, rage, rebase, record, remotefilelog, remotenames, reset, sampling, fbshow, sigtrace, simplecache, smartlog, sshaskpass, strip, directaccess, uncommit, conflictinfo, extorder, obsshelve, sqldirstate, fastmanifest, hgsubversion, traceprof, dialect, grpcheck, errorredirect, fbamend, pushvars, pushrebase\n", "traceback":"Traceback (most recent call last):\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 326, in _callcatch\n return scmutil.callcatch(ui, func)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/scmutil.py\", line 145, in callcatch\n return func()\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 308, in _runcatchfunc\n return _dispatch(req)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 938, in _dispatch\n cmdpats, cmdoptions)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/fastmanifest\/cachemanager.py\", line 318, in runcommandtrigger\n result = orig(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/fastmanifest\/__init__.py\", line 175, in _logonexit\n r = orig(ui, repo, cmd, fullargs, *args)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext3rd\/perftweaks.py\", line 263, in _tracksparseprofiles\n res = runcommand(lui, repo, *args)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext3rd\/perftweaks.py\", line 248, in _trackdirstatesizes\n res = runcommand(lui, repo, *args)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext\/journal.py\", line 76, in runcommand\n return orig(lui, repo, cmd, fullargs, *args)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 674, in runcommand\n ret = _runcommand(ui, options, cmd, d)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext\/pager.py\", line 69, in pagecmd\n return orig(ui, options, cmd, cmdfunc)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 946, in _runcommand\n return cmdfunc()\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/dispatch.py\", line 935, in <lambda>\n d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext3rd\/absorb\/__init__.py\", line 977, in _amendcmd\n return orig(ui, repo, *pats, **opts)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext3rd\/tweakdefaults.py\", line 627, in cmd\n return origcmd(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext3rd\/tweakdefaults.py\", line 694, in amendcmd\n return orig(ui, repo, *pats, **opts)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/extensions.py\", line 273, in closure\n return func(*(args + a), **kw)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/usr\/lib64\/python2.7\/site-packages\/hgext\/automv.py\", line 63, in mvcheck\n return orig(ui, repo, *pats, **opts)\n File \"\/usr\/lib64\/python2.7\/site-packages\/mercurial\/util.py\", line 1056, in check\n return func(*args, **kwargs)\n File \"\/data\/users\/lsen\/facebook-hg-rpms\/fb-hgext\/hgext3rd\/fbamend\/__init__.py\", line 169, in amend\n raise Exception(\"testing\")\nException: testing\n", "type":"<type 'exceptions.Exception'>"} } ``` Reviewers: juehui, akushner, durham Reviewed By: durham Subscribers: medson, mjpieters Differential Revision: https://phabricator.intern.facebook.com/D5326253 Tasks: 19602850 Signature: t1:5326253:1498577444:c62977ec782519a22b9dc64b33f2a575c544b83c
2017-06-27 19:44:29 +03:00
import sys
import traceback
from edenscm.mercurial import dispatch, encoding, extensions
def _printtrace(ui, warning):
# Like dispatch.handlecommandexception, but avoids an unnecessary ui.log
ui.warn(warning)
return False # return value for "handlecommandexception", re-raises
def _handlecommandexception(orig, ui):
warning = dispatch._exceptionwarning(ui)
trace = traceback.format_exc()
# let blackbox log it (if it is configured to do so)
ui.log("commandexception", "%s\n%s\n", warning, trace)
exctype = sys.exc_info()[0]
exctypename = "None" if exctype is None else exctype.__name__
ui.log(
"hgerrors",
"exception has occurred: %s",
warning,
type=exctypename,
traceback=trace,
)
script = ui.config("errorredirect", "script")
if not script:
return orig(ui)
# run the external script
env = encoding.environ.copy()
env["WARNING"] = warning
env["TRACE"] = trace
# decide whether to use shell smartly, see 9335dc6b2a9c in hg
shell = any(c in script for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
try:
p = subprocess.Popen(script, shell=shell, stdin=subprocess.PIPE, env=env)
p.communicate(trace)
flake8: enable F821 check Summary: This check is useful and detects real errors (ex. fbconduit). Unfortunately `arc lint` will run it with both py2 and py3 so a lot of py2 builtins will still be warned. I didn't find a clean way to disable py3 check. So this diff tries to fix them. For `xrange`, the change was done by a script: ``` import sys import redbaron headertypes = {'comment', 'endl', 'from_import', 'import', 'string', 'assignment', 'atomtrailers'} xrangefix = '''try: xrange(0) except NameError: xrange = range ''' def isxrange(x): try: return x[0].value == 'xrange' except Exception: return False def main(argv): for i, path in enumerate(argv): print('(%d/%d) scanning %s' % (i + 1, len(argv), path)) content = open(path).read() try: red = redbaron.RedBaron(content) except Exception: print(' warning: failed to parse') continue hasxrange = red.find('atomtrailersnode', value=isxrange) hasxrangefix = 'xrange = range' in content if hasxrangefix or not hasxrange: print(' no need to change') continue # find a place to insert the compatibility statement changed = False for node in red: if node.type in headertypes: continue # node.insert_before is an easier API, but it has bugs changing # other "finally" and "except" positions. So do the insert # manually. # # node.insert_before(xrangefix) line = node.absolute_bounding_box.top_left.line - 1 lines = content.splitlines(1) content = ''.join(lines[:line]) + xrangefix + ''.join(lines[line:]) changed = True break if changed: # "content" is faster than "red.dumps()" open(path, 'w').write(content) print(' updated') if __name__ == "__main__": sys.exit(main(sys.argv[1:])) ``` For other py2 builtins that do not have a py3 equivalent, some `# noqa` were added as a workaround for now. Reviewed By: DurhamG Differential Revision: D6934535 fbshipit-source-id: 546b62830af144bc8b46788d2e0fd00496838939
2018-02-10 04:31:44 +03:00
except Exception:
# The binary cannot be executed, or some other issues. For example,
# "script" is not in PATH, and shell is False; or the peer closes the
# pipe early. Fallback to the plain error reporting.
return _printtrace(ui, warning)
else:
ret = p.returncode
# Python returns negative exit code for signal-terminated process. The
# shell converts singal-terminated process to a positive exit code by
# +128. Ctrl+C generates SIGTERM. Re-report the error unless the
# process exits cleanly or is terminated by SIGTERM (Ctrl+C).
ctrlc = (ret == signal.SIGTERM + 128) or (ret == -signal.SIGTERM)
if ret != 0 and not ctrlc:
return _printtrace(ui, warning)
return True # do not re-raise
def uisetup(ui):
extensions.wrapfunction(dispatch, "handlecommandexception", _handlecommandexception)