mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
feat(linux): improve Sentry error reports
- add breadcrumbs with keyboard details - add some more information about system - mark files in `km_config` as to the app belonging - some refactorings Test-bot: skip
This commit is contained in:
parent
f3643827d1
commit
596371dde2
9 changed files with 117 additions and 62 deletions
|
|
@ -110,27 +110,30 @@ def _set_dbus_started_for_session(value):
|
|||
|
||||
|
||||
def verify_dbus_running():
|
||||
if not 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
|
||||
try:
|
||||
# Seems dbus isn't running for the current user. Try to start it
|
||||
# and set these environment variables
|
||||
logging.info('Starting dbus with dbus-launch')
|
||||
stdout = subprocess.run(
|
||||
('dbus-launch', '--exit-with-session'),
|
||||
stdout=subprocess.PIPE, check=False).stdout
|
||||
_set_dbus_started_for_session(True)
|
||||
lines = stdout.decode('utf-8').splitlines()
|
||||
for line in lines:
|
||||
equal_sign = line.find('=')
|
||||
if equal_sign <= 0:
|
||||
logging.warning('Got unexpected line from dbus-launch: %s', line)
|
||||
continue
|
||||
name = line[:equal_sign]
|
||||
value = line[equal_sign+1:]
|
||||
logging.debug('Setting environment %s=%s', name, value)
|
||||
os.environ[name] = value
|
||||
except Exception as e:
|
||||
logging.error('Starting dbus-launch failed with %s', e)
|
||||
if 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
|
||||
# already running - nothing to do
|
||||
return
|
||||
|
||||
try:
|
||||
# Seems dbus isn't running for the current user. Try to start it
|
||||
# and set these environment variables
|
||||
logging.info('Starting dbus with dbus-launch')
|
||||
stdout = subprocess.run(
|
||||
('dbus-launch', '--exit-with-session'),
|
||||
stdout=subprocess.PIPE, check=False).stdout
|
||||
_set_dbus_started_for_session(True)
|
||||
lines = stdout.decode('utf-8').splitlines()
|
||||
for line in lines:
|
||||
equal_sign = line.find('=')
|
||||
if equal_sign <= 0:
|
||||
logging.warning('Got unexpected line from dbus-launch: %s', line)
|
||||
continue
|
||||
name = line[:equal_sign]
|
||||
value = line[equal_sign+1:]
|
||||
logging.debug('Setting environment %s=%s', name, value)
|
||||
os.environ[name] = value
|
||||
except Exception as e:
|
||||
logging.error('Starting dbus-launch failed with %s', e)
|
||||
|
||||
|
||||
def add_standard_arguments(parser):
|
||||
|
|
|
|||
|
|
@ -94,8 +94,7 @@ class DownloadKmpWindow(Gtk.Dialog):
|
|||
qs = urllib.parse.parse_qs(parsed.query)
|
||||
package_id = parsed.path.split('/')[-1]
|
||||
downloadfile = os.path.join(get_download_folder(), package_id)
|
||||
download_url = KeymanComUrl + '/go/package/download/' + package_id + \
|
||||
'?platform=linux&tier=' + __tier__
|
||||
download_url = f'{KeymanComUrl}/go/package/download/{package_id}?platform=linux&tier={__tier__}'
|
||||
if 'bcp47' in qs:
|
||||
self.language = qs['bcp47'][0]
|
||||
download_url += '&bcp47=' + qs['bcp47'][0]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from gi.repository import GObject
|
|||
from keyman_config import _
|
||||
from keyman_config import KeymanApiUrl, KeymanDownloadsUrl
|
||||
from keyman_config.deprecated_decorator import deprecated
|
||||
from keyman_config.sentry_handling import SentryErrorHandling
|
||||
|
||||
|
||||
class InstallLocation(GObject.GEnum):
|
||||
|
|
@ -105,7 +106,7 @@ def get_keyboard_data(keyboardID, weekCache=False):
|
|||
dict: Keyboard data
|
||||
"""
|
||||
logging.info('Getting data for keyboard %s', keyboardID)
|
||||
api_url = KeymanApiUrl + '/keyboard/' + keyboardID
|
||||
api_url = f'{KeymanApiUrl}/keyboard/{keyboardID}'
|
||||
logging.debug('At URL %s', api_url)
|
||||
cache_dir = keyman_cache_dir()
|
||||
current_dir = os.getcwd()
|
||||
|
|
@ -123,10 +124,7 @@ def get_keyboard_data(keyboardID, weekCache=False):
|
|||
logging.debug('Time: {0} / Used Cache: {1}'.format(now, _did_use_cache(response)))
|
||||
os.chdir(current_dir)
|
||||
_uninstall_cache()
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return None
|
||||
return response.json() if response.status_code == 200 else None
|
||||
|
||||
|
||||
def get_download_folder():
|
||||
|
|
@ -234,6 +232,8 @@ def download_kmp_file(url, kmpfile, cache=False):
|
|||
"""
|
||||
logging.info('Download URL: %s', url)
|
||||
downloadfile = None
|
||||
sentry = SentryErrorHandling()
|
||||
sentry.add_breadcrumb(category='download', message=f'Downloading keyboard from {url}')
|
||||
|
||||
if cache:
|
||||
cache_dir = keyman_cache_dir()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import logging
|
|||
import os
|
||||
|
||||
from keyman_config.gsettings import GSettings
|
||||
from keyman_config.sentry_handling import SentryErrorHandling
|
||||
|
||||
|
||||
# pylint: disable=global-statement
|
||||
|
|
@ -52,15 +53,20 @@ def _reset_gnome_shell():
|
|||
def get_ibus_keyboard_id(keyboard, packageDir, language=None, ignore_language=False):
|
||||
if not keyboard:
|
||||
return None
|
||||
sentry = SentryErrorHandling()
|
||||
kmx_file = os.path.join(packageDir, keyboard['id'] + ".kmx")
|
||||
if ignore_language:
|
||||
sentry.add_breadcrumb(category='keyboard', message=f'ibus_keyboard_id(1): {kmx_file}')
|
||||
return kmx_file
|
||||
if language is not None and language != '':
|
||||
logging.debug(language)
|
||||
sentry.add_breadcrumb(category='keyboard', message=f'ibus_keyboard_id(2): {language}:{kmx_file}')
|
||||
return f"{language}:{kmx_file}"
|
||||
if "languages" in keyboard and len(keyboard["languages"]) > 0:
|
||||
firstLanguage = keyboard["languages"][0]
|
||||
logging.debug(firstLanguage)
|
||||
if 'id' in firstLanguage:
|
||||
sentry.add_breadcrumb(category='keyboard', message=f'ibus_keyboard_id(3): {firstLanguage["id"]}:{kmx_file}')
|
||||
return f"{firstLanguage['id']}:{kmx_file}"
|
||||
sentry.add_breadcrumb(category='keyboard', message=f'ibus_keyboard_id(4): {kmx_file}')
|
||||
return kmx_file
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from zipfile import is_zipfile
|
|||
from keyman_config import KeymanComUrl, __tier__
|
||||
from keyman_config.get_kmp import download_kmp_file, get_download_folder
|
||||
from keyman_config.install_window import InstallKmpWindow
|
||||
from keyman_config.sentry_handling import SentryErrorHandling
|
||||
|
||||
|
||||
def download_and_install_package(url):
|
||||
|
|
@ -18,6 +19,7 @@ def download_and_install_package(url):
|
|||
url: a .kmp file, a keyman:// URL, or a file:// URL pointing to a .kmp file,
|
||||
possibly with a bcp47=<language> specified
|
||||
"""
|
||||
sentry = SentryErrorHandling()
|
||||
parsedUrl = urlparse(url)
|
||||
bcp47 = _extract_bcp47(parsedUrl.query)
|
||||
|
||||
|
|
@ -34,11 +36,13 @@ def download_and_install_package(url):
|
|||
|
||||
downloadFile = os.path.join(get_download_folder(), packageId)
|
||||
downloadUrl = f'{KeymanComUrl}/go/package/download/{packageId}?platform=linux&tier={__tier__}'
|
||||
sentry.add_breadcrumb(category='download', message=f'About to download keyboard {packageId} from {downloadUrl}')
|
||||
packageFile = download_kmp_file(downloadUrl, downloadFile)
|
||||
if packageFile is None:
|
||||
return
|
||||
elif parsedUrl.scheme in ['', 'file']:
|
||||
packageFile = parsedUrl.path
|
||||
sentry.add_breadcrumb(category='keyboard', message=f'adding keyboard from file {packageFile}')
|
||||
else:
|
||||
logging.error(f"Invalid URL: {url}")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from keyman_config.gnome_keyboards_util import (GnomeKeyboardsUtil,
|
|||
from keyman_config.ibus_util import get_ibus_bus, install_to_ibus, restart_ibus
|
||||
from keyman_config.kmpmetadata import KMFileTypes, get_metadata
|
||||
from keyman_config.kvk2ldml import convert_kvk_to_ldml, output_ldml
|
||||
from keyman_config.sentry_handling import SentryErrorHandling
|
||||
|
||||
# TODO userdir install
|
||||
# special processing for kmn if needed
|
||||
|
|
@ -105,6 +106,9 @@ class InstallKmp():
|
|||
self.kmpdocdir = get_keyman_doc_dir(area, self.packageID)
|
||||
self.kmpfontdir = get_keyman_font_dir(area, self.packageID)
|
||||
|
||||
sentry = SentryErrorHandling()
|
||||
sentry.add_breadcrumb(category='install', message=f'Installing kmp: {self.packageID} from "{inputfile}" for lang: {language}; area: {"OS" if area == 1 else "Shared" if area == 2 else "User" if area == 3 else "Unknown"}')
|
||||
|
||||
if not os.path.isfile(inputfile):
|
||||
message = _("File {kmpfile} doesn't exist").format(kmpfile=inputfile)
|
||||
logging.error("install_kmp.py: %s", message)
|
||||
|
|
@ -258,6 +262,9 @@ class InstallKmp():
|
|||
if not language:
|
||||
language = self._add_custom_keyboard(firstKeyboard, packageDir, requested_language)
|
||||
|
||||
sentry = SentryErrorHandling()
|
||||
sentry.add_breadcrumb(category='install', message=f'Installing keyboards for lang: {language}')
|
||||
|
||||
if is_fcitx_running():
|
||||
return self._install_keyboards_to_fcitx()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from json.decoder import JSONDecodeError
|
|||
import magic
|
||||
|
||||
from keyman_config import secure_lookup
|
||||
from keyman_config.sentry_handling import SentryErrorHandling
|
||||
|
||||
|
||||
class KMFileTypes(Enum):
|
||||
|
|
@ -455,6 +456,7 @@ def parsemetadata(jsonfile, verbose=False):
|
|||
"""
|
||||
info = system = keyboards = files = options = nonexistent = None
|
||||
extracted_dir = os.path.dirname(jsonfile)
|
||||
sentry = SentryErrorHandling()
|
||||
|
||||
logging.debug("parsemetadata: reading file:%s dir:%s", jsonfile, extracted_dir)
|
||||
|
||||
|
|
@ -496,6 +498,11 @@ def parsemetadata(jsonfile, verbose=False):
|
|||
print_options(options)
|
||||
print_keyboards(keyboards)
|
||||
print_files(files, extracted_dir)
|
||||
|
||||
if system:
|
||||
sentry.add_breadcrumb(
|
||||
category='keyboard', message=f'kmp.json: pkg: "{secure_lookup(info, "name", "description")}", KeymanDeveloperVersion: {secure_lookup(system, "keymanDeveloperVersion")}, dir: {os.path.basename(extracted_dir)}')
|
||||
|
||||
return info, system, options, keyboards, files
|
||||
|
||||
|
||||
|
|
@ -532,19 +539,22 @@ def get_and_convert_infdata(tmpdirname):
|
|||
see kmpmetadata.parseinfdata for details
|
||||
"""
|
||||
kmpinf = os.path.join(tmpdirname, "kmp.inf")
|
||||
if os.path.isfile(kmpinf):
|
||||
info, system, options, keyboards, files = parseinfdata(kmpinf, False)
|
||||
j = infmetadata_to_json(info, system, options, keyboards, files)
|
||||
kmpjson = os.path.join(tmpdirname, "kmp.json")
|
||||
try:
|
||||
with open(kmpjson, "w") as write_file:
|
||||
print(j, file=write_file)
|
||||
except Exception as e:
|
||||
logging.warning('Exception %s writing metadata %s %s', type(e), kmpjson, e.args)
|
||||
return info, system, options, keyboards, files
|
||||
else:
|
||||
if not os.path.isfile(kmpinf):
|
||||
return None, None, None, None, None
|
||||
|
||||
sentry = SentryErrorHandling()
|
||||
info, system, options, keyboards, files = parseinfdata(kmpinf, False)
|
||||
j = infmetadata_to_json(info, system, options, keyboards, files)
|
||||
kmpjson = os.path.join(tmpdirname, "kmp.json")
|
||||
try:
|
||||
with open(kmpjson, "w") as write_file:
|
||||
print(j, file=write_file)
|
||||
except Exception as e:
|
||||
logging.warning('Exception %s writing metadata %s %s', type(e), kmpjson, e.args)
|
||||
sentry.add_breadcrumb(
|
||||
category='keyboard', message=f'kmp.inf: pkg: "{secure_lookup(info, "name", "description")}", KeymanDeveloperVersion: {secure_lookup(system, "keymanDeveloperVersion")}, dir: {os.path.basename(tmpdirname)}')
|
||||
return info, system, options, keyboards, files
|
||||
|
||||
|
||||
def infmetadata_to_json(info, system, options, keyboards, files):
|
||||
jsonfiles = []
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ gi.require_version('Gtk', '3.0')
|
|||
from gi.repository import Gio, Gtk
|
||||
|
||||
|
||||
is_sentry_sdk = False
|
||||
|
||||
|
||||
class SentryErrorHandling:
|
||||
def __init__(self) -> None:
|
||||
self.error_reporting_setting = KeymanOption('error-reporting')
|
||||
|
|
@ -78,6 +81,15 @@ class SentryErrorHandling:
|
|||
if enabled != was_enabled:
|
||||
self._handle_enabled(enabled)
|
||||
|
||||
def add_breadcrumb(self, category, message, level='info'):
|
||||
global is_sentry_sdk
|
||||
if not self.is_sentry_enabled() or not is_sentry_sdk:
|
||||
return
|
||||
|
||||
# sentry_sdk = importlib.import_module('sentry_sdk')
|
||||
import sentry_sdk
|
||||
sentry_sdk.add_breadcrumb(category=category, message=message, level=level)
|
||||
|
||||
def _get_environ_nosentry(self):
|
||||
keyman_nosentry = os.environ.get('KEYMAN_NOSENTRY')
|
||||
return keyman_nosentry and (int(keyman_nosentry) == 1)
|
||||
|
|
@ -114,47 +126,61 @@ class SentryErrorHandling:
|
|||
|
||||
def _sentry_sdk_initialize(self):
|
||||
# Try new sentry-sdk first
|
||||
global is_sentry_sdk
|
||||
sentry_sdk = importlib.import_module('sentry_sdk')
|
||||
from sentry_sdk import configure_scope, set_user
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
|
||||
is_sentry_sdk = True
|
||||
|
||||
sentry_logging = LoggingIntegration(
|
||||
level=logging.INFO, # Capture info and above as breadcrumbs
|
||||
event_level=logging.CRITICAL # Send critical errors as events
|
||||
)
|
||||
SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357@o1005580.ingest.sentry.io/5983525"
|
||||
|
||||
os_release = None
|
||||
dist = None
|
||||
try:
|
||||
os_release = platform.freedesktop_os_release()
|
||||
dist = os_release['VERSION_CODENAME']
|
||||
except OSError as e:
|
||||
logging.debug(f'System does not have os_release file: {e.strerror}')
|
||||
except AttributeError:
|
||||
logging.debug('System does not have platform.freedesktop_os_release() method')
|
||||
except Exception:
|
||||
logging.debug(
|
||||
'Got exception trying to access platform.freedesktop_os_release() method or os_release information')
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn=SentryUrl,
|
||||
environment=__environment__,
|
||||
release=__versiongittag__,
|
||||
integrations=[sentry_logging],
|
||||
dist=dist,
|
||||
in_app_include=['keyman_config'],
|
||||
before_send=self._before_send
|
||||
)
|
||||
hash = hashlib.md5()
|
||||
hash.update(getpass.getuser().encode())
|
||||
set_user({'id': hash.hexdigest()})
|
||||
md5Hash = hashlib.md5()
|
||||
md5Hash.update(getpass.getuser().encode())
|
||||
set_user({'id': md5Hash.hexdigest()})
|
||||
with configure_scope() as scope:
|
||||
scope.set_tag("app", os.path.basename(sys.argv[0]))
|
||||
scope.set_tag("pkgversion", __pkgversion__)
|
||||
scope.set_tag("platform", platform.platform())
|
||||
scope.set_tag("system", platform.system())
|
||||
scope.set_tag("tier", __tier__)
|
||||
scope.set_tag("device", platform.node())
|
||||
try:
|
||||
os_release = platform.freedesktop_os_release()
|
||||
scope.set_tag('os', os_release['PRETTY_NAME'])
|
||||
scope.set_tag('os.name', os_release['NAME'])
|
||||
if 'VERSION' in os_release:
|
||||
scope.set_tag('os.version', os_release['VERSION'])
|
||||
except OSError as e:
|
||||
logging.debug(f'System does not have os_release file: {e.strerror}')
|
||||
except AttributeError:
|
||||
logging.debug('System does not have platform.freedesktop_os_release() method')
|
||||
except:
|
||||
logging.debug(
|
||||
'Got exception trying to access platform.freedesktop_os_release() method or os_release information')
|
||||
self._set_system_info(scope, os_release)
|
||||
logging.info("Initialized Sentry error reporting")
|
||||
|
||||
def _set_system_info(self, scope, os_release):
|
||||
scope.set_tag("app", os.path.basename(sys.argv[0]))
|
||||
scope.set_tag("pkgversion", __pkgversion__)
|
||||
scope.set_tag("platform", platform.platform())
|
||||
scope.set_tag("system", platform.system())
|
||||
scope.set_tag("tier", __tier__)
|
||||
scope.set_tag("device", platform.node())
|
||||
if os_release:
|
||||
scope.set_tag('os', os_release['PRETTY_NAME'])
|
||||
scope.set_tag('os.name', os_release['NAME'])
|
||||
if 'VERSION' in os_release:
|
||||
scope.set_tag('os.version', os_release['VERSION'])
|
||||
|
||||
def _raven_initialize(self):
|
||||
# sentry-sdk is not available, so use older raven
|
||||
raven = importlib.import_module('raven')
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ if __name__ == '__main__':
|
|||
download_and_install_package(args.url)
|
||||
else:
|
||||
# Workaround for bug in webkit2gtk (#12587)
|
||||
if not 'WEBKIT_DISABLE_DMABUF_RENDERER' in os.environ:
|
||||
if 'WEBKIT_DISABLE_DMABUF_RENDERER' not in os.environ:
|
||||
os.environ['WEBKIT_DISABLE_DMABUF_RENDERER'] = '1'
|
||||
|
||||
w = ViewInstalledWindow()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue