def prerun_callback(sender=None, headers=None, body=None, **kwargs): name = kwargs["task"].name tr = TrackedRequest.instance() tr.mark_real_request() span = tr.start_span(operation=("Job/" + name)) span.tag("queue", "default")
def worker_result(self, worker_ctx, result=None, exc_info=None): if self._do_nothing: return tracked_request = TrackedRequest.instance() if exc_info: tracked_request.tag("error", "true") elif isinstance(worker_ctx.entrypoint, HttpRequestHandler): # Handle the cases that HttpRequestHandler.response_from_result # does if isinstance(result, Response): status_code = result.status_code elif isinstance(result, tuple): if len(result) == 3: status_code, _headers, _payload = result elif len(result) == 2: status_code, _payload = result else: # Nameko doesn't support other formats, so we know it will # turn this into an error status_code = 500 else: status_code = 200 if 500 <= status_code <= 599: tracked_request.tag("error", "true") tracked_request.stop_span()
def executemany_wrapper(wrapped, instance, args, kwargs): """ CursorWrapper.executemany() wrapper for Django < 2.0 """ try: sql, param_list = _extract_sql_param_list(*args, **kwargs) except TypeError: sql = None param_list = None if sql is not None: tracked_request = TrackedRequest.instance() span = tracked_request.start_span(operation="SQL/Many") span.tag("db.statement", sql) try: return wrapped(*args, **kwargs) finally: if sql is not None: tracked_request.stop_span() if tracked_request.n_plus_one_tracker.should_capture_backtrace( sql=sql, duration=span.duration(), count=len(param_list), ): span.capture_backtrace()
def wrap_client_index_method(wrapped, instance, args, kwargs): # elasticsearch-py 7.5.1 changed the order of arguments for client methods, # so to be safe we need to inspect the wrapped method's positional # arguments to see if we should pull it from there if "index" in kwargs: index = kwargs["index"] else: unwrapped = unwrap_decorators(wrapped) pos_args = get_pos_args(unwrapped) try: index_index = pos_args.index("index") except ValueError: # pragma: no cover # This guards against the method not accepting an 'index' argument # but they all do - for now index = "" else: try: index = args[index_index - 1] # subtract 'self' except IndexError: index = "" if isinstance(index, (list, tuple)): index = ",".join(index) if index == "": index = "Unknown" index = index.title() camel_name = "".join(c.title() for c in wrapped.__name__.split("_")) operation = "Elasticsearch/{}/{}".format(index, camel_name) tracked_request = TrackedRequest.instance() with tracked_request.span(operation=operation, ignore_children=True): return wrapped(*args, **kwargs)
async def wrapped_background_call(wrapped, instance, args, kwargs): tracked_request = TrackedRequest.instance() tracked_request.is_real_request = True with tracked_request.span(operation="Job/{}.{}".format( instance.func.__module__, instance.func.__qualname__)): return await wrapped(*args, **kwargs)
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="") track_request_queue_time(queue_time, tracked_request)
def __enter__(self): # type: () -> instrument tracked_request = TrackedRequest.instance() self.span = tracked_request.start_span(operation=self.operation) for key, value in self.tags.items(): self.tag(key, value) return self
def db_execute_hook(execute, sql, params, many, context): """ Database instrumentation hook for Django 2.0+ https://docs.djangoproject.com/en/2.0/topics/db/instrumentation/ """ if many: operation = "SQL/Many" else: operation = "SQL/Query" if sql is not None: tracked_request = TrackedRequest.instance() span = tracked_request.start_span(operation=operation) span.tag("db.statement", sql) try: return execute(sql, params, many, context) finally: if sql is not None: tracked_request.stop_span() if tracked_request.n_plus_one_tracker.should_capture_backtrace( sql=sql, duration=span.duration(), count=(1 if not many else len(params)), ): span.capture_backtrace()
def __call__(self, request): """ Wrap a single incoming request with start and stop calls. This will start timing, but relies on the process_view callback to capture more details about what view was really called, and other similar info. If process_view isn't called, then the request will not be recorded. This can happen if a middleware further along the stack doesn't call onward, and instead returns a response directly. """ if not scout_config.value("monitor"): return self.get_response(request) tracked_request = TrackedRequest.instance() # This operation name won't be recorded unless changed later in # process_view with tracked_request.span(operation="Unknown", should_capture_backtrace=False): response = self.get_response(request) track_request_view_data(request, tracked_request) if 500 <= response.status_code <= 599: tracked_request.tag("error", "true") return response
def tracked_request(): request = TrackedRequest.instance() request.start_span() # prevent request from finalizing try: yield request finally: request.stop_span()
def process_exception(self, request, exception): tracked_request = TrackedRequest.instance() if (hasattr(request, "scout_view_span") and tracked_request.current_span() == request.scout_view_span): tracked_request.tag("error", "true") tracked_request.stop_span()
def prerun_callback(task=None, **kwargs): tracked_request = TrackedRequest.instance() tracked_request.is_real_request = True start = getattr(task.request, "scout_task_start", None) if start is not None: now = datetime_to_timestamp(dt.datetime.utcnow()) try: queue_time = now - start except TypeError: pass else: tracked_request.tag("queue_time", queue_time) task_id = getattr(task.request, "id", None) if task_id: tracked_request.tag("task_id", task_id) parent_task_id = getattr(task.request, "parent_id", None) if parent_task_id: tracked_request.tag("parent_task_id", parent_task_id) delivery_info = task.request.delivery_info tracked_request.tag("is_eager", delivery_info.get("is_eager", False)) tracked_request.tag("exchange", delivery_info.get("exchange", "unknown")) tracked_request.tag("routing_key", delivery_info.get("routing_key", "unknown")) tracked_request.tag("queue", delivery_info.get("queue", "unknown")) tracked_request.start_span(operation=("Job/" + task.name))
def wrapper(*args, **kwargs): try: tr = TrackedRequest.instance() tr.mark_real_request() path = 'Unknown' if request.route.name is not None: path = request.route.name else: path = request.route.rule if path == '/': path = '/home' if not path.startswith('/'): path = '/{}'.format(path) span = tr.start_span(operation='Controller{}'.format(path)) try: Context.add('path', path) Context.add('user_ip', request.remote_addr) except: pass try: response = callback(*args, **kwargs) except: tr.tag('error', 'true') raise finally: tr.stop_span() return response
def sync_home(db: Session = Depends(get_db)): from scout_apm.core.tracked_request import TrackedRequest print("sync", TrackedRequest.instance()) x = [] # for i in range(-100, 0): # x.append(len(db.query(User).filter(User.id >= i).all())) return {"users": len(db.query(User).all())}
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany): tr = TrackedRequest.instance() span = tr.current_span() if span is not None: tr.callset.update(statement, 1, span.duration()) if tr.callset.should_capture_backtrace(statement): span.capture_backtrace() tr.stop_span()
def test_ignore_transaction(): tr = TrackedRequest.instance() ignore_transaction() tr.finish() assert tr.tags["ignore_transaction"]
def test_background_transaction_start_stop(): tr = TrackedRequest.instance() BackgroundTransaction.start("Foo") BackgroundTransaction.stop() span = tr.complete_spans[-1] assert span.operation == "Job/Foo"
def test_web_transaction_start_stop(): tr = TrackedRequest.instance() WebTransaction.start("Foo") WebTransaction.stop() span = tr.complete_spans[-1] assert span.operation == "Controller/Foo"
def test_background_transaction_manual(): tr = TrackedRequest.instance() scout_apm.api.BackgroundTransaction.start("Foo") scout_apm.api.BackgroundTransaction.stop() span = tr.complete_spans[-1] assert (span.operation == "Job/Foo")
def test_web_transaction_manual(): tr = TrackedRequest.instance() scout_apm.api.WebTransaction.start("Foo") scout_apm.api.WebTransaction.stop() span = tr.complete_spans[-1] assert (span.operation == "Controller/Foo")
def wrapped_execute(wrapped, instance, args, kwargs): tracked_request = TrackedRequest.instance() tracked_request.start_span(operation="Redis/MULTI") try: return wrapped(*args, **kwargs) finally: tracked_request.stop_span()
def execute(original, self, *args, **kwargs): tr = TrackedRequest.instance() tr.start_span(operation='Redis/MULTI') try: return original(*args, **kwargs) finally: tr.stop_span()
def wrap_collection_method(wrapped, instance, args, kwargs): tracked_request = TrackedRequest.instance() camel_name = "".join(c.title() for c in wrapped.__name__.split("_")) operation = "MongoDB/{}.{}".format(instance.name, camel_name) with tracked_request.span(operation=operation, ignore_children=True) as span: span.tag("name", instance.name) return wrapped(*args, **kwargs)
def wrapped_render(wrapped, instance, args, kwargs): tracked_request = TrackedRequest.instance() span = tracked_request.start_span(operation="Template/Render") span.tag("name", instance.name) try: return wrapped(*args, **kwargs) finally: tracked_request.stop_span()
def tracing_method(original, self, *args, **kwargs): entry_type, detail = info_func(self, *args, **kwargs) operation = entry_type if detail["name"] is not None: operation = operation + "/" + detail["name"] tr = TrackedRequest.instance() span = tr.start_span(operation=operation) for key in detail: span.tag(key, detail[key]) try: return original(*args, **kwargs) finally: TrackedRequest.instance().stop_span()
def process_exception(request, exception): tr = TrackedRequest.instance() if hasattr(request, 'scout_view_span') and tr.current_span() == request.scout_view_span: tr.tag('error', 'true') tr.stop_span() return None
async def __call__(self, scope, receive, send): if self._do_nothing or scope["type"] != "http": return await self.app(scope, receive, send) tracked_request = TrackedRequest.instance() # Assume the request is real until we determine it's not. This is useful # when the asyncio instrumentation is determining if a new Task should # reuse the existing tracked request. tracked_request.is_real_request = True # Can't name controller until post-routing - see final clause controller_span = tracked_request.start_span(operation="Controller/Unknown") asgi_track_request_data(scope, tracked_request) def grab_extra_data(): if "endpoint" in scope: # Rename top span endpoint = scope["endpoint"] if not hasattr(endpoint, "__qualname__"): endpoint = endpoint.__class__ controller_span.operation = "Controller/{}.{}".format( endpoint.__module__, endpoint.__qualname__, ) else: # Mark the request as not real tracked_request.is_real_request = False # 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): type_ = data.get("type", None) if type_ == "http.response.start" and 500 <= data.get("status", 200) <= 599: tracked_request.tag("error", "true") elif type_ == "http.response.body" and not data.get("more_body", False): # Finish HTTP span when body finishes sending, not later (e.g. # after background tasks) 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()
def render(original, self, *args, **kwargs): tr = TrackedRequest.instance() span = tr.start_span(operation="Template/Render") span.tag("name", self.name) try: return original(*args, **kwargs) finally: tr.stop_span()
def tracked_request(): """ Create a temporary tracked request for the duration of the test. """ request = TrackedRequest.instance() try: yield request finally: request.finish()
def start(cls, kind, name, tags={}): operation = text(kind) + "/" + text(name) tr = TrackedRequest.instance() tr.mark_real_request() span = tr.start_span(operation=operation) for key, value in tags.items(): tr.tag(key, value) return span