def process_response(self, req, resp, resource, req_succeeded=None): if self.client and req.user_agent != 'mcod-heartbeat': rule = route_to_name(req.uri_template, prefix='api', method=req.method) elasticapm.set_context( lambda: get_data_from_request( req, capture_body=self.client.config.capture_body in ("transactions", "all"), capture_headers=self.client.config.capture_headers, ), "request", ) elasticapm.set_context( lambda: get_data_from_response( resp, capture_headers=self.client.config.capture_headers), "response") result = resp.status elasticapm.set_transaction_name(rule, override=False) if hasattr(req, 'user') and req.user.is_authenticated: elasticapm.set_user_context(email=req.user.email, user_id=req.user.id) elasticapm.set_transaction_result(result, override=False) # Instead of calling end_transaction here, we defer the call until the response is closed. # This ensures that we capture things that happen until the WSGI server closes the response. self.client.end_transaction(rule, result)
async def _request_started(self, request: Request): """Captures the begin of the request processing to APM. Args: request (Request) """ # When we consume the body, we replace the streaming mechanism with # a mocked version -- this workaround came from # https://github.com/encode/starlette/issues/495#issuecomment-513138055 # and we call the workaround here to make sure that regardless of # `capture_body` settings, we will have access to the body if we need it. if self.client.config.capture_body != "off": await get_body(request) if not self.client.should_ignore_url(request.url.path): trace_parent = TraceParent.from_headers(dict(request.headers)) self.client.begin_transaction("request", trace_parent=trace_parent) await set_context( lambda: get_data_from_request(request, self.client.config, constants.TRANSACTION), "request") transaction_name = self.get_route_name(request) or request.url.path elasticapm.set_transaction_name("{} {}".format( request.method, transaction_name), override=False)
async def call(self, module, method, wrapped, instance, args, kwargs): # Late import to avoid ImportErrors from elasticapm.contrib.tornado.utils import get_data_from_request, get_data_from_response request = instance.request trace_parent = TraceParent.from_headers(request.headers) client = instance.application.elasticapm_client client.begin_transaction("request", trace_parent=trace_parent) elasticapm.set_context( lambda: get_data_from_request(instance, request, client.config, constants.TRANSACTION), "request") # TODO: Can we somehow incorporate the routing rule itself here? elasticapm.set_transaction_name("{} {}".format( request.method, type(instance).__name__), override=False) ret = await wrapped(*args, **kwargs) elasticapm.set_context( lambda: get_data_from_response(instance, client.config, constants. TRANSACTION), "response") result = "HTTP {}xx".format(instance.get_status() // 100) elasticapm.set_transaction_result(result, override=False) client.end_transaction() return ret
async def call(self, module, method, wrapped, instance, args, kwargs): if not hasattr(instance.application, "elasticapm_client"): # If tornado was instrumented but not as the main framework # (i.e. in Flower), we should skip it. return await wrapped(*args, **kwargs) # Late import to avoid ImportErrors from elasticapm.contrib.tornado.utils import get_data_from_request, get_data_from_response request = instance.request trace_parent = TraceParent.from_headers(request.headers) client = instance.application.elasticapm_client client.begin_transaction("request", trace_parent=trace_parent) elasticapm.set_context( lambda: get_data_from_request(instance, request, client.config, constants.TRANSACTION), "request") # TODO: Can we somehow incorporate the routing rule itself here? elasticapm.set_transaction_name("{} {}".format( request.method, type(instance).__name__), override=False) ret = await wrapped(*args, **kwargs) elasticapm.set_context( lambda: get_data_from_response(instance, client.config, constants. TRANSACTION), "response") status = instance.get_status() result = "HTTP {}xx".format(status // 100) elasticapm.set_transaction_result(result, override=False) elasticapm.set_transaction_outcome(http_status_code=status) client.end_transaction() return ret
def request_finished(self, app, response): if not self.app.debug or self.client.config.debug: rule = request.url_rule.rule if request.url_rule is not None else "" rule = build_name_with_http_method_prefix(rule, request) elasticapm.set_context( lambda: get_data_from_request( request, capture_body=self.client.config.capture_body in ("transactions", "all"), capture_headers=self.client.config.capture_headers, ), "request", ) elasticapm.set_context( lambda: get_data_from_response(response, capture_headers=self.client. config.capture_headers), "response") if response.status_code: result = "HTTP {}xx".format(response.status_code // 100) else: result = response.status elasticapm.set_transaction_name(rule, override=False) elasticapm.set_transaction_result(result, override=False) # Instead of calling end_transaction here, we defer the call until the response is closed. # This ensures that we capture things that happen until the WSGI server closes the response. response.call_on_close(self.client.end_transaction)
def process_response(self, request, response): if django_settings.DEBUG and not self.client.config.debug: return response try: if hasattr(response, "status_code"): transaction_name = None if self.client.config.django_transaction_name_from_route and hasattr(request.resolver_match, "route"): transaction_name = request.resolver_match.route elif getattr(request, "_elasticapm_view_func", False): transaction_name = get_name_from_func(request._elasticapm_view_func) if transaction_name: transaction_name = build_name_with_http_method_prefix(transaction_name, request) elasticapm.set_transaction_name(transaction_name, override=False) elasticapm.set_context( lambda: self.client.get_data_from_request(request, constants.TRANSACTION), "request" ) elasticapm.set_context( lambda: self.client.get_data_from_response(response, constants.TRANSACTION), "response" ) elasticapm.set_context(lambda: self.client.get_user_info(request), "user") elasticapm.set_transaction_result("HTTP {}xx".format(response.status_code // 100), override=False) elasticapm.set_transaction_outcome(http_status_code=response.status_code, override=False) except Exception: self.client.error_logger.error("Exception during timing of request", exc_info=True) return response
def process_response(self, request: HttpRequest, response: HttpResponse): if django_settings.DEBUG and not self.client.config.debug: return response try: if hasattr(response, "status_code"): if not getattr(request, "_elasticapm_name_set", False): elasticapm.set_transaction_name( self.get_transaction_name(request), override=False) elasticapm.set_context( lambda: self.client.get_data_from_request( request, constants.TRANSACTION), "request") elasticapm.set_context( lambda: self.client.get_data_from_response( response, constants.TRANSACTION), "response") elasticapm.set_context( lambda: self.client.get_user_info(request), "user") elasticapm.set_transaction_result("HTTP {}xx".format( response.status_code // 100), override=False) elasticapm.set_transaction_outcome( http_status_code=response.status_code, override=False) except Exception: self.client.error_logger.error( "Exception during timing of request", exc_info=True) return response
def process_response(self, request, response): if django_settings.DEBUG and not self.client.config.debug: return response try: if hasattr(response, "status_code"): if getattr(request, "_elasticapm_view_func", False): transaction_name = get_name_from_func( request._elasticapm_view_func) transaction_name = build_name_with_http_method_prefix( transaction_name, request) elasticapm.set_transaction_name(transaction_name, override=False) elasticapm.set_context( lambda: self.client.get_data_from_request( request, capture_body=self.client.config.capture_body in ("all", "transactions")), "request", ) elasticapm.set_context( lambda: self.client.get_data_from_response(response), "response") elasticapm.set_context( lambda: self.client.get_user_info(request), "user") elasticapm.set_transaction_result("HTTP {}xx".format( response.status_code // 100), override=False) except Exception: self.client.error_logger.error( "Exception during timing of request", exc_info=True) return response
def process_response(self, request, response): if django_settings.DEBUG and not self.client.config.debug: return response try: if hasattr(response, 'status_code'): if getattr(request, '_elasticapm_view_func', False): transaction_name = get_name_from_func( request._elasticapm_view_func ) transaction_name = build_name_with_http_method_prefix( transaction_name, request ) elasticapm.set_transaction_name(transaction_name, override=False) elasticapm.set_context(lambda: self.client.get_data_from_request( request, capture_body=self.client.config.capture_body in ('all', 'transactions') ), 'request') elasticapm.set_context(lambda: self.client.get_data_from_response(response), 'response') elasticapm.set_context(lambda: self.client.get_user_info(request), 'user') elasticapm.set_transaction_result('HTTP {}xx'.format(response.status_code // 100), override=False) except Exception: self.client.error_logger.error( 'Exception during timing of request', exc_info=True, ) return response
async def handle_request(request, handler): elasticapm_client = app.get(CLIENT_KEY) if elasticapm_client: request[CLIENT_KEY] = elasticapm_client trace_parent = AioHttpTraceParent.from_headers(request.headers) elasticapm_client.begin_transaction("request", trace_parent=trace_parent) resource = request.match_info.route.resource name = request.method if resource: # canonical has been added in 3.3, and returns one of path, formatter, prefix for attr in ("canonical", "_path", "_formatter", "_prefix"): if hasattr(resource, attr): name += " " + getattr(resource, attr) break else: name += " unknown route" else: name += " unknown route" elasticapm.set_transaction_name(name, override=False) elasticapm.set_context( lambda: get_data_from_request( request, capture_body=elasticapm_client.config.capture_body in ("transactions", "all"), capture_headers=elasticapm_client.config.capture_headers, ), "request", ) try: response = await handler(request) elasticapm.set_transaction_result("HTTP {}xx".format( response.status // 100), override=False) elasticapm.set_context( lambda: get_data_from_response( response, capture_headers=elasticapm_client.config.capture_headers), "response", ) return response except Exception: if elasticapm_client: elasticapm_client.capture_exception( context={ "request": get_data_from_request(request, capture_body=elasticapm_client. config.capture_body in ( "all", "errors")) }) elasticapm.set_transaction_result("HTTP 5xx", override=False) elasticapm.set_context({"status_code": 500}, "response") raise finally: elasticapm_client.end_transaction()
async def handle_request(request, handler): elasticapm_client = app.get(CLIENT_KEY) if elasticapm_client: request[CLIENT_KEY] = elasticapm_client trace_parent = AioHttpTraceParent.from_headers(request.headers) elasticapm_client.begin_transaction("request", trace_parent=trace_parent) resource = request.match_info.route.resource name = request.method if resource: # canonical has been added in 3.3, and returns one of path, formatter, prefix for attr in ("canonical", "_path", "_formatter", "_prefix"): if hasattr(resource, attr): name += " " + getattr(resource, attr) break else: name += " unknown route" else: name += " unknown route" elasticapm.set_transaction_name(name, override=False) elasticapm.set_context( lambda: get_data_from_request(request, elasticapm_client. config, constants.TRANSACTION), "request") try: response = await handler(request) elasticapm.set_transaction_result("HTTP {}xx".format( response.status // 100), override=False) elasticapm.set_context( lambda: get_data_from_response(response, elasticapm_client. config, constants.TRANSACTION), "response") return response except Exception as exc: if elasticapm_client: elasticapm_client.capture_exception( context={ "request": get_data_from_request( request, elasticapm_client.config, constants.ERROR) }) elasticapm.set_transaction_result("HTTP 5xx", override=False) elasticapm.set_context({"status_code": 500}, "response") # some exceptions are response-like, e.g. have headers and status code. Let's try and capture them if isinstance(exc, (Response, HTTPException)): elasticapm.set_context( lambda: get_data_from_response( exc, elasticapm_client.config, constants.ERROR ), # noqa: F821 "response", ) raise finally: elasticapm_client.end_transaction()
def __enter__(self): """ Transaction setup """ trace_parent = TraceParent.from_headers(self.event.get("headers", {})) global COLD_START cold_start = COLD_START COLD_START = False self.source = "other" transaction_type = "request" transaction_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME", self.name) self.httpmethod = nested_key( self.event, "requestContext", "httpMethod") or nested_key( self.event, "requestContext", "http", "method") if self.httpmethod: # API Gateway self.source = "api" if os.environ.get("AWS_LAMBDA_FUNCTION_NAME"): transaction_name = "{} {}".format( self.httpmethod, os.environ["AWS_LAMBDA_FUNCTION_NAME"]) else: transaction_name = self.name elif "Records" in self.event and len(self.event["Records"]) == 1: record = self.event["Records"][0] if record.get("eventSource") == "aws:s3": # S3 self.source = "s3" transaction_name = "{} {}".format( record["eventName"], record["s3"]["bucket"]["name"]) elif record.get("EventSource") == "aws:sns": # SNS self.source = "sns" transaction_type = "messaging" transaction_name = "RECEIVE {}".format( record["Sns"]["TopicArn"].split(":")[5]) elif record.get("eventSource") == "aws:sqs": # SQS self.source = "sqs" transaction_type = "messaging" transaction_name = "RECEIVE {}".format( record["eventSourceARN"].split(":")[5]) self.transaction = self.client.begin_transaction( transaction_type, trace_parent=trace_parent) elasticapm.set_transaction_name(transaction_name, override=False) if self.source == "api": elasticapm.set_context( lambda: get_data_from_request( self.event, capture_body=self.client.config.capture_body in ("transactions", "all"), capture_headers=self.client.config.capture_headers, ), "request", ) self.set_metadata_and_context(cold_start)
def request_started(self, app): if (not self.app.debug or self.client.config.debug) and not self.client.should_ignore_url(request.path): trace_parent = TraceParent.from_headers(request.headers) self.client.begin_transaction("request", trace_parent=trace_parent) elasticapm.set_context( lambda: get_data_from_request(request, self.client.config, constants.TRANSACTION), "request" ) rule = request.url_rule.rule if request.url_rule is not None else "" rule = build_name_with_http_method_prefix(rule, request) elasticapm.set_transaction_name(rule, override=False)
def process_request_wrapper(wrapped, instance, args, kwargs): response = wrapped(*args, **kwargs) try: if response is not None: request = args[0] elasticapm.set_transaction_name( build_name_with_http_method_prefix( get_name_from_middleware(wrapped, instance), request)) finally: return response
async def _request_started(self, request: Request): """Captures the begin of the request processing to APM. Args: request (Request) """ trace_parent = TraceParent.from_headers(dict(request.headers)) self.client.begin_transaction("request", trace_parent=trace_parent) await set_context(lambda: get_data_from_request(request, self.client.config, constants.TRANSACTION), "request") elasticapm.set_transaction_name("{} {}".format(request.method, request.url.path), override=False)
def orders(request): if request.method == 'POST': # set transaction name to post_order elasticapm.set_transaction_name('POST opbeans.views.post_order') return post_order(request) order_list = list( m.Order.objects.values('id', 'customer_id', 'customer__full_name', 'created_at')[:1000]) for order_dict in order_list: order_dict['customer_name'] = order_dict.pop('customer__full_name') return JsonResponse(order_list, safe=False)
def _setup_transaction_name(self, request: Request) -> None: """ Method used to setup the transaction name using the provided callback or the default mode :param request: Incoming HTTP Request entity :return: None """ if self._transaction_name_callback: name = self._transaction_name_callback(request) else: name = self._default_transaction_name_generator(request=request) if name: set_transaction_name(name, override=False)
def on_finish(self): apm_elastic = self.settings.get("apm_elastic") name_trasaction = '{} {}'.format(self.request.method, self.get_url()) status = self.get_status() result = 'HTTP {}xx'.format(status // 100) data_request = get_data_from_request(self.request) data_response = get_data_from_response(self) elasticapm.set_context(lambda: data_request, "request") elasticapm.set_context(lambda: data_response, "response") elasticapm.set_transaction_name(name_trasaction, override=False) elasticapm.set_transaction_result(result, override=False) apm_elastic.client.end_transaction()
def test_set_transaction_name(elasticapm_client): elasticapm_client.begin_transaction('test') elasticapm_client.end_transaction('test_name', 200) elasticapm_client.begin_transaction('test') elasticapm.set_transaction_name('another_name') elasticapm_client.end_transaction('test_name', 200) transactions = elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] == 'test_name' assert transactions[1]['name'] == 'another_name'
def test_set_transaction_name(elasticapm_client): elasticapm_client.begin_transaction("test") elasticapm_client.end_transaction("test_name", 200) elasticapm_client.begin_transaction("test") elasticapm.set_transaction_name("another_name") elasticapm_client.end_transaction("test_name", 200) transactions = elasticapm_client.transaction_store.get_all() assert transactions[0]["name"] == "test_name" assert transactions[1]["name"] == "another_name"
def process_response_wrapper(wrapped, instance, args, kwargs): response = wrapped(*args, **kwargs) try: request, original_response = args # if there's no view_func on the request, and this middleware created # a new response object, it's logged as the responsible transaction # name if not hasattr(request, "_elasticapm_view_func") and response is not original_response: elasticapm.set_transaction_name( build_name_with_http_method_prefix(get_name_from_middleware(wrapped, instance), request) ) finally: return response
def test_set_transaction_name(elasticapm_client): elasticapm_client.begin_transaction("test") elasticapm_client.end_transaction("test_name", 200) elasticapm_client.begin_transaction("test") elasticapm.set_transaction_name("another_name") elasticapm_client.end_transaction("test_name", 200) transactions = elasticapm_client.events[TRANSACTION] assert transactions[0]["name"] == "test_name" assert transactions[1]["name"] == "another_name"
def test_set_transaction_name(elasticapm_client, name1, name2, expected1, expected2): elasticapm_client.begin_transaction("test") elasticapm_client.end_transaction(name1, 200) elasticapm_client.begin_transaction("test") elasticapm.set_transaction_name(name2) elasticapm_client.end_transaction(name1, 200) transactions = elasticapm_client.events[TRANSACTION] assert transactions[0]["name"] == expected1 assert transactions[1]["name"] == expected2
def orders(request): if request.method == 'POST': # set transaction name to post_order elasticapm.set_transaction_name('POST opbeans.views.post_order') return post_order(request) order_list = list(m.Order.objects.all()[:1000]) with elasticapm.capture_span("iterate_orders"): orders = [ {"id": order.id, "customer_id": order.customer_id, "customer_name": order.customer.full_name, "created_at": order.created_at } for order in order_list ] return JsonResponse(orders, safe=False)
def request_started(self, app): if not self.app.debug or self.client.config.debug: trace_parent = TraceParent.from_headers(request.headers) self.client.begin_transaction("request", trace_parent=trace_parent) elasticapm.set_context( lambda: get_data_from_request( request, capture_body=self.client.config.capture_body in ("transactions", "all"), capture_headers=self.client.config.capture_headers, ), "request", ) rule = request.url_rule.rule if request.url_rule is not None else "" rule = build_name_with_http_method_prefix(rule, request) elasticapm.set_transaction_name(rule, override=False)
def process_response(self, request, response): if django_settings.DEBUG and not self.client.config.debug: return response if request.META.get('HTTP_USER_AGENT') == 'mcod-heartbeat': return response try: if hasattr(response, "status_code"): try: route = request.resolver_match.route except AttributeError: route = request.path transaction_name = route_to_name(route, prefix='admin', method=request.method) if transaction_name: elasticapm.set_transaction_name(transaction_name, override=False) elasticapm.set_context( lambda: self.client.get_data_from_request( request, capture_body=self.client.config.capture_body in ("all", "transactions")), "request", ) elasticapm.set_context( lambda: self.client.get_data_from_response(response), "response") elasticapm.set_context( lambda: self.client.get_user_info(request), "user") elasticapm.set_transaction_result("HTTP {}xx".format( response.status_code // 100), override=False) except Exception: self.client.error_logger.error( "Exception during timing of request", exc_info=True) return response
def override_transaction_name_view(request): elasticapm.set_transaction_name("foo") elasticapm.set_transaction_result("okydoky") return HttpResponse()
def call(self, module, method, wrapped, instance, args, kwargs): topic = None destination_info = { "service": { "name": "kafka", "resource": "kafka/", "type": "messaging" }, } self.destination_info = destination_info if method == "KafkaProducer.send": address = None port = None time_start = time.time() while not instance._metadata.controller: if time.time() - time_start > 1: break continue if instance: if instance._metadata.controller: address = instance._metadata.controller[1] port = instance._metadata.controller[2] self.destination_info["port"] = port self.destination_info["address"] = address topic = args[0].encode("utf-8") transaction = execution_context.get_transaction() if transaction: return self._trace_send(wrapped, topic, **kwargs) if method == "KafkaConsumer.next": transaction = execution_context.get_transaction() if transaction and transaction.transaction_type != "messaging": action = "consume" with capture_span( name="consumer", span_type="messaging", span_subtype=self.provider_name, span_action=action, extra={ "message": { "queue": { "name": "" } }, "destination": self.destination_info, }, ) as span: result = wrapped(*args, **kwargs) topic = result[0] new_trace_id = get_trace_id(result) service = self.destination_info["service"] service["resource"] = service["resource"] + topic span.context["message"]["queue"]["name"] = topic span.context["destination"]["service"] = service span.name = "KafkaConsumer#receive from " + topic transaction.trace_parent = TraceParent.from_string( new_trace_id) return result else: client = get_client() if transaction and transaction.transaction_type == "messaging": client.end_transaction() result = wrapped(*args, **kwargs) topic = result[0] new_trace_id = None new_trace_id = get_trace_id(result) client.begin_transaction("messaging", trace_parent=None) transaction = execution_context.get_transaction() if result.timestamp_type == 0: current_time_millis = int(round(time.time() * 1000)) age = current_time_millis - result.timestamp transaction.context = { "message": { "age": { "ms": age }, "queue": { "name": topic } } } if new_trace_id: transaction.trace_parent = TraceParent.from_string( new_trace_id) t_name = "Kafka record from " + topic elasticapm.set_transaction_name(t_name, override=True) res = constants.OUTCOME.SUCCESS elasticapm.set_transaction_result(res, override=False) return result
async def handle_request(request, handler): elasticapm_client = get_client() if client is None else client should_trace = elasticapm_client and not elasticapm_client.should_ignore_url( request.path) if should_trace: trace_parent = AioHttpTraceParent.from_headers(request.headers) elasticapm_client.begin_transaction("request", trace_parent=trace_parent) resource = request.match_info.route.resource name = request.method if resource: # canonical has been added in 3.3, and returns one of path, formatter, prefix for attr in ("canonical", "_path", "_formatter", "_prefix"): if hasattr(resource, attr): name += " " + getattr(resource, attr) break else: name += " unknown route" else: name += " unknown route" elasticapm.set_transaction_name(name, override=False) elasticapm.set_context( lambda: get_data_from_request(request, elasticapm_client. config, constants.TRANSACTION), "request") try: response = await handler(request) if should_trace: elasticapm.set_transaction_result("HTTP {}xx".format( response.status // 100), override=False) elasticapm.set_transaction_outcome( http_status_code=response.status, override=False) elasticapm.set_context( lambda: get_data_from_response(response, elasticapm_client.config, constants.TRANSACTION), "response", ) return response except HTTPException as exc: # HTTPExceptions are response-like, e.g. have headers and status code. They can represent an HTTP # response below a 500 status code and therefore not something to capture as exception. Like # HTTPOk can be raised but will most likely be wrongly tagged as an APM error. Let's try and # capture this according to the status. if exc.status_code < 500 and not should_trace: raise if elasticapm_client: elasticapm.set_transaction_result("HTTP {}xx".format( exc.status_code // 100), override=False) elasticapm.set_transaction_outcome( http_status_code=exc.status_code, override=False) elasticapm.set_context( lambda: get_data_from_response( exc, # noqa: F821 elasticapm_client.config, constants.ERROR if exc.status_code >= 500 else constants.TRANSACTION, # noqa: F821 ), "response", ) if exc.status_code >= 500: elasticapm_client.capture_exception( context={ "request": get_data_from_request(request, elasticapm_client. config, constants.ERROR) }) raise except Exception: if elasticapm_client: elasticapm.set_transaction_result("HTTP 5xx", override=False) elasticapm.set_transaction_outcome(http_status_code=500, override=False) elasticapm.set_context({"status_code": 500}, "response") elasticapm_client.capture_exception( context={ "request": get_data_from_request( request, elasticapm_client.config, constants.ERROR) }) raise finally: elasticapm_client.end_transaction()
def transaction_name(): elasticapm.set_transaction_name("foo") elasticapm.set_transaction_result("okydoky") return Response("")