def process_request(self, req, resp): tracked_request = TrackedRequest.instance() tracked_request.is_real_request = True req.context.scout_tracked_request = tracked_request path = req.path # Falcon URL parameter values are *either* single items or lists url_params = [ (k, v) for k, vs in req.params.items() for v in (vs if isinstance(vs, list) else [vs]) ] tracked_request.tag("path", create_filtered_path(path, url_params)) if ignore_path(path): tracked_request.tag("ignore_transaction", True) # 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 = ( req.get_header("x-forwarded-for", default="").split(",")[0] or req.get_header("client-ip", default="").split(",")[0] or req.remote_addr ) tracked_request.tag("user_ip", user_ip) queue_time = req.get_header("x-queue-start", default="") or req.get_header( "x-request-start", default="" ) tracked_queue_time = track_request_queue_time(queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = req.get_header("x-amzn-trace-id", default="") track_amazon_request_queue_time(amazon_queue_time, tracked_request)
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", "" ) tracked_queue_time = track_request_queue_time(queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = request.headers.get("x-amzn-trace-id", "") track_amazon_request_queue_time(amazon_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 scout_tween(request): tracked_request = TrackedRequest.instance() span = tracked_request.start_span(operation="Controller/Pyramid", should_capture_backtrace=False) try: 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) tracked_queue_time = False queue_time = request.headers.get( "x-queue-start", default="") or request.headers.get( "x-request-start", default="") tracked_queue_time = track_request_queue_time( queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = request.headers.get("x-amzn-trace-id", default="") track_amazon_request_queue_time(amazon_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") finally: tracked_request.stop_span() return response
def wrap_callback(wrapped, instance, args, kwargs): tracked_request = TrackedRequest.instance() tracked_request.is_real_request = True path = request.path # allitems() is an undocumented bottle internal tracked_request.tag("path", create_filtered_path(path, request.query.allitems())) if ignore_path(path): tracked_request.tag("ignore_transaction", True) if request.route.name is not None: controller_name = request.route.name else: controller_name = request.route.rule if controller_name == "/": controller_name = "/home" if not controller_name.startswith("/"): controller_name = "/" + controller_name tracked_request.start_span( operation="Controller{}".format(controller_name), should_capture_backtrace=False) 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.environ.get("REMOTE_ADDR")) tracked_request.tag("user_ip", user_ip) tracked_queue_time = False queue_time = request.headers.get( "x-queue-start", "") or request.headers.get("x-request-start", "") tracked_queue_time = track_request_queue_time(queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = request.headers.get("x-amzn-trace-id", "") track_amazon_request_queue_time(amazon_queue_time, tracked_request) try: value = wrapped(*args, **kwargs) except Exception: tracked_request.tag("error", "true") raise else: if 500 <= response.status_code <= 599: tracked_request.tag("error", "true") return value finally: tracked_request.stop_span()
def process_request(self, request): if not scout_config.value("monitor"): return tracked_request = TrackedRequest.instance() request._scout_tracked_request = tracked_request queue_time = request.META.get( "HTTP_X_QUEUE_START") or request.META.get("HTTP_X_REQUEST_START", "") queue_time_tracked = track_request_queue_time(queue_time, tracked_request) if not queue_time_tracked: track_amazon_request_queue_time( request.META.get("HTTP_X_AMZN_TRACE_ID", ""), tracked_request) tracked_request.start_span(operation="Middleware", should_capture_backtrace=False)
def test_track_amazon_request_queue_time_valid(header_value, tracked_request): start_time = int(datetime_to_timestamp(dt.datetime.utcnow())) - 2 result = track_amazon_request_queue_time( header_value.format(start_time=start_time), tracked_request) assert result is True queue_time_ns = tracked_request.tags["scout.queue_time_ns"] assert isinstance(queue_time_ns, int) and queue_time_ns > 0
def test_track_amazon_request_queue_time_valid(header_value, tracked_request): start_time = int(datetime_to_timestamp(dt.datetime.utcnow())) - 2 result = track_amazon_request_queue_time( header_value.format(start_time=start_time), tracked_request) assert result is True queue_time_ns = tracked_request.tags["scout.queue_time_ns"] # Upper bound assumes we didn't take more than 2s to run this test... assert queue_time_ns >= 2000000000 and queue_time_ns < 4000000000
def test_track_amazon_request_queue_time_hexidecimal_valid( header_value, tracked_request): start_time = format( int(datetime_to_timestamp(dt.datetime.utcnow())) - 2, "x") result = track_amazon_request_queue_time( "Root=1-{start_time}-12456789abcdef012345678".format( start_time=start_time), tracked_request, ) assert result is True queue_time_ns = tracked_request.tags["scout.queue_time_ns"] assert isinstance(queue_time_ns, int) and queue_time_ns > 0
def process_request(self, req, resp): if self._do_nothing: return if self.api is None and self.hug_http_interface is not None: self.api = self.hug_http_interface.falcon tracked_request = TrackedRequest.instance() tracked_request.is_real_request = True req.context.scout_tracked_request = tracked_request tracked_request.start_span(operation="Middleware", should_capture_backtrace=False) path = req.path # Falcon URL parameter values are *either* single items or lists url_params = [(k, v) for k, vs in req.params.items() for v in (vs if isinstance(vs, list) else [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 = (req.get_header("x-forwarded-for", default="").split(",")[0] or req.get_header("client-ip", default="").split(",")[0] or req.remote_addr) tracked_request.tag("user_ip", user_ip) queue_time = req.get_header( "x-queue-start", default="") or req.get_header("x-request-start", default="") tracked_queue_time = track_request_queue_time(queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = req.get_header("x-amzn-trace-id", default="") track_amazon_request_queue_time(amazon_queue_time, tracked_request)
def test_track_amazon_request_queue_time_invalid(header_value, tracked_request): result = track_amazon_request_queue_time(header_value, tracked_request) assert result is False assert "scout.queue_time_ns" not in tracked_request.tags
async def __call__(self, scope, receive, send): if self._do_nothing or scope["type"] != "http": await self.app(scope, receive, send) return request = Request(scope) tracked_request = TrackedRequest.instance() # Can't name controller until post-routing - see final clause controller_span = tracked_request.start_span(operation="Controller/Unknown") tracked_request.tag( "path", create_filtered_path(request.url.path, request.query_params.multi_items()), ) if ignore_path(request.url.path): tracked_request.tag("ignore_transaction", True) user_ip = ( request.headers.get("x-forwarded-for", default="").split(",")[0] or request.headers.get("client-ip", default="").split(",")[0] or request.client.host ) tracked_request.tag("user_ip", user_ip) queue_time = request.headers.get( "x-queue-start", default="" ) or request.headers.get("x-request-start", default="") tracked_queue_time = track_request_queue_time(queue_time, tracked_request) if not tracked_queue_time: amazon_queue_time = request.headers.get("x-amzn-trace-id", default="") track_amazon_request_queue_time(amazon_queue_time, tracked_request) def grab_extra_data(): if "endpoint" in scope: # Rename top span endpoint = scope["endpoint"] controller_span.operation = "Controller/{}.{}".format( endpoint.__module__, endpoint.__qualname__ ) tracked_request.is_real_request = True # From AuthenticationMiddleware - bypass request.user because it # throws AssertionError if 'user' is not in Scope, and we need a # try/except already try: username = scope["user"].display_name except (KeyError, AttributeError): pass else: tracked_request.tag("username", username) async def wrapped_send(data): # Finish HTTP span when body finishes sending, not later (e.g. # after background tasks) if data.get("type", None) == "http.response.body" and not data.get( "more_body", False ): grab_extra_data() tracked_request.stop_span() return await send(data) try: await self.app(scope, receive, wrapped_send) except Exception as exc: tracked_request.tag("error", "true") raise exc finally: if tracked_request.end_time is None: grab_extra_data() tracked_request.stop_span()