Group tool templating exceptions in sentry

This will properly group tool templating exceptions.
The added tags should make it easy to prioritize and distribute fixes.
Also improves the exception if not using sentry:
```
...
Cheetah.Parser.ParseError:

Some #directives are missing their corresponding #end ___ tag: for
Line 3, column 4

Line|Cheetah Code
----|-------------------------------------------------------------
2   |        moo
3   |
        ^

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/mvandenb/src/galaxy/lib/galaxy/jobs/runners/__init__.py", line 297, in prepare_job
    job_wrapper.prepare()
  File "/Users/mvandenb/src/galaxy/lib/galaxy/jobs/__init__.py", line 1260, in prepare
    ) = tool_evaluator.build()
  File "/Users/mvandenb/src/galaxy/lib/galaxy/tools/evaluation.py", line 611, in build
    global_tool_logs(self._build_command_line, config_file, "Building Command Line", self.tool)
  File "/Users/mvandenb/src/galaxy/lib/galaxy/tools/evaluation.py", line 111, in global_tool_logs
    raise ToolTemplatingException(
galaxy.tools.evaluation.ToolTemplatingException: Error occurred while building command line for tool 'cheetah_problem_syntax_error'
```
This commit is contained in:
mvdbeek
2024-07-19 15:15:16 +02:00
parent 2f6d924b32
commit 385f752617
2 changed files with 59 additions and 13 deletions
+23
View File
@@ -147,6 +147,7 @@ from galaxy.tools.cache import ToolCache
from galaxy.tools.data import ToolDataTableManager
from galaxy.tools.data_manager.manager import DataManagers
from galaxy.tools.error_reports import ErrorReports
from galaxy.tools.evaluation import ToolTemplatingException
from galaxy.tools.search import ToolBoxSearch
from galaxy.tools.special_tools import load_lib_tools
from galaxy.tours import (
@@ -227,12 +228,34 @@ class SentryClientMixin:
level=logging.INFO, # Capture info and above as breadcrumbs
event_level=getattr(logging, event_level), # Send errors as events
)
def before_send(event, hint):
if "exc_info" in hint:
exc_type, exc_value, tb = hint["exc_info"]
if isinstance(exc_value, ToolTemplatingException):
# We set a custom fingerprint that may look like:
# ["Error occurred while {action_str.lower()} for tool '{tool.id}'",
# "1.0",
# "cannot find 'file_name' while searching for 'species_chromosomes.file_name'"]
# If we don't do this issues are never properly grouped since by default the calling stack is inspected,
# and that is always unique in cheetah as it is dynamically generated.
event["fingerprint"] = [str(exc_value), str(exc_value.tool_version), str(exc_value.__cause__)]
event.setdefault("tags", {}).update(
{
"tool_is_latest": exc_value.is_latest,
"tool_id": str(exc_value.tool_id),
"tool_version": exc_value.tool_version,
}
)
return event
self.sentry_client = sentry_sdk.init(
self.config.sentry_dsn,
release=f"{self.config.version_major}.{self.config.version_minor}",
integrations=[sentry_logging],
traces_sample_rate=self.config.sentry_traces_sample_rate,
ca_certs=self.config.sentry_ca_certs,
before_send=before_send,
)
+36 -13
View File
@@ -12,6 +12,7 @@ from typing import (
Dict,
List,
Optional,
TYPE_CHECKING,
Union,
)
@@ -70,6 +71,9 @@ from galaxy.util.template import (
from galaxy.util.tree_dict import TreeDict
from galaxy.work.context import WorkRequestContext
if TYPE_CHECKING:
from galaxy.tools import Tool
log = logging.getLogger(__name__)
@@ -89,13 +93,27 @@ class ToolErrorLog:
global_tool_errors = ToolErrorLog()
def global_tool_logs(func, config_file, action_str):
class ToolTemplatingException(Exception):
def __init__(self, *args: object, tool_id: Optional[str], tool_version: str, is_latest: bool) -> None:
super().__init__(*args)
self.tool_id = tool_id
self.tool_version = tool_version
self.is_latest = is_latest
def global_tool_logs(func, config_file: str, action_str: str, tool: "Tool"):
try:
return func()
except Exception as e:
# capture and log parsing errors
global_tool_errors.add_error(config_file, action_str, e)
raise e
raise ToolTemplatingException(
f"Error occurred while {action_str.lower()} for tool '{tool.id}'",
tool_id=tool.id,
tool_version=tool.version,
is_latest=tool.is_latest_version,
) from e
DeferrableObjectsT = Union[
@@ -581,13 +599,18 @@ class ToolEvaluator:
"""
config_file = self.tool.config_file
global_tool_logs(
self._create_interactivetools_entry_points, config_file, "Building Interactive Tool Entry Points"
self._create_interactivetools_entry_points, config_file, "Building Interactive Tool Entry Points", self.tool
)
global_tool_logs(self._build_config_files, config_file, "Building Config Files")
global_tool_logs(self._build_param_file, config_file, "Building Param File")
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
global_tool_logs(
self._build_config_files,
config_file,
"Building Config Files",
self.tool,
)
global_tool_logs(self._build_param_file, config_file, "Building Param File", self.tool)
global_tool_logs(self._build_command_line, config_file, "Building Command Line", self.tool)
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line", self.tool)
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables", self.tool)
return (
self.command_line,
self.version_command_line,
@@ -836,7 +859,7 @@ class PartialToolEvaluator(ToolEvaluator):
def build(self):
config_file = self.tool.config_file
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables", self.tool)
return (
self.command_line,
self.version_command_line,
@@ -857,10 +880,10 @@ class RemoteToolEvaluator(ToolEvaluator):
def build(self):
config_file = self.tool.config_file
global_tool_logs(self._build_config_files, config_file, "Building Config Files")
global_tool_logs(self._build_param_file, config_file, "Building Param File")
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
global_tool_logs(self._build_config_files, config_file, "Building Config Files", self.tool)
global_tool_logs(self._build_param_file, config_file, "Building Param File", self.tool)
global_tool_logs(self._build_command_line, config_file, "Building Command Line", self.tool)
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line", self.tool)
return (
self.command_line,
self.version_command_line,