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)
            return wrapped(*args, **kwargs)
示例#2
0
    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)
        destination = url_to_destination(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}, "destination": destination},
            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
            return await wrapped(*args, **kwargs)
示例#3
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)
async def set_context(data, key="custom"):
    """
    Asynchronous copy of zuqa.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
示例#5
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
    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
    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)
 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._zuqa_span_id = span.id
                 except AttributeError:
                     # could happen if the exception has __slots__
                     pass
         except LookupError:
             error_logger.info("ended non-existing span %s of type %s", self.name, self.type)
 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,
         )
示例#9
0
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 {}
 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 _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.zuqa_transaction_id = transaction_id

    trace_id = transaction.trace_parent.trace_id if transaction and transaction.trace_parent else None
    record.zuqa_trace_id = trace_id

    span = execution_context.get_span()
    span_id = span.id if span else None
    record.zuqa_span_id = span_id

    record.zuqa_labels = {"transaction.id": transaction_id, "trace.id": trace_id, "span.id": span_id}

    return record
示例#12
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 = "zuqa.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
示例#13
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