Skip to content

Commit

Permalink
Group tool templating exceptions in sentry
Browse files Browse the repository at this point in the history
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'
```
  • Loading branch information
mvdbeek committed Aug 8, 2024
1 parent acd1611 commit 85c4f65
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 13 deletions.
23 changes: 23 additions & 0 deletions lib/galaxy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
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 (
Expand Down Expand Up @@ -227,12 +228,34 @@ def configure_sentry_client(self):
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,
)


Expand Down
49 changes: 36 additions & 13 deletions lib/galaxy/tools/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Dict,
List,
Optional,
TYPE_CHECKING,
Union,
)

Expand Down Expand Up @@ -70,6 +71,9 @@
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__)


Expand All @@ -89,13 +93,27 @@ def add_error(self, file, phase, exception):
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[
Expand Down Expand Up @@ -581,13 +599,18 @@ def build(self):
"""
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",
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_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,
Expand Down Expand Up @@ -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,
Expand All @@ -857,10 +880,10 @@ def execute_tool_hooks(self, inp_data, out_data, incoming):

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,
Expand Down

0 comments on commit 85c4f65

Please sign in to comment.