From c9087fe21e650892af647940915f3bcfbac693cc Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Mon, 24 Apr 2017 19:25:48 +0000 Subject: [PATCH 1/2] Graphite support --- config/galaxy.ini.sample | 10 +++++ lib/galaxy/config.py | 4 ++ lib/galaxy/dependencies/__init__.py | 2 + .../dependencies/conditional-requirements.txt | 1 + .../web/framework/middleware/graphite.py | 38 +++++++++++++++++++ lib/galaxy/webapps/galaxy/buildapp.py | 9 +++++ 6 files changed, 64 insertions(+) create mode 100644 lib/galaxy/web/framework/middleware/graphite.py diff --git a/config/galaxy.ini.sample b/config/galaxy.ini.sample index 2ad963773a93..e04fef127ec1 100644 --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -875,6 +875,16 @@ use_interactive = True #statsd_port=8125 #statsd_prefix=galaxy +# Log to graphite +# Graphite is an external statistics aggregator (https://github.com/graphite-project/carbon) +# Enabling the following options will cause galaxy to log request timing and +# other statistics to the configured graphite instance. The graphite_prefix is +# useful if you are running multiple Galaxy instances and want to segment +# statistics between them within the same aggregator. +#graphite_host= +#graphite_port=2003 +#graphite_prefix=galaxy + # -- Data Libraries # These library upload options are described in much more detail in the wiki: diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index e6c6273392e1..f0b6295df8a6 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -569,6 +569,10 @@ def __init__( self, **kwargs ): self.statsd_host = kwargs.get( 'statsd_host', '') self.statsd_port = int( kwargs.get( 'statsd_port', 8125 ) ) self.statsd_prefix = kwargs.get( 'statsd_prefix', 'galaxy' ) + # Statistics and profiling with graphite + self.graphite_host = kwargs.get( 'graphite_host', '') + self.graphite_port = int( kwargs.get( 'graphite_port', 2003 ) ) + self.graphite_prefix = kwargs.get( 'graphite_prefix', 'galaxy' ) # Logging with fluentd self.fluent_log = string_as_bool( kwargs.get( 'fluent_log', False ) ) self.fluent_host = kwargs.get( 'fluent_host', 'localhost' ) diff --git a/lib/galaxy/dependencies/__init__.py b/lib/galaxy/dependencies/__init__.py index 66b765343949..9d04ddbd4b8a 100644 --- a/lib/galaxy/dependencies/__init__.py +++ b/lib/galaxy/dependencies/__init__.py @@ -91,6 +91,8 @@ def check_raven( self ): def check_statsd( self ): return self.config.get("statsd_host", None) is not None + def check_graphite( self ): + return self.config.get("graphite_host", None) is not None def check_weberror( self ): return ( asbool( self.config["debug"] ) and asbool( self.config["use_interactive"] ) ) diff --git a/lib/galaxy/dependencies/conditional-requirements.txt b/lib/galaxy/dependencies/conditional-requirements.txt index ba690314d208..981ffc26c0bc 100644 --- a/lib/galaxy/dependencies/conditional-requirements.txt +++ b/lib/galaxy/dependencies/conditional-requirements.txt @@ -9,6 +9,7 @@ raven pbs_python drmaa statsd +graphitesend azure-storage==0.32.0 # PyRods not in PyPI python-ldap==2.4.27 diff --git a/lib/galaxy/web/framework/middleware/graphite.py b/lib/galaxy/web/framework/middleware/graphite.py new file mode 100644 index 000000000000..d97dc45cc9cf --- /dev/null +++ b/lib/galaxy/web/framework/middleware/graphite.py @@ -0,0 +1,38 @@ +""" +Middleware for sending request statistics to graphite +""" +from __future__ import absolute_import + +import time + +try: + import graphitesend +except ImportError: + # This middleware will never be used without graphite. This block allows + # unit tests pass on systems without it. + graphitesend = None + + +class GraphiteMiddleware(object): + """ + This middleware will log request durations to the configured graphite + instance. + """ + + def __init__(self, + application, + graphite_host, + graphite_port, + graphite_prefix): + if not graphitesend: + raise ImportError("graphite middleware configured, but no graphite python module found. " + "Please install the python graphitesend module to use this functionality.") + self.application = application + self.graphite_client = graphitesend.init(graphite_server=graphite_host, graphite_port=int(graphite_port), prefix=graphite_prefix.rstrip('.')) + + def __call__(self, environ, start_response): + start_time = time.time() + req = self.application(environ, start_response) + dt = int((time.time() - start_time) * 1000) + self.graphite_client.send(environ.get('controller_action_key', None) or environ.get('PATH_INFO', "NOPATH").strip('/').replace('/', '.'), dt) + return req diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 946054c9a7b1..f4fc53a4fe14 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -892,6 +892,15 @@ def wrap_in_middleware( app, global_conf, application_stack, **local_conf ): conf.get('statsd_port', 8125), conf.get('statsd_prefix', 'galaxy') ) ) log.debug( "Enabling 'statsd' middleware" ) + # graphite request timing and profiling + graphite_host = conf.get('graphite_host', None) + if graphite_host: + from galaxy.web.framework.middleware.graphite import GraphiteMiddleware + app = wrap_if_allowed( app, stack, GraphiteMiddleware, + args=( graphite_host, + conf.get('graphite_port', 2003), + conf.get('graphite_prefix', 'galaxy') ) ) + log.debug( "Enabling 'graphite' middleware" ) # If we're using remote_user authentication, add middleware that # protects Galaxy from improperly configured authentication in the # upstream server From 26c06880c2d49bda0c03c0ec11f39e6a529a962b Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Mon, 24 Apr 2017 20:14:13 +0000 Subject: [PATCH 2/2] flake8 --- lib/galaxy/dependencies/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/dependencies/__init__.py b/lib/galaxy/dependencies/__init__.py index 9d04ddbd4b8a..883b750dcb9a 100644 --- a/lib/galaxy/dependencies/__init__.py +++ b/lib/galaxy/dependencies/__init__.py @@ -93,6 +93,7 @@ def check_statsd( self ): def check_graphite( self ): return self.config.get("graphite_host", None) is not None + def check_weberror( self ): return ( asbool( self.config["debug"] ) and asbool( self.config["use_interactive"] ) )