diff --git a/lib/galaxy/tools/error_reports/plugins/sentry.py b/lib/galaxy/tools/error_reports/plugins/sentry.py index 96622b9361f6..21b914e0be01 100644 --- a/lib/galaxy/tools/error_reports/plugins/sentry.py +++ b/lib/galaxy/tools/error_reports/plugins/sentry.py @@ -1,31 +1,19 @@ -"""The module describes the ``sentry`` error plugin plugin.""" +"""The module describes the ``sentry`` error plugin.""" import logging +from typing import Dict try: import sentry_sdk except ImportError: sentry_sdk = None -from galaxy import web from galaxy.util import string_as_bool from . import ErrorPlugin log = logging.getLogger(__name__) SENTRY_SDK_IMPORT_MESSAGE = "The Python sentry-sdk package is required to use this feature, please install it" -ERROR_TEMPLATE = """Galaxy Job Error: {tool_id} v{tool_version} - -Command Line: -{command_line} - -Stderr: -{stderr} - -Stdout: -{stdout} - -The user provided the following information: -{message}""" +ERROR_TEMPLATE = "Galaxy Job Error: {tool_name}" class SentryPlugin(ErrorPlugin): @@ -41,8 +29,18 @@ def __init__(self, **kwargs): assert sentry_sdk, SENTRY_SDK_IMPORT_MESSAGE def submit_report(self, dataset, job, tool, **kwargs): - """Submit the error report to sentry""" - extra = { + """Submit the error report to Sentry.""" + tool_name = ( + job.tool_id + if not job.tool_id.endswith(f"/{job.tool_version}") + else job.tool_id[: -len(job.tool_version) - 1] + ) # strip the tool's version from its long id + + # Add contexts to the report. + contexts: Dict[str, dict] = dict() + + # - "job" context + contexts["job"] = { "info": job.info, "id": job.id, "command_line": job.command_line, @@ -56,61 +54,56 @@ def submit_report(self, dataset, job, tool, **kwargs): "tool_version": job.tool_version, "tool_xml": tool.config_file if tool else None, } - if self.redact_user_details_in_bugreport: - extra["email"] = "redacted" - else: - if "email" in kwargs: - extra["email"] = kwargs["email"] - # User submitted message - extra["message"] = kwargs.get("message", "") + # - "feedback" context + # The User Feedback API https://docs.sentry.io/api/projects/submit-user-feedback/ would be a better approach for + # this. + if "message" in kwargs: + contexts["feedback"] = { + "message": kwargs["message"], + } - # Construct the error message to send to sentry. The first line - # will be the issue title, everything after that becomes the - # "message" - error_message = ERROR_TEMPLATE.format(**extra) - - # Update context with user information in a sentry-specific manner - context = {} - - # Getting the url allows us to link to the dataset info page in case - # anything is missing from this report. - try: - url = web.url_for( - controller="dataset", - action="show_params", - dataset_id=self.app.security.encode_id(dataset.id), - qualified=True, - ) - except AttributeError: - # The above does not work when handlers are separate from the web handlers - url = None + for name, context in contexts.items(): + sentry_sdk.set_context(name, context) + # Add user information to the report. user = job.get_user() - if self.redact_user_details_in_bugreport: - if user: - # Opaque identifier - context["user"] = {"id": user.id} - else: - if user: - # User information here also places email links + allows seeing - # a list of affected users in the tags/filtering. - context["user"] = { - "name": user.username, + sentry_user = { + "id": user.id if user else None, + } + if user and not self.redact_user_details_in_bugreport: + sentry_user.update( + { + "username": user.username, "email": user.email, } + ) + sentry_sdk.set_user(sentry_user) + + # Add tags to the report. + tags = { + "tool": job.tool_id, + "tool.name": tool_name, + "tool.version": job.tool_version, + "feedback": "yes" if "message" in kwargs else "no", + } + for name, value in tags.items(): + sentry_sdk.set_tag(name, value) - context["request"] = {"url": url} + # Construct the error message to send to sentry. The first line + # will be the issue title, everything after that becomes the + # "message". + error_message = ERROR_TEMPLATE.format( + tool_name=tool_name, + ) - for key, value in context.items(): - sentry_sdk.set_context(key, value) - sentry_sdk.set_context("job", extra) - sentry_sdk.set_tag("tool_id", job.tool_id) - sentry_sdk.set_tag("tool_version", job.tool_version) + # Send the report as an error. + sentry_sdk.set_level("error") - # Send the message, using message because + # Send the report. response = sentry_sdk.capture_message(error_message) - return (f"Submitted bug report to Sentry. Your guru meditation number is {response}", "success") + + return f"Submitted bug report to Sentry. Your guru meditation number is {response}", "success" __all__ = ("SentryPlugin",)