From 23057f786fefc63dd77d67b47c7e4ece8c5dda4f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Nov 2019 12:59:05 +0100 Subject: [PATCH 01/16] Add initial stubs for sentry logging --- cura/CrashHandler.py | 20 +++++++++++--------- cura_app.py | 5 +++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 1d85a1da54..4f4e834b58 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -13,6 +13,9 @@ import ssl import urllib.request import urllib.error +from sentry_sdk.hub import Hub +from sentry_sdk.utils import capture_internal_exceptions, event_from_exception + import certifi from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl @@ -365,15 +368,14 @@ class CrashHandler: print("Sending crash report info to [%s]...\n" % self.crash_url) try: - f = urllib.request.urlopen(self.crash_url, **kwoptions) - Logger.log("i", "Sent crash report info.") - if not self.has_started: - print("Sent crash report info.\n") - f.close() - except urllib.error.HTTPError as e: - Logger.logException("e", "An HTTP error occurred while trying to send crash report") - if not self.has_started: - print("An HTTP error occurred while trying to send crash report: %s" % e) + hub = Hub.current + client = hub.client + event, hint = event_from_exception((self.exception_type, self.value, self.traceback), + client_options=client.options, + mechanism={"type": "excepthook", "handled": False}, + ) + hub.capture_event(event, hint=hint) + hub.flush() except Exception as e: # We don't want any exception to cause problems Logger.logException("e", "An exception occurred while trying to send crash report") if not self.has_started: diff --git a/cura_app.py b/cura_app.py index e14b4410bc..cc9a44a5db 100755 --- a/cura_app.py +++ b/cura_app.py @@ -9,8 +9,11 @@ import os import sys from UM.Platform import Platform +from cura import ApplicationMetadata from cura.ApplicationMetadata import CuraAppName +import sentry_sdk + parser = argparse.ArgumentParser(prog = "cura", add_help = False) parser.add_argument("--debug", @@ -18,7 +21,9 @@ parser.add_argument("--debug", default = False, help = "Turn on the debug mode by setting this option." ) + known_args = vars(parser.parse_known_args()[0]) +sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", release = "cura%s" % ApplicationMetadata.CuraVersion, default_integrations=False ) if not known_args["debug"]: def get_cura_dir_path(): From 6c8fddc765d9d9b90f477a650427c6f60e8eb140 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Nov 2019 13:26:17 +0100 Subject: [PATCH 02/16] Add sentry logger to display breadcrumbs --- plugins/SentryLogger/SentryLogger.py | 43 ++++++++++++++++++++++++++++ plugins/SentryLogger/__init__.py | 12 ++++++++ plugins/SentryLogger/plugin.json | 8 ++++++ resources/bundled_packages/cura.json | 17 +++++++++++ 4 files changed, 80 insertions(+) create mode 100644 plugins/SentryLogger/SentryLogger.py create mode 100644 plugins/SentryLogger/__init__.py create mode 100644 plugins/SentryLogger/plugin.json diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py new file mode 100644 index 0000000000..0c0730b1dc --- /dev/null +++ b/plugins/SentryLogger/SentryLogger.py @@ -0,0 +1,43 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Logger import LogOutput +from typing import Set +from sentry_sdk import add_breadcrumb +from typing import Optional + + +class SentryLogger(LogOutput): + def __init__(self) -> None: + super().__init__() + self._show_once = set() # type: Set[str] + + ## Log the message to the sentry hub as a breadcrumb + # \param log_type "e" (error), "i"(info), "d"(debug), "w"(warning) or "c"(critical) (can postfix with "_once") + # \param message String containing message to be logged + def log(self, log_type: str, message: str) -> None: + level = self._translateLogType(log_type) + if level is None: + if message not in self._show_once: + level = self._translateLogType(log_type[0]) + if level is not None: + self._show_once.add(message) + add_breadcrumb(level=level, message=message) + else: + add_breadcrumb(level=level, message=message) + + @staticmethod + def _translateLogType(log_type: str) -> Optional[str]: + level = None + if log_type == "w": + level = "warning" + elif log_type == "i": + level = "info" + elif log_type == "c": + level = "fatal" + elif log_type == "e": + level = "error" + elif log_type == "d": + level = "debug" + + return level \ No newline at end of file diff --git a/plugins/SentryLogger/__init__.py b/plugins/SentryLogger/__init__.py new file mode 100644 index 0000000000..6cf92ab624 --- /dev/null +++ b/plugins/SentryLogger/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import SentryLogger + + +def getMetaData(): + return {} + + +def register(app): + return { "logger": SentryLogger.SentryLogger() } diff --git a/plugins/SentryLogger/plugin.json b/plugins/SentryLogger/plugin.json new file mode 100644 index 0000000000..fef4e1c554 --- /dev/null +++ b/plugins/SentryLogger/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Sentry Logger", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Logs certain events so that they can be used by the crash reporter", + "api": "7.0", + "i18n-catalog": "cura" +} diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 22642cd38e..7cb229150c 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -407,6 +407,23 @@ } } }, + "SentryLogger": { + "package_info": { + "package_id": "SentryLogger", + "package_type": "plugin", + "display_name": "Sentry Logger", + "description": "Logs certain events so that they can be used by the crash reporter", + "package_version": "1.0.0", + "sdk_version": "7.0.0", + "website": "https://ultimaker.com", + "author": { + "author_id": "UltimakerPackages", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, "SimulationView": { "package_info": { "package_id": "SimulationView", From c7649e3db160eb669d1a158c73471adb6072ed33 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Nov 2019 14:04:35 +0100 Subject: [PATCH 03/16] Add some extra tags to make crash reports more traceable --- cura/CrashHandler.py | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 4f4e834b58..a76093863f 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -15,6 +15,7 @@ import urllib.error from sentry_sdk.hub import Hub from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk import configure_scope import certifi @@ -198,6 +199,12 @@ class CrashHandler: self.data["qt_version"] = QT_VERSION_STR self.data["pyqt_version"] = PYQT_VERSION_STR + with configure_scope() as scope: + scope.set_tag("qt_version", QT_VERSION_STR) + scope.set_tag("pyqt_version", PYQT_VERSION_STR) + scope.set_tag("os", platform.system()) + scope.set_tag("os_version", platform.version()) + return group def _getOpenGLInfo(self): @@ -213,7 +220,10 @@ class CrashHandler: info += "" self.data["opengl"] = {"version": opengl_instance.getOpenGLVersion(), "vendor": opengl_instance.getGPUVendorName(), "type": opengl_instance.getGPUType()} - + with configure_scope() as scope: + scope.set_tag("opengl_version", opengl_instance.getOpenGLVersion()) + scope.set_tag("gpu_vendor", opengl_instance.getGPUVendorName()) + scope.set_tag("gpu_type", opengl_instance.getGPUType()) return info def _exceptionInfoWidget(self): @@ -295,6 +305,10 @@ class CrashHandler: "module_name": module_name, "version": module_version, "is_plugin": isPlugin} self.data["exception"] = exception_dict + with configure_scope() as scope: + scope.set_tag("is_plugin", isPlugin) + scope.set_tag("module", module_name) + return group def _logInfoWidget(self): @@ -352,28 +366,9 @@ class CrashHandler: # Before sending data, the user comments are stored self.data["user_info"] = self.user_description_text_area.toPlainText() - # Convert data to bytes - binary_data = json.dumps(self.data).encode("utf-8") - - # CURA-6698 Create an SSL context and use certifi CA certificates for verification. - context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLSv1_2) - context.load_verify_locations(cafile = certifi.where()) - # Submit data - kwoptions = {"data": binary_data, - "timeout": 5, - "context": context} - - Logger.log("i", "Sending crash report info to [%s]...", self.crash_url) - if not self.has_started: - print("Sending crash report info to [%s]...\n" % self.crash_url) - try: hub = Hub.current - client = hub.client - event, hint = event_from_exception((self.exception_type, self.value, self.traceback), - client_options=client.options, - mechanism={"type": "excepthook", "handled": False}, - ) + event, hint = event_from_exception((self.exception_type, self.value, self.traceback)) hub.capture_event(event, hint=hint) hub.flush() except Exception as e: # We don't want any exception to cause problems From 466c90ca6c910def8430bc95f3b8034ff9cb14dc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Nov 2019 14:41:54 +0100 Subject: [PATCH 04/16] Add some extra context info to the crashes --- cura/CrashHandler.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index a76093863f..b70af53061 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import platform @@ -9,16 +9,12 @@ import os import os.path import time import json -import ssl -import urllib.request -import urllib.error +from typing import cast from sentry_sdk.hub import Hub -from sentry_sdk.utils import capture_internal_exceptions, event_from_exception +from sentry_sdk.utils import event_from_exception from sentry_sdk import configure_scope -import certifi - from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton from PyQt5.QtGui import QDesktopServices @@ -28,6 +24,7 @@ from UM.Logger import Logger from UM.View.GL.OpenGL import OpenGL from UM.i18n import i18nCatalog from UM.Resources import Resources +from cura import ApplicationMetadata catalog = i18nCatalog("cura") @@ -48,9 +45,8 @@ skip_exception_types = [ GeneratorExit ] -class CrashHandler: - crash_url = "https://stats.ultimaker.com/api/cura" +class CrashHandler: def __init__(self, exception_type, value, tb, has_started = True): self.exception_type = exception_type self.value = value @@ -58,15 +54,11 @@ class CrashHandler: self.has_started = has_started self.dialog = None # Don't create a QDialog before there is a QApplication - # While we create the GUI, the information will be stored for sending afterwards - self.data = dict() - self.data["time_stamp"] = time.time() - Logger.log("c", "An uncaught error has occurred!") for line in traceback.format_exception(exception_type, value, tb): for part in line.rstrip("\n").split("\n"): Logger.log("c", part) - + self.data = {} # If Cura has fully started, we only show fatal errors. # If Cura has not fully started yet, we always show the early crash dialog. Otherwise, Cura will just crash # without any information. @@ -204,6 +196,7 @@ class CrashHandler: scope.set_tag("pyqt_version", PYQT_VERSION_STR) scope.set_tag("os", platform.system()) scope.set_tag("os_version", platform.version()) + scope.set_tag("is_enterprise", ApplicationMetadata.IsEnterpriseVersion) return group @@ -220,10 +213,31 @@ class CrashHandler: info += "" self.data["opengl"] = {"version": opengl_instance.getOpenGLVersion(), "vendor": opengl_instance.getGPUVendorName(), "type": opengl_instance.getGPUType()} + + active_machine_definition_id = "unknown" + active_machine_manufacterer = "unknown" + + try: + from cura.CuraApplication import CuraApplication + application = cast(CuraApplication, Application.getInstance()) + machine_manager = application.getMachineManager() + global_stack = machine_manager.activeMachine + if global_stack is None: + active_machine_definition_id = "empty" + active_machine_manufacterer = "empty" + else: + active_machine_definition_id = global_stack.definition.getId() + active_machine_manufacterer = global_stack.definition.getMetaDataEntry("manufacturer", "unknown") + except: + pass + with configure_scope() as scope: scope.set_tag("opengl_version", opengl_instance.getOpenGLVersion()) scope.set_tag("gpu_vendor", opengl_instance.getGPUVendorName()) scope.set_tag("gpu_type", opengl_instance.getGPUType()) + scope.set_tag("active_machine", active_machine_definition_id) + scope.set_tag("active_machine_manufacterer", active_machine_manufacterer) + return info def _exceptionInfoWidget(self): From 028a60318caa7811123976a6ab049c358ec5ef3c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Nov 2019 15:43:45 +0100 Subject: [PATCH 05/16] Add extra tag if crash was during startup or not --- cura/CrashHandler.py | 3 +++ cura_app.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index b70af53061..8812d1b98f 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -65,6 +65,9 @@ class CrashHandler: if has_started and exception_type in skip_exception_types: return + with configure_scope() as scope: + scope.set_tag("during_startup", not has_started) + if not has_started: self._send_report_checkbox = None self.early_crash_dialog = self._createEarlyCrashDialog() diff --git a/cura_app.py b/cura_app.py index cc9a44a5db..218a63b24d 100755 --- a/cura_app.py +++ b/cura_app.py @@ -23,7 +23,10 @@ parser.add_argument("--debug", ) known_args = vars(parser.parse_known_args()[0]) -sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", release = "cura%s" % ApplicationMetadata.CuraVersion, default_integrations=False ) +sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", + release = "cura%s" % ApplicationMetadata.CuraVersion, + default_integrations = False, + max_breadcrumbs = 200) if not known_args["debug"]: def get_cura_dir_path(): From 93ee1115668c3c60aa23f9e492859ceeb0183408 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 13 Dec 2019 15:20:43 +0100 Subject: [PATCH 06/16] Add missing typing --- plugins/SentryLogger/SentryLogger.py | 2 +- plugins/SentryLogger/__init__.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index 0c0730b1dc..9d5bb8a21d 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -40,4 +40,4 @@ class SentryLogger(LogOutput): elif log_type == "d": level = "debug" - return level \ No newline at end of file + return level diff --git a/plugins/SentryLogger/__init__.py b/plugins/SentryLogger/__init__.py index 6cf92ab624..c464de5fd4 100644 --- a/plugins/SentryLogger/__init__.py +++ b/plugins/SentryLogger/__init__.py @@ -1,12 +1,16 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import TYPE_CHECKING, Dict, Any from . import SentryLogger +if TYPE_CHECKING: + from UM.Application import Application -def getMetaData(): + +def getMetaData() -> Dict[str, Any]: return {} -def register(app): - return { "logger": SentryLogger.SentryLogger() } +def register(app: "Application") -> Dict[str, Any]: + return {"logger": SentryLogger.SentryLogger()} From 6a36a71c2ccc1479d103c03a25ff8380a45a6eab Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 13 Dec 2019 16:25:58 +0100 Subject: [PATCH 07/16] Prune user paths from the sentry logs --- plugins/SentryLogger/SentryLogger.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index 9d5bb8a21d..c25c7dc0a4 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -5,7 +5,9 @@ from UM.Logger import LogOutput from typing import Set from sentry_sdk import add_breadcrumb from typing import Optional +import os +home_dir = os.path.expanduser("~") class SentryLogger(LogOutput): def __init__(self) -> None: @@ -17,6 +19,7 @@ class SentryLogger(LogOutput): # \param message String containing message to be logged def log(self, log_type: str, message: str) -> None: level = self._translateLogType(log_type) + message = self._pruneSensitiveData(message) if level is None: if message not in self._show_once: level = self._translateLogType(log_type[0]) @@ -26,6 +29,12 @@ class SentryLogger(LogOutput): else: add_breadcrumb(level=level, message=message) + @staticmethod + def _pruneSensitiveData(message): + if home_dir in message: + message = message.replace(home_dir, "") + return message + @staticmethod def _translateLogType(log_type: str) -> Optional[str]: level = None From 76401d78a9d2f9d08a69142f0c35e3938c43a611 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 13 Dec 2019 16:53:17 +0100 Subject: [PATCH 08/16] Sentry: Add locale (both cura and os) to tags. --- cura/CrashHandler.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 8812d1b98f..6618b55b22 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -9,6 +9,7 @@ import os import os.path import time import json +import locale from typing import cast from sentry_sdk.hub import Hub @@ -176,8 +177,10 @@ class CrashHandler: try: from UM.Application import Application self.cura_version = Application.getInstance().getVersion() + self.cura_locale = Application.getInstance().getPreferences().getValue("general/language") except: self.cura_version = catalog.i18nc("@label unknown version of Cura", "Unknown") + self.cura_locale = "??_??" crash_info = "" + catalog.i18nc("@label Cura version number", "Cura version") + ": " + str(self.cura_version) + "
" crash_info += "" + catalog.i18nc("@label Type of platform", "Platform") + ": " + str(platform.platform()) + "
" @@ -193,12 +196,16 @@ class CrashHandler: self.data["os"] = {"type": platform.system(), "version": platform.version()} self.data["qt_version"] = QT_VERSION_STR self.data["pyqt_version"] = PYQT_VERSION_STR + self.data["locale_os"] = locale.getlocale(locale.LC_MESSAGES)[0] if hasattr(locale, 'LC_MESSAGES') else locale.getdefaultlocale()[0] + self.data["locale_cura"] = self.cura_locale with configure_scope() as scope: scope.set_tag("qt_version", QT_VERSION_STR) scope.set_tag("pyqt_version", PYQT_VERSION_STR) scope.set_tag("os", platform.system()) scope.set_tag("os_version", platform.version()) + scope.set_tag("locale_os", self.data["locale_os"]) + scope.set_tag("locale_cura", self.cura_locale) scope.set_tag("is_enterprise", ApplicationMetadata.IsEnterpriseVersion) return group From 157a25a992b611fe5133d908edf792b2fc4bd5fc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:06:48 +0100 Subject: [PATCH 09/16] Fix minor codestyle issues --- cura/CrashHandler.py | 2 +- plugins/SentryLogger/SentryLogger.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 6618b55b22..5d07ae05e3 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -196,7 +196,7 @@ class CrashHandler: self.data["os"] = {"type": platform.system(), "version": platform.version()} self.data["qt_version"] = QT_VERSION_STR self.data["pyqt_version"] = PYQT_VERSION_STR - self.data["locale_os"] = locale.getlocale(locale.LC_MESSAGES)[0] if hasattr(locale, 'LC_MESSAGES') else locale.getdefaultlocale()[0] + self.data["locale_os"] = locale.getlocale(locale.LC_MESSAGES)[0] if hasattr(locale, "LC_MESSAGES") else locale.getdefaultlocale()[0] self.data["locale_cura"] = self.cura_locale with configure_scope() as scope: diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index c25c7dc0a4..3cc94f83ef 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -25,9 +25,9 @@ class SentryLogger(LogOutput): level = self._translateLogType(log_type[0]) if level is not None: self._show_once.add(message) - add_breadcrumb(level=level, message=message) + add_breadcrumb(level = level, message = message) else: - add_breadcrumb(level=level, message=message) + add_breadcrumb(level = level, message = message) @staticmethod def _pruneSensitiveData(message): From 8fa6239365a598fabd3bb09eeeebcf4f182f3842 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:08:09 +0100 Subject: [PATCH 10/16] Fix spelling mistake --- cura/CrashHandler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 5d07ae05e3..27b486d99a 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -225,7 +225,7 @@ class CrashHandler: self.data["opengl"] = {"version": opengl_instance.getOpenGLVersion(), "vendor": opengl_instance.getGPUVendorName(), "type": opengl_instance.getGPUType()} active_machine_definition_id = "unknown" - active_machine_manufacterer = "unknown" + active_machine_manufacturer = "unknown" try: from cura.CuraApplication import CuraApplication @@ -234,10 +234,10 @@ class CrashHandler: global_stack = machine_manager.activeMachine if global_stack is None: active_machine_definition_id = "empty" - active_machine_manufacterer = "empty" + active_machine_manufacturer = "empty" else: active_machine_definition_id = global_stack.definition.getId() - active_machine_manufacterer = global_stack.definition.getMetaDataEntry("manufacturer", "unknown") + active_machine_manufacturer = global_stack.definition.getMetaDataEntry("manufacturer", "unknown") except: pass @@ -246,7 +246,7 @@ class CrashHandler: scope.set_tag("gpu_vendor", opengl_instance.getGPUVendorName()) scope.set_tag("gpu_type", opengl_instance.getGPUType()) scope.set_tag("active_machine", active_machine_definition_id) - scope.set_tag("active_machine_manufacterer", active_machine_manufacterer) + scope.set_tag("active_machine_manufacturer", active_machine_manufacturer) return info From 7c9d7a3aef068a77d4842949675f7443dd5b9027 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:11:28 +0100 Subject: [PATCH 11/16] Clean up the _translateLogType This makes it a bit easier to read --- plugins/SentryLogger/SentryLogger.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index 3cc94f83ef..76d6b4077b 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -9,7 +9,16 @@ import os home_dir = os.path.expanduser("~") + class SentryLogger(LogOutput): + _levels = { + "w": "warning", + "i": "info", + "c": "fatal", + "e": "error", + "d": "debug" + } + def __init__(self) -> None: super().__init__() self._show_once = set() # type: Set[str] @@ -37,16 +46,4 @@ class SentryLogger(LogOutput): @staticmethod def _translateLogType(log_type: str) -> Optional[str]: - level = None - if log_type == "w": - level = "warning" - elif log_type == "i": - level = "info" - elif log_type == "c": - level = "fatal" - elif log_type == "e": - level = "error" - elif log_type == "d": - level = "debug" - - return level + return SentryLogger._levels.get(log_type) From 62ea766880cd2ea1022d1e7a6cf78262b69b98df Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:12:19 +0100 Subject: [PATCH 12/16] Rename to --- plugins/SentryLogger/SentryLogger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index 76d6b4077b..93db965a17 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -41,7 +41,7 @@ class SentryLogger(LogOutput): @staticmethod def _pruneSensitiveData(message): if home_dir in message: - message = message.replace(home_dir, "") + message = message.replace(home_dir, "") return message @staticmethod From ffccca257f98c1f8aa2c9b6f02395b851c4db1d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:15:25 +0100 Subject: [PATCH 13/16] Add some documentation to SentryLogger --- plugins/SentryLogger/SentryLogger.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/SentryLogger/SentryLogger.py b/plugins/SentryLogger/SentryLogger.py index 93db965a17..8367cfc26e 100644 --- a/plugins/SentryLogger/SentryLogger.py +++ b/plugins/SentryLogger/SentryLogger.py @@ -11,6 +11,12 @@ home_dir = os.path.expanduser("~") class SentryLogger(LogOutput): + # Sentry (https://sentry.io) is the service that Cura uses for logging crashes. This logger ensures that the + # regular log entries that we create are added as breadcrumbs so when a crash actually happens, they are already + # processed and ready for sending. + # Note that this only prepares them for sending. It only sends them when the user actually agrees to sending the + # information. + _levels = { "w": "warning", "i": "info", From c15599f1c4e082c6fa46bd9b8143659e6cd684ef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 16 Dec 2019 11:23:12 +0100 Subject: [PATCH 14/16] Add some extra info to crash handler screen --- cura/CrashHandler.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 27b486d99a..aa53ca0cc5 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -182,23 +182,27 @@ class CrashHandler: self.cura_version = catalog.i18nc("@label unknown version of Cura", "Unknown") self.cura_locale = "??_??" - crash_info = "" + catalog.i18nc("@label Cura version number", "Cura version") + ": " + str(self.cura_version) + "
" - crash_info += "" + catalog.i18nc("@label Type of platform", "Platform") + ": " + str(platform.platform()) + "
" - crash_info += "" + catalog.i18nc("@label", "Qt version") + ": " + str(QT_VERSION_STR) + "
" - crash_info += "" + catalog.i18nc("@label", "PyQt version") + ": " + str(PYQT_VERSION_STR) + "
" - crash_info += "" + catalog.i18nc("@label OpenGL version", "OpenGL") + ": " + str(self._getOpenGLInfo()) + "
" - label.setText(crash_info) - - layout.addWidget(label) - group.setLayout(layout) - self.data["cura_version"] = self.cura_version self.data["os"] = {"type": platform.system(), "version": platform.version()} self.data["qt_version"] = QT_VERSION_STR self.data["pyqt_version"] = PYQT_VERSION_STR - self.data["locale_os"] = locale.getlocale(locale.LC_MESSAGES)[0] if hasattr(locale, "LC_MESSAGES") else locale.getdefaultlocale()[0] + self.data["locale_os"] = locale.getlocale(locale.LC_MESSAGES)[0] if hasattr(locale, "LC_MESSAGES") else \ + locale.getdefaultlocale()[0] self.data["locale_cura"] = self.cura_locale + crash_info = "" + catalog.i18nc("@label Cura version number", "Cura version") + ": " + str(self.cura_version) + "
" + crash_info += "" + catalog.i18nc("@label", "Cura language") + ": " + str(self.cura_locale) + "
" + crash_info += "" + catalog.i18nc("@label", "OS language") + ": " + str(self.data["locale_os"]) + "
" + crash_info += "" + catalog.i18nc("@label Type of platform", "Platform") + ": " + str(platform.platform()) + "
" + crash_info += "" + catalog.i18nc("@label", "Qt version") + ": " + str(QT_VERSION_STR) + "
" + crash_info += "" + catalog.i18nc("@label", "PyQt version") + ": " + str(PYQT_VERSION_STR) + "
" + crash_info += "" + catalog.i18nc("@label OpenGL version", "OpenGL") + ": " + str(self._getOpenGLInfo()) + "
" + + label.setText(crash_info) + + layout.addWidget(label) + group.setLayout(layout) + with configure_scope() as scope: scope.set_tag("qt_version", QT_VERSION_STR) scope.set_tag("pyqt_version", PYQT_VERSION_STR) From 8dc6e7a38670d81e919d4b3a817834308d6029b0 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 19 Dec 2019 09:45:35 +0100 Subject: [PATCH 15/16] Add encvironment variable to Sentry crash logging CURA-7050 --- cura_app.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cura_app.py b/cura_app.py index 218a63b24d..4909f1296d 100755 --- a/cura_app.py +++ b/cura_app.py @@ -23,7 +23,18 @@ parser.add_argument("--debug", ) known_args = vars(parser.parse_known_args()[0]) + +sentry_env = "production" +if ApplicationMetadata.CuraVersion == "master": + sentry_env = "development" +try: + if ApplicationMetadata.CuraVersion.split(".")[2] == "99": + sentry_env = "nightly" +except IndexError: + pass + sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", + environment = sentry_env, release = "cura%s" % ApplicationMetadata.CuraVersion, default_integrations = False, max_breadcrumbs = 200) From 0295113b3103486af9f55a5dd333c8d103df1059 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 19 Dec 2019 11:23:39 +0100 Subject: [PATCH 16/16] Add user (identified by MAC) & ensure that no personal info is in server_name --- cura/CrashHandler.py | 4 +++- cura_app.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index aa53ca0cc5..09fda25a73 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -7,7 +7,7 @@ import faulthandler import tempfile import os import os.path -import time +import uuid import json import locale from typing import cast @@ -212,6 +212,8 @@ class CrashHandler: scope.set_tag("locale_cura", self.cura_locale) scope.set_tag("is_enterprise", ApplicationMetadata.IsEnterpriseVersion) + scope.set_user({"id": str(uuid.getnode())}) + return group def _getOpenGLInfo(self): diff --git a/cura_app.py b/cura_app.py index 4909f1296d..51f9041e86 100755 --- a/cura_app.py +++ b/cura_app.py @@ -37,7 +37,8 @@ sentry_sdk.init("https://5034bf0054fb4b889f82896326e79b13@sentry.io/1821564", environment = sentry_env, release = "cura%s" % ApplicationMetadata.CuraVersion, default_integrations = False, - max_breadcrumbs = 200) + max_breadcrumbs = 300, + server_name = "cura") if not known_args["debug"]: def get_cura_dir_path():