From 9b293ac4381583d6b9ca866fa7bbecc971ef00de Mon Sep 17 00:00:00 2001 From: Nate Coraor Date: Tue, 30 Nov 2021 15:04:32 -0500 Subject: [PATCH] Refactor Sentry middleware/client changes for Tool Shed --- lib/galaxy/app.py | 48 ++++++++++--------- lib/galaxy/config/sample/tool_shed.yml.sample | 4 ++ lib/tool_shed/webapp/app.py | 5 +- lib/tool_shed/webapp/buildapp.py | 4 +- lib/tool_shed/webapp/config.py | 3 +- lib/tool_shed/webapp/config_schema.yml | 8 ++++ 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/lib/galaxy/app.py b/lib/galaxy/app.py index b238a57737c..e8a89669ba9 100644 --- a/lib/galaxy/app.py +++ b/lib/galaxy/app.py @@ -97,7 +97,31 @@ class HaltableContainer(Container): raise exception -class MinimalGalaxyApplication(BasicApp, config.ConfiguresGalaxyMixin, HaltableContainer): +class SentryClientMixin: + def configure_sentry_client(self): + self.sentry_client = None + if self.config.sentry_dsn: + event_level = self.config.sentry_event_level.upper() + assert event_level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], f"Invalid sentry event level '{self.config.sentry.event_level}'" + + def postfork_sentry_client(): + import sentry_sdk + from sentry_sdk.integrations.logging import LoggingIntegration + + sentry_logging = LoggingIntegration( + level=logging.INFO, # Capture info and above as breadcrumbs + event_level=getattr(logging, event_level) # Send errors as events + ) + self.sentry_client = sentry_sdk.init( + self.config.sentry_dsn, + release=f"{self.config.version_major}.{self.config.version_minor}", + integrations=[sentry_logging] + ) + + self.application_stack.register_postfork_function(postfork_sentry_client) + + +class MinimalGalaxyApplication(BasicApp, config.ConfiguresGalaxyMixin, HaltableContainer, SentryClientMixin): """Encapsulates the state of a minimal Galaxy application""" def __init__(self, fsmon=False, configure_logging=True, **kwargs) -> None: @@ -190,27 +214,7 @@ class GalaxyManagerApplication(MinimalManagerApp, MinimalGalaxyApplication): self._configure_datatypes_registry(self.installed_repository_manager) self._register_singleton(Registry, self.datatypes_registry) galaxy.model.set_datatypes_registry(self.datatypes_registry) - - self.sentry_client = None - if self.config.sentry_dsn: - event_level = self.config.sentry_event_level.upper() - assert event_level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], f"Invalid sentry event level '{self.config.sentry.event_level}'" - - def postfork_sentry_client(): - import sentry_sdk - from sentry_sdk.integrations.logging import LoggingIntegration - - sentry_logging = LoggingIntegration( - level=logging.INFO, # Capture info and above as breadcrumbs - event_level=getattr(logging, event_level) # Send errors as events - ) - self.sentry_client = sentry_sdk.init( - self.config.sentry_dsn, - release=f"{self.config.version_major}.{self.config.version_minor}", - integrations=[sentry_logging] - ) - - self.application_stack.register_postfork_function(postfork_sentry_client) + self.configure_sentry_client() class UniverseApplication(StructuredApp, GalaxyManagerApplication): diff --git a/lib/galaxy/config/sample/tool_shed.yml.sample b/lib/galaxy/config/sample/tool_shed.yml.sample index 3ba6eb2948e..3b099773899 100644 --- a/lib/galaxy/config/sample/tool_shed.yml.sample +++ b/lib/galaxy/config/sample/tool_shed.yml.sample @@ -415,6 +415,10 @@ tool_shed: # -> Settings -> API Keys. #sentry_dsn: null + # Determines the minimum log level that will be sent as an event to + # Sentry. Possible values are DEBUG, INFO, WARNING, ERROR or CRITICAL. + #sentry_event_level: ERROR + # Galaxy Session Timeout This provides a timeout (in minutes) after # which a user will have to log back in. A duration of 0 disables this # feature. diff --git a/lib/tool_shed/webapp/app.py b/lib/tool_shed/webapp/app.py index e7531115e6e..6a963c4e34c 100644 --- a/lib/tool_shed/webapp/app.py +++ b/lib/tool_shed/webapp/app.py @@ -11,6 +11,7 @@ import tool_shed.repository_registry import tool_shed.repository_types.registry import tool_shed.webapp.model from galaxy import auth +from galaxy.app import SentryClientMixin from galaxy.config import configure_logging from galaxy.managers.citations import CitationsManager from galaxy.managers.users import UserManager @@ -28,7 +29,7 @@ from . import config log = logging.getLogger(__name__) -class UniverseApplication(BasicApp): +class UniverseApplication(BasicApp, SentryClientMixin): """Encapsulates the state of a Universe application""" def __init__(self, **kwd) -> None: @@ -93,6 +94,8 @@ class UniverseApplication(BasicApp): self.hgweb_config_manager.hgweb_config_dir = self.config.hgweb_config_dir # Initialize the repository registry. self.repository_registry = tool_shed.repository_registry.Registry(self) + # Configure Sentry client if configured + self.configure_sentry_client() # used for cachebusting -- refactor this into a *SINGLE* UniverseApplication base. self.server_starttime = int(time.time()) log.debug("Tool shed hgweb.config file is: %s", self.hgweb_config_manager.hgweb_config) diff --git a/lib/tool_shed/webapp/buildapp.py b/lib/tool_shed/webapp/buildapp.py index bf176432d68..ceb2e35cc06 100644 --- a/lib/tool_shed/webapp/buildapp.py +++ b/lib/tool_shed/webapp/buildapp.py @@ -248,8 +248,8 @@ def wrap_in_middleware(app, global_conf, application_stack, **local_conf): # TODO sentry config is duplicated between tool_shed/galaxy, refactor this. sentry_dsn = conf.get('sentry_dsn', None) if sentry_dsn: - from galaxy.web.framework.middleware.sentry import Sentry - app = wrap_if_allowed(app, stack, Sentry, args=(sentry_dsn,)) + from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware + app = wrap_if_allowed(app, stack, SentryWsgiMiddleware) # X-Forwarded-Host handling from galaxy.web.framework.middleware.xforwardedhost import XForwardedHostMiddleware app = wrap_if_allowed(app, stack, XForwardedHostMiddleware) diff --git a/lib/tool_shed/webapp/config.py b/lib/tool_shed/webapp/config.py index 866017b1a51..cdbb987e126 100644 --- a/lib/tool_shed/webapp/config.py +++ b/lib/tool_shed/webapp/config.py @@ -15,7 +15,7 @@ from galaxy.config import ( from galaxy.config.schema import AppSchema from galaxy.exceptions import ConfigurationError from galaxy.util import string_as_bool -from galaxy.version import VERSION, VERSION_MAJOR +from galaxy.version import VERSION, VERSION_MAJOR, VERSION_MINOR from galaxy.web.formatting import expand_pretty_datetime_format log = logging.getLogger(__name__) @@ -67,6 +67,7 @@ class ToolShedAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin): os.umask(self.umask) # can't get w/o set, so set it back self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster self.version_major = VERSION_MAJOR + self.version_minor = VERSION_MINOR self.version = VERSION # Database related configuration if not self.database_connection: # Provide default if not supplied by user diff --git a/lib/tool_shed/webapp/config_schema.yml b/lib/tool_shed/webapp/config_schema.yml index 3fadb90539a..62060f9103c 100644 --- a/lib/tool_shed/webapp/config_schema.yml +++ b/lib/tool_shed/webapp/config_schema.yml @@ -578,6 +578,14 @@ mapping: indicated sentry instance. This connection string is available in your sentry instance under -> Settings -> API Keys. + sentry_event_level: + type: str + default: ERROR + required: false + desc: | + Determines the minimum log level that will be sent as an event to Sentry. + Possible values are DEBUG, INFO, WARNING, ERROR or CRITICAL. + session_duration: type: int default: 0