tbase_crash_reporter.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tbase_crash_reporter.py (5160B)
---
1 # Electrum - lightweight Bitcoin client
2 #
3 # Permission is hereby granted, free of charge, to any person
4 # obtaining a copy of this software and associated documentation files
5 # (the "Software"), to deal in the Software without restriction,
6 # including without limitation the rights to use, copy, modify, merge,
7 # publish, distribute, sublicense, and/or sell copies of the Software,
8 # and to permit persons to whom the Software is furnished to do so,
9 # subject to the following conditions:
10 #
11 # The above copyright notice and this permission notice shall be
12 # included in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 # SOFTWARE.
22 import asyncio
23 import json
24 import locale
25 import traceback
26 import sys
27
28 from .version import ELECTRUM_VERSION
29 from . import constants
30 from .i18n import _
31 from .util import make_aiohttp_session
32 from .logging import describe_os_version, Logger, get_git_version
33
34
35 class BaseCrashReporter(Logger):
36 report_server = "https://crashhub.electrum.org"
37 config_key = "show_crash_reporter"
38 issue_template = """<h2>Traceback</h2>
39 <pre>
40 {traceback}
41 </pre>
42
43 <h2>Additional information</h2>
44 <ul>
45 <li>Electrum version: {app_version}</li>
46 <li>Python version: {python_version}</li>
47 <li>Operating system: {os}</li>
48 <li>Wallet type: {wallet_type}</li>
49 <li>Locale: {locale}</li>
50 </ul>
51 """
52 CRASH_MESSAGE = _('Something went wrong while executing Electrum.')
53 CRASH_TITLE = _('Sorry!')
54 REQUEST_HELP_MESSAGE = _('To help us diagnose and fix the problem, you can send us a bug report that contains '
55 'useful debug information:')
56 DESCRIBE_ERROR_MESSAGE = _("Please briefly describe what led to the error (optional):")
57 ASK_CONFIRM_SEND = _("Do you want to send this report?")
58
59 def __init__(self, exctype, value, tb):
60 Logger.__init__(self)
61 self.exc_args = (exctype, value, tb)
62
63 def send_report(self, asyncio_loop, proxy, endpoint="/crash", *, timeout=None):
64 if constants.net.GENESIS[-4:] not in ["4943", "e26f"] and ".electrum.org" in BaseCrashReporter.report_server:
65 # Gah! Some kind of altcoin wants to send us crash reports.
66 raise Exception(_("Missing report URL."))
67 report = self.get_traceback_info()
68 report.update(self.get_additional_info())
69 report = json.dumps(report)
70 coro = self.do_post(proxy, BaseCrashReporter.report_server + endpoint, data=report)
71 response = asyncio.run_coroutine_threadsafe(coro, asyncio_loop).result(timeout)
72 return response
73
74 async def do_post(self, proxy, url, data):
75 async with make_aiohttp_session(proxy) as session:
76 async with session.post(url, data=data, raise_for_status=True) as resp:
77 return await resp.text()
78
79 def get_traceback_info(self):
80 exc_string = str(self.exc_args[1])
81 stack = traceback.extract_tb(self.exc_args[2])
82 readable_trace = "".join(traceback.format_list(stack))
83 id = {
84 "file": stack[-1].filename,
85 "name": stack[-1].name,
86 "type": self.exc_args[0].__name__
87 }
88 return {
89 "exc_string": exc_string,
90 "stack": readable_trace,
91 "id": id
92 }
93
94 def get_additional_info(self):
95 args = {
96 "app_version": get_git_version() or ELECTRUM_VERSION,
97 "python_version": sys.version,
98 "os": describe_os_version(),
99 "wallet_type": "unknown",
100 "locale": locale.getdefaultlocale()[0] or "?",
101 "description": self.get_user_description()
102 }
103 try:
104 args["wallet_type"] = self.get_wallet_type()
105 except:
106 # Maybe the wallet isn't loaded yet
107 pass
108 return args
109
110 def _get_traceback_str(self) -> str:
111 return "".join(traceback.format_exception(*self.exc_args))
112
113 def get_report_string(self):
114 info = self.get_additional_info()
115 info["traceback"] = self._get_traceback_str()
116 return self.issue_template.format(**info)
117
118 def get_user_description(self):
119 raise NotImplementedError
120
121 def get_wallet_type(self) -> str:
122 raise NotImplementedError
123
124
125 def trigger_crash():
126 # note: do not change the type of the exception, the message,
127 # or the name of this method. All reports generated through this
128 # method will be grouped together by the crash reporter, and thus
129 # don't spam the issue tracker.
130
131 class TestingException(Exception):
132 pass
133
134 def crash_test():
135 raise TestingException("triggered crash for testing purposes")
136
137 import threading
138 t = threading.Thread(target=crash_test)
139 t.start()