diff --git a/README.md b/README.md index cf23b2c4..4239a4c3 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,14 @@ Python 2.7 or 3.4+. Scout APM has integrations for the following frameworks: * Bottle 0.12+ -* CherryPy 18.0.0+ * Celery 3.1+ -* Django 1.8+ +* Django 3.2+ * Dramatiq 1.0+ * Falcon 2.0+ * Flask 0.10+ * Huey 2.0+ * Hug 2.5.1+ * Nameko 2.0+ -* Pyramid 1.8+ * RQ 1.0+ * Starlette 0.12+ diff --git a/src/scout_apm/cherrypy.py b/src/scout_apm/cherrypy.py deleted file mode 100644 index b1a6e9f0..00000000 --- a/src/scout_apm/cherrypy.py +++ /dev/null @@ -1,130 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals - -import cherrypy -from cherrypy.lib.encoding import ResponseEncoder -from cherrypy.process import plugins - -import scout_apm.core -from scout_apm.compat import parse_qsl -from scout_apm.core.config import scout_config -from scout_apm.core.tracked_request import TrackedRequest -from scout_apm.core.web_requests import ( - create_filtered_path, - ignore_path, - track_request_queue_time, -) - - -class ScoutPlugin(plugins.SimplePlugin): - def __init__(self, bus): - super(ScoutPlugin, self).__init__(bus) - installed = scout_apm.core.install() - self._do_nothing = not installed - - def before_request(self): - if self._do_nothing: - return - request = cherrypy.request - tracked_request = TrackedRequest.instance() - tracked_request.is_real_request = True - request._scout_tracked_request = tracked_request - - # Can't name operation until after request, when routing has been done - request._scout_controller_span = tracked_request.start_span( - "Controller/Unknown" - ) - - def after_request(self): - if self._do_nothing: - return - request = cherrypy.request - tracked_request = getattr(request, "_scout_tracked_request", None) - if tracked_request is None: - return - - # Rename controller span now routing has been done - operation_name = get_operation_name(request) - if operation_name is not None: - request._scout_controller_span.operation = operation_name - - # Grab general request data now it has been parsed - path = request.path_info - # Parse params ourselves because we want only GET params but CherryPy - # parses POST params (nearly always sensitive) into the same dict. - params = parse_qsl(request.query_string) - tracked_request.tag("path", create_filtered_path(path, params)) - if ignore_path(path): - tracked_request.tag("ignore_transaction", True) - - if scout_config.value("collect_remote_ip"): - # Determine a remote IP to associate with the request. The value is - # spoofable by the requester so this is not suitable to use in any - # security sensitive context. - user_ip = ( - request.headers.get("x-forwarded-for", "").split(",")[0] - or request.headers.get("client-ip", "").split(",")[0] - or (request.remote.ip or None) - ) - tracked_request.tag("user_ip", user_ip) - - queue_time = request.headers.get("x-queue-start", "") or request.headers.get( - "x-request-start", "" - ) - track_request_queue_time(queue_time, tracked_request) - - response = cherrypy.response - status = response.status - if isinstance(status, int): - status_int = status - else: - status_first = status.split(" ", 1)[0] - try: - status_int = int(status_first) - except ValueError: - # Assume OK - status_int = 200 - if 500 <= status_int <= 599: - tracked_request.tag("error", "true") - elif status_int == 404: - tracked_request.is_real_request = False - - tracked_request.stop_span() - - -def get_operation_name(request): - handler = request.handler - if handler is None: - return None - - if isinstance(handler, ResponseEncoder): - real_handler = handler.oldhandler - else: - real_handler = handler - - # Unwrap HandlerWrapperTool classes - while hasattr(real_handler, "callable"): - real_handler = real_handler.callable - - # Looks like it's from HandlerTool - if getattr(real_handler, "__name__", "") == "handle_func": - try: - wrapped_tool = real_handler.__closure__[2].cell_contents.callable - except (AttributeError, IndexError): - pass - else: - try: - return "Controller/{}".format(wrapped_tool.__name__) - except AttributeError: - pass - - # Not a method? Not from an exposed view then - if not hasattr(real_handler, "__self__"): - return None - - real_handler_cls = real_handler.__self__.__class__ - return "Controller/{}.{}.{}".format( - real_handler_cls.__module__, - real_handler_cls.__name__, - real_handler.__name__, - ) diff --git a/src/scout_apm/pyramid.py b/src/scout_apm/pyramid.py deleted file mode 100644 index d76d2772..00000000 --- a/src/scout_apm/pyramid.py +++ /dev/null @@ -1,78 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals - -import scout_apm.core -from scout_apm.core.config import scout_config -from scout_apm.core.tracked_request import TrackedRequest -from scout_apm.core.web_requests import ( - create_filtered_path, - ignore_path, - track_request_queue_time, -) - - -def includeme(config): - configs = {} - pyramid_config = config.get_settings() - for name in pyramid_config: - if name.startswith("SCOUT_"): - value = pyramid_config[name] - clean_name = name.replace("SCOUT_", "").lower() - configs[clean_name] = value - scout_config.set(**configs) - - if scout_apm.core.install(): - config.add_tween("scout_apm.pyramid.instruments") - - -def instruments(handler, registry): - def scout_tween(request): - tracked_request = TrackedRequest.instance() - - with tracked_request.span( - operation="Controller/Pyramid", should_capture_backtrace=False - ) as span: - path = request.path - # mixed() returns values as *either* single items or lists - url_params = [ - (k, v) for k, vs in request.GET.dict_of_lists().items() for v in vs - ] - tracked_request.tag("path", create_filtered_path(path, url_params)) - if ignore_path(path): - tracked_request.tag("ignore_transaction", True) - - if scout_config.value("collect_remote_ip"): - # Determine a remote IP to associate with the request. The value is - # spoofable by the requester so this is not suitable to use in any - # security sensitive context. - user_ip = ( - request.headers.get("x-forwarded-for", default="").split(",")[0] - or request.headers.get("client-ip", default="").split(",")[0] - or request.remote_addr - ) - tracked_request.tag("user_ip", user_ip) - - queue_time = request.headers.get( - "x-queue-start", default="" - ) or request.headers.get("x-request-start", default="") - track_request_queue_time(queue_time, tracked_request) - - try: - try: - response = handler(request) - finally: - # Routing lives further down the call chain. So time it - # starting above, but only set the name if it gets a name - if request.matched_route is not None: - tracked_request.is_real_request = True - span.operation = "Controller/" + request.matched_route.name - except Exception: - tracked_request.tag("error", "true") - raise - - if 500 <= response.status_code <= 599: - tracked_request.tag("error", "true") - - return response - - return scout_tween diff --git a/tests/integration/app.py b/tests/integration/app.py index 6a4c7caa..d4743303 100755 --- a/tests/integration/app.py +++ b/tests/integration/app.py @@ -20,7 +20,7 @@ import logging import scout_apm.api -from tests.integration import test_bottle, test_django, test_flask, test_pyramid +from tests.integration import test_bottle, test_django, test_flask logger = logging.getLogger(__name__) @@ -36,8 +36,6 @@ SUB_APPS["/django"] = app with test_flask.app_with_scout() as app: SUB_APPS["/flask"] = app -with test_pyramid.app_with_scout() as app: - SUB_APPS["/pyramid"] = app def app(environ, start_response): @@ -86,14 +84,6 @@ def app(environ, start_response):