示例#1
0
    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)
示例#2
0
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
示例#3
0
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"
                )
示例#4
0
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
示例#6
0
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
示例#7
0
    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)
示例#8
0
    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
示例#9
0
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
示例#10
0
 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
示例#11
0
    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)
示例#12
0
 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)
示例#13
0
    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
示例#14
0
    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)
示例#15
0
 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 {}
示例#17
0
 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)
示例#18
0
 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
示例#19
0
 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 {}
示例#20
0
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 {}
示例#22
0
    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
示例#23
0
    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
示例#24
0
    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
示例#25
0
    def process_request(self, request):
        from elasticapm.traces import execution_context

        transaction = execution_context.get_transaction()
        if transaction:
            transaction.is_sampled = False
示例#26
0
    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()
示例#28
0
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
示例#29
0
    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