def call_if_sampling(self, module, method, wrapped, instance, args, kwargs): """ This is the function which will wrap the instrumented method/function. By default, will call the instrumented method/function, via `call()`, only if a transaction is active and sampled. This behavior can be overridden by setting `creates_transactions = True` at the class level. If `creates_transactions == False` and there's an active transaction with `transaction.is_sampled == False`, then the `mutate_unsampled_call_args()` method is called, and the resulting args and kwargs are passed into the wrapped function directly, not via `call()`. This can e.g. be used to add traceparent headers to the underlying http call for HTTP instrumentations, even if we're not sampling the transaction. """ if self.creates_transactions: return self.call(module, method, wrapped, instance, args, kwargs) transaction = execution_context.get_transaction() if not transaction: return wrapped(*args, **kwargs) elif not transaction.is_sampled: args, kwargs = self.mutate_unsampled_call_args( module, method, wrapped, instance, args, kwargs, transaction) return wrapped(*args, **kwargs) else: return self.call(module, method, wrapped, instance, args, kwargs)
def _add_attributes_to_log_record(record): """ Add custom attributes for APM tracing fields to a LogRecord object :param record: LogRecord object :return: Updated LogRecord object with new APM tracing fields """ transaction = execution_context.get_transaction() transaction_id = transaction.id if transaction else None record.elasticapm_transaction_id = transaction_id trace_id = transaction.trace_parent.trace_id if transaction and transaction.trace_parent else None record.elasticapm_trace_id = trace_id span = execution_context.get_span() span_id = span.id if span else None record.elasticapm_span_id = span_id client = get_client() service_name = client.config.service_name if client else None record.elasticapm_service_name = service_name event_dataset = f"{client.config.service_name}.log" if client else None record.elasticapm_event_dataset = event_dataset record.elasticapm_labels = { "transaction.id": transaction_id, "trace.id": trace_id, "span.id": span_id, "service.name": service_name, "event.dataset": event_dataset, } return record
def modify_span_sqs(span, args, kwargs): if span.id: trace_parent = span.transaction.trace_parent.copy_from(span_id=span.id) else: # this is a dropped span, use transaction id instead transaction = execution_context.get_transaction() trace_parent = transaction.trace_parent.copy_from( span_id=transaction.id) attributes = { constants.TRACEPARENT_HEADER_NAME: { "DataType": "String", "StringValue": trace_parent.to_string() } } if trace_parent.tracestate: attributes[constants.TRACESTATE_HEADER_NAME] = { "DataType": "String", "StringValue": trace_parent.tracestate } if len(args) > 1: attributes_count = len(attributes) if "MessageAttributes" in args[1]: messages = [args[1]] elif "Entries" in args[1]: messages = args[1]["Entries"] else: messages = [] for message in messages: if len(message["MessageAttributes"] ) + attributes_count <= SQS_MAX_ATTRIBUTES: message["MessageAttributes"].update(attributes) else: logger.info( "Not adding disttracing headers to message due to attribute limit reached" )
def structlog_processor(logger, method_name, event_dict): """ Add three new entries to the event_dict for any processed events: * transaction.id * trace.id * span.id Only adds non-None IDs. :param logger: Unused (logger instance in structlog) :param method_name: Unused (wrapped method_name) :param event_dict: Event dictionary for the event we're processing :return: `event_dict`, with three new entries. """ transaction = execution_context.get_transaction() if transaction: event_dict["transaction.id"] = transaction.id event_dict["service.name"] = transaction.tracer.config.service_name if transaction and transaction.trace_parent: event_dict["trace.id"] = transaction.trace_parent.trace_id span = execution_context.get_span() if span and span.id: event_dict["span.id"] = span.id return event_dict
async def call(self, module, method, wrapped, instance, args, kwargs): method = kwargs["method"] if "method" in kwargs else args[0] url = kwargs["url"] if "url" in kwargs else args[1] url = str(url) signature = " ".join([method.upper(), get_host_from_url(url)]) url = sanitize_url(url) transaction = execution_context.get_transaction() async with async_capture_span( signature, span_type="external", span_subtype="http", extra={"http": { "url": url }}, leaf=True, ) as span: leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) headers = kwargs.get("headers") or {} self._set_disttracing_headers(headers, trace_parent, transaction) kwargs["headers"] = headers response = await wrapped(*args, **kwargs) if response: if span.context: span.context["http"]["status_code"] = response.status span.set_success( ) if response.status < 400 else span.set_failure() return response
def _add_attributes_to_log_record(record): """ Add custom attributes for APM tracing fields to a LogRecord object :param record: LogRecord object :return: Updated LogRecord object with new APM tracing fields """ transaction = execution_context.get_transaction() transaction_id = transaction.id if transaction else None record.elasticapm_transaction_id = transaction_id trace_id = transaction.trace_parent.trace_id if transaction and transaction.trace_parent else None record.elasticapm_trace_id = trace_id span = execution_context.get_span() span_id = span.id if span else None record.elasticapm_span_id = span_id record.elasticapm_labels = { "transaction.id": transaction_id, "trace.id": trace_id, "span.id": span_id } return record
def call(self, module, method, wrapped, instance, args, kwargs): request_object = args[1] if len(args) > 1 else kwargs["req"] method = request_object.get_method() host = request_host(request_object) url = sanitize_url(request_object.get_full_url()) signature = method.upper() + " " + host transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={"http": {"url": url}}, leaf=True ) as span: # if urllib has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True) ) self._set_disttracing_headers(request_object, trace_parent, transaction) return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): args, kwargs, params = self._ensure_headers_in_kwargs(args, kwargs) signature = params.get("method", "GET").upper() signature += " " + get_host_from_url(params["url"]) url = sanitize_url(params["url"]) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={"http": {"url": url}}, leaf=True, ) as span: # if httplib2 has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent # It's possible that there are only dropped spans, e.g. if we started dropping spans. # In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True) ) self._set_disttracing_headers(params["headers"], trace_parent, transaction) if leaf_span: leaf_span.dist_tracing_propagated = True response, content = wrapped(*args, **kwargs) if span.context: span.context["http"]["status_code"] = response.status span.set_success() if response.status < 400 else span.set_failure() return response, content
async def set_context(data, key="custom"): """ Asynchronous copy of elasticapm.traces.set_context(). Attach contextual data to the current transaction and errors that happen during the current transaction. If the transaction is not sampled, this function becomes a no-op. :param data: a dictionary, or a callable that returns a dictionary :param key: the namespace for this data """ transaction = execution_context.get_transaction() if not (transaction and transaction.is_sampled): return if callable(data): data = await data() # remove invalid characters from key names for k in list(data.keys()): if LABEL_RE.search(k): data[LABEL_RE.sub("_", k)] = data.pop(k) if key in transaction.context: transaction.context[key].update(data) else: transaction.context[key] = data
def wrapper(*args, **kwds): transaction = execution_context.get_transaction() if transaction: transaction.begin_span(func.__name__, span_type) result = func(*args, **kwds) if transaction: transaction.end_span() return result
def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"] else: method = args[0] headers = None if "headers" in kwargs: headers = kwargs["headers"] if headers is None: headers = {} kwargs["headers"] = headers host = instance.host if instance.port != default_ports.get(instance.scheme): host += ":" + str(instance.port) if "url" in kwargs: url = kwargs["url"] else: url = args[1] signature = method.upper() + " " + host url = "%s://%s%s" % (instance.scheme, host, url) destination = url_to_destination(url) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if urllib3 has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans. # In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) self._set_disttracing_headers(headers, trace_parent, transaction) return wrapped(*args, **kwargs)
def call_if_sampling(self, module, method, wrapped, instance, args, kwargs): transaction = execution_context.get_transaction() if not transaction: return wrapped(*args, **kwargs) elif not transaction.is_sampled: args, kwargs = self.mutate_unsampled_call_args( module, method, wrapped, instance, args, kwargs, transaction) return wrapped(*args, **kwargs) else: return self.call(module, method, wrapped, instance, args, kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"] else: method = args[0] host = instance.host if instance.port != default_ports.get(instance.scheme): host += ":" + str(instance.port) if "url" in kwargs: url = kwargs["url"] else: url = args[1] signature = method.upper() + " " + host url = "%s://%s%s" % (instance.scheme, host, url) destination = url_to_destination(url) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if urllib3 has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) args, kwargs = update_headers(args, kwargs, instance, transaction, trace_parent) response = wrapped(*args, **kwargs) if response: if span.context: span.context["http"]["status_code"] = response.status span.set_success( ) if response.status < 400 else span.set_failure() return response
def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"] else: method = args[0] headers = None if "headers" in kwargs: headers = kwargs["headers"] if headers is None: headers = {} kwargs["headers"] = headers host = instance.host if instance.port != default_ports.get(instance.scheme): host += ":" + str(instance.port) if "url" in kwargs: url = kwargs["url"] else: url = args[1] signature = method.upper() + " " + host # TODO: reconstruct URL more faithfully, e.g. include port url = instance.scheme + "://" + host + url transaction = execution_context.get_transaction() with capture_span(signature, span_type="external", span_subtype="http", extra={"http": { "url": url }}, leaf=True) as span: # if urllib3 has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans. # In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) headers[constants. TRACEPARENT_HEADER_NAME] = trace_parent.to_string() return wrapped(*args, **kwargs)
async def __aenter__(self): transaction = execution_context.get_transaction() if transaction and transaction.is_sampled: return transaction.begin_span( self.name, self.type, context=self.extra, leaf=self.leaf, labels=self.labels, span_subtype=self.subtype, span_action=self.action, sync=False, start=self.start, )
def rum_tracing(request): transaction = execution_context.get_transaction() if transaction and transaction.trace_parent: return { "apm": { "trace_id": transaction.trace_parent.trace_id, # only put the callable into the context to ensure that we only change the span_id if the value # is rendered "span_id": transaction.ensure_parent_id, "is_sampled": transaction.is_sampled, "is_sampled_js": "true" if transaction.is_sampled else "false", } } return {}
async def __aexit__(self, exc_type, exc_val, exc_tb): transaction = execution_context.get_transaction() if transaction and transaction.is_sampled: try: span = transaction.end_span(self.skip_frames) if exc_val and not isinstance(span, DroppedSpan): try: exc_val._elastic_apm_span_id = span.id except AttributeError: # could happen if the exception has __slots__ pass except LookupError: error_logger.debug("ended non-existing span %s of type %s", self.name, self.type)
def _trace_send( self, method, topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None, action="send", ): span_name = "KafkaProducer#send to " + topic service = self.destination_info["service"] service["resource"] = service["resource"] + topic self.destination_info["service"] = service with capture_span( name=span_name, span_type="messaging", span_subtype=self.provider_name, span_action=action, extra={ "message": { "queue": { "name": topic } }, "destination": self.destination_info, }, ) as span: transaction = execution_context.get_transaction() if transaction: tp = transaction.trace_parent tp_string = tp.to_string() # ID REPLACE SECTION START new_tp_string = tp_string.replace(transaction.id, span.id) if headers: headers.append(("trace", bytes(new_tp_string))) else: headers = [("trace", bytes(new_tp_string))] # ID REPLACE SECTION STOP result = method( topic, value=value, key=key, headers=headers, partition=partition, timestamp_ms=timestamp_ms, ) return result
def rum_tracing(): """ Adds APM related IDs to the context used for correlating the backend transaction with the RUM transaction """ transaction = execution_context.get_transaction() if transaction and transaction.trace_parent: return { "apm": { "trace_id": transaction.trace_parent.trace_id, "span_id": lambda: transaction.ensure_parent_id(), "is_sampled": transaction.is_sampled, "is_sampled_js": "true" if transaction.is_sampled else "false", } } return {}
def set_celery_headers(headers=None, **kwargs): """ Add elasticapm specific information to celery headers """ headers = {} if headers is None else headers transaction = execution_context.get_transaction() if transaction is not None: trace_parent = transaction.trace_parent trace_parent_string = trace_parent.to_string() headers.update( {"elasticapm": { "trace_parent_string": trace_parent_string }})
def rum_tracing(request): transaction = execution_context.get_transaction() if transaction and transaction.trace_parent: transaction_name = request.resolver_match.route if hasattr(request.resolver_match, "route") else None return { "apm": { "trace_id": transaction.trace_parent.trace_id, # only put the callable into the context to ensure that we only change the span_id if the value # is rendered "span_id": transaction.ensure_parent_id, "transaction_name": transaction_name, "is_sampled": transaction.is_sampled, "is_sampled_js": "true" if transaction.is_sampled else "false", } } return {}
def call(self, module, method, wrapped, instance, args, kwargs): url, method, headers = utils.get_request_data(args, kwargs) scheme, host, port, target = url if port != default_ports.get(scheme): host += ":" + str(port) signature = "%s %s" % (method.upper(), host) url = "%s://%s%s" % (scheme, host, target) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={"http": { "url": url }}, leaf=True, ) as span: # if httpcore has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans. # In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) utils.set_disttracing_headers(headers, trace_parent, transaction) if leaf_span: leaf_span.dist_tracing_propagated = True response = wrapped(*args, **kwargs) status_code = utils.get_status(response) if status_code: if span.context: span.context["http"]["status_code"] = status_code span.set_success() if status_code < 400 else span.set_failure() return response
def call(self, module, method, wrapped, instance, args, kwargs): request_object = args[1] if len(args) > 1 else kwargs["req"] method = request_object.get_method() host = request_host(request_object) url = sanitize_url(request_object.get_full_url()) destination = url_to_destination(url) signature = method.upper() + " " + host transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if urllib has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) self._set_disttracing_headers(request_object, trace_parent, transaction) response = wrapped(*args, **kwargs) if response: status = getattr(response, "status", None) or response.getcode() # Python 2 compat if span.context: span.context["http"]["status_code"] = status span.set_success() if status < 400 else span.set_failure() return response
def _build_msg_for_logging( self, event_type, date=None, context=None, custom=None, stack=None, handled=True, **kwargs ): """ Captures, processes and serializes an event into a dict object """ transaction = execution_context.get_transaction() span = execution_context.get_span() if transaction: transaction_context = deepcopy(transaction.context) else: transaction_context = {} event_data = {} if custom is None: custom = {} if date is not None: warnings.warn( "The date argument is no longer evaluated and will be removed in a future release", DeprecationWarning ) date = time.time() if stack is None: stack = self.config.auto_log_stacks if context: transaction_context.update(context) context = transaction_context else: context = transaction_context event_data["context"] = context if transaction and transaction.labels: context["tags"] = deepcopy(transaction.labels) # if '.' not in event_type: # Assume it's a builtin event_type = "elasticapm.events.%s" % event_type handler = self.get_handler(event_type) result = handler.capture(self, **kwargs) if self._filter_exception_type(result): return # data (explicit) culprit takes over auto event detection culprit = result.pop("culprit", None) if custom.get("culprit"): culprit = custom.pop("culprit") for k, v in compat.iteritems(result): if k not in event_data: event_data[k] = v log = event_data.get("log", {}) if stack and "stacktrace" not in log: if stack is True: frames = stacks.iter_stack_frames(skip=3, config=self.config) else: frames = stack frames = stacks.get_stack_info( frames, with_locals=self.config.collect_local_variables in ("errors", "all"), library_frame_context_lines=self.config.source_lines_error_library_frames, in_app_frame_context_lines=self.config.source_lines_error_app_frames, include_paths_re=self.include_paths_re, exclude_paths_re=self.exclude_paths_re, locals_processor_func=lambda local_var: varmap( lambda k, v: shorten( v, list_length=self.config.local_var_list_max_length, string_length=self.config.local_var_max_length, dict_length=self.config.local_var_dict_max_length, ), local_var, ), ) log["stacktrace"] = frames if "stacktrace" in log and not culprit: culprit = stacks.get_culprit(log["stacktrace"], self.config.include_paths, self.config.exclude_paths) if "level" in log and isinstance(log["level"], compat.integer_types): log["level"] = logging.getLevelName(log["level"]).lower() if log: event_data["log"] = log if culprit: event_data["culprit"] = culprit if "custom" in context: context["custom"].update(custom) else: context["custom"] = custom # Make sure all data is coerced event_data = transform(event_data) if "exception" in event_data: event_data["exception"]["handled"] = bool(handled) event_data["timestamp"] = int(date * 1000000) if transaction: if transaction.trace_parent: event_data["trace_id"] = transaction.trace_parent.trace_id # parent id might already be set in the handler event_data.setdefault("parent_id", span.id if span else transaction.id) event_data["transaction_id"] = transaction.id event_data["transaction"] = {"sampled": transaction.is_sampled, "type": transaction.transaction_type} return event_data
def process_request(self, request): from elasticapm.traces import execution_context transaction = execution_context.get_transaction() if transaction: transaction.is_sampled = False
def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"].decode("utf-8") else: method = args[0].decode("utf-8") # URL is a tuple of (scheme, host, port, path), we want path if "url" in kwargs: url = kwargs["url"][3].decode("utf-8") else: url = args[1][3].decode("utf-8") headers = None if "headers" in kwargs: headers = kwargs["headers"] if headers is None: headers = [] kwargs["headers"] = headers scheme, host, port = instance.origin scheme = scheme.decode("utf-8") host = host.decode("utf-8") if port != default_ports.get(scheme): host += ":" + str(port) signature = "%s %s" % (method.upper(), host) url = "%s://%s%s" % (scheme, host, url) destination = url_to_destination(url) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if httpcore has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans. # In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) self._set_disttracing_headers(headers, trace_parent, transaction) response = wrapped(*args, **kwargs) if len(response) > 4: # httpcore < 0.11.0 # response = (http_version, status_code, reason_phrase, headers, stream) status_code = response[1] else: # httpcore >= 0.11.0 # response = (status_code, headers, stream, ext) status_code = response[0] if status_code: if span.context: span.context["http"]["status_code"] = status_code span.set_success() if status_code < 400 else span.set_failure() return response
def test_get_transaction(): requests_store = Tracer(lambda: [], lambda: [], lambda *args: None) t = requests_store.begin_transaction("test") assert t == execution_context.get_transaction()
def test_get_transaction_clear(): requests_store = Tracer(lambda: [], lambda: [], lambda *args: None, Config(), None) t = requests_store.begin_transaction("test") assert t == execution_context.get_transaction(clear=True) assert execution_context.get_transaction() is None
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