Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change Sentry error reporting plug-in #193

Merged
merged 3 commits into from
Sep 28, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 55 additions & 62 deletions lib/galaxy/tools/error_reports/plugins/sentry.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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,
Expand All @@ -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",)