示例#1
0
    def __exit__(self, exc, value, tb):
        if not self.transaction:
            return

        if self.enter_time is not None:
            exit_time = time.time()
            start_time = self.enter_time
            self.enter_time = None

            trace_cache().record_event_loop_wait(start_time, exit_time)

        global CancelledError

        if CancelledError is None:
            try:
                from concurrent.futures import CancelledError
            except:
                CancelledError = GeneratorExit

        # case: coroutine completed or cancelled
        if (exc is StopIteration or exc is GeneratorExit
                or exc is CancelledError):
            self.transaction.__exit__(None, None, None)

        # case: coroutine completed because of error
        elif exc:
            self.transaction.__exit__(exc, value, tb)
def wrap_finish(wrapped, instance, args, kwargs):
    try:
        return wrapped(*args, **kwargs)
    finally:
        transaction = getattr(instance, '_nr_transaction', None)
        if transaction:
            start_time = getattr(transaction, '_async_start_time', None)
            if start_time:
                trace_cache().record_event_loop_wait(start_time, time.time())
                transaction._async_start_time = None
            record_exception(*sys.exc_info(), ignore_errors=should_ignore)
            transaction.__exit__(None, None, None)
            instance._nr_transaction = None
 def context_wrapper(wrapped, instance, args, kwargs):
     cache = trace_cache()
     thread_id = cache.thread_start(trace)
     try:
         return wrapped(*args, **kwargs)
     finally:
         cache.thread_stop(thread_id)
def collect_stack_traces(include_nr_threads=False, include_xrays=False):
    """Generator that yields the (thread category, stack trace) of all the
    python threads.

    """
    for (txn, thread_id, thread_category, frame) in \
            trace_cache().active_threads():

        # Skip NR Threads unless explicitly requested.

        if (thread_category == 'AGENT') and (not include_nr_threads):
            continue

        stack_trace = format_stack_trace(frame, thread_category)

        # Skip over empty stack traces. This is merely for optimization.
        #
        # It saves us from adding an empty deque to the txn obj, which will be
        # discarded later on during call tree merge.

        if not stack_trace:
            continue

        if include_xrays and txn:
            txn.add_profile_sample(stack_trace)

        yield thread_category, stack_trace
示例#5
0
def test_context_propagation(event_loop, schedule, loop_policy):
    import asyncio

    asyncio.set_event_loop_policy(loop_policy)
    exceptions = []

    def handle_exception(loop, context):
        exceptions.append(context)

    event_loop.set_exception_handler(handle_exception)

    schedule = getattr(asyncio, schedule, None) or getattr(
        event_loop, schedule)

    # Keep the trace around so that it's not removed from the trace cache
    # through reference counting (for testing)
    _ = event_loop.run_until_complete(_test(asyncio, schedule))

    # The agent should have removed all traces from the cache since
    # run_until_complete has terminated (all callbacks scheduled inside the
    # task have run)
    assert not trace_cache()._cache

    # Assert that no exceptions have occurred
    assert not exceptions, exceptions
def test_transaction_exit_trace_cache(fg):
    """
    Verifying that the use of ensure_future will not cause errors
    when traces remain in the trace cache after transaction exit
    """
    import asyncio
    exceptions = []

    def handle_exception(loop, context):
        exceptions.append(context)

    async def bg(event):
        with BackgroundTask(application(), 'bg'):
            event.set()

    async def handler():
        task = await fg(asyncio, bg)
        await task

    def _test():
        loop = asyncio.get_event_loop()
        loop.set_exception_handler(handle_exception)
        return loop.run_until_complete(handler())

    _test()

    # The agent should have removed all traces from the cache since
    # run_until_complete has terminated
    assert not trace_cache()._cache

    # Assert that no exceptions have occurred
    assert not exceptions, exceptions
示例#7
0
    def __init__(self, trace=None, request=None, trace_cache_id=None, strict=True):
        self.trace = None
        self.trace_cache = trace_cache()
        self.thread_id = None
        self.restore = None
        self.should_restore = False

        def log_propagation_failure(s):
            if strict:
                _logger.debug(
                    "Request context propagation failed. %s This may be an issue if there's an active transaction. Consult with New Relic support if further issues arise.",
                    s,
                )

        # Extract trace if possible, else leave as None for safety
        if trace is None and request is None and trace_cache_id is None:
            if strict:
                log_propagation_failure("No trace or request provided.")
        elif trace is not None:
            self.trace = trace
        elif trace_cache_id is not None:
            self.trace = self.trace_cache._cache.get(trace_cache_id, None)
            if self.trace is None:
                log_propagation_failure("No trace with id %d." % trace_cache_id)
        elif hasattr(request, "_nr_trace") and request._nr_trace is not None:
            # Unpack traces from objects patched with them
            self.trace = request._nr_trace
        else:
            log_propagation_failure("No context attached to request.")
    def __enter__(self):
        trace = getattr(self.request, "_nr_trace", None)
        self.force_propagate = trace and current_trace() is None

        # Propagate trace context onto the current task
        if self.force_propagate:
            self.thread_id = trace_cache().thread_start(trace)
示例#9
0
def propagate_task_context(task):
    trace = current_trace()
    if trace:
        cache = trace_cache()
        cache._cache[id(task)] = trace
        task.add_done_callback(remove_from_cache)

    return task
示例#10
0
def error_matches_rules(
    rules_prefix,
    exc_info,
    status_code=None,
):
    # Delay imports to prevent lockups
    from newrelic.api.application import application_instance
    from newrelic.core.trace_cache import trace_cache

    trace = trace_cache().current_trace()
    settings = trace and trace.settings

    if not settings:
        # Retrieve application settings
        application = application_instance()
        settings = application and application.settings

    # Default to global settings
    settings = settings or global_settings()  

    if not settings:
        return False

    # Retrieve settings based on prefix
    classes_rules = getattr(settings.error_collector, "%s_classes" % rules_prefix, set())
    status_codes_rules = getattr(settings.error_collector, "%s_status_codes" % rules_prefix, set())

    module, name, fullnames, message = parse_exc_info(exc_info)
    fullname = fullnames[0]

    # Check class names
    for fullname in fullnames:
        if fullname in classes_rules:
            return True

    # Check status_code
    # For callables, call on exc_info to retrieve status_code.
    # It's possible to return None, in which case no code is evaluated.
    if callable(status_code):
        status_code = status_code(*exc_info)
    
    # Match status_code if it exists
    if status_code is not None:
        try:
            # Coerce into integer
            status_code = int(status_code)
        except:
            _logger.error("Failed to coerce status code into integer. "
                          "status_code: %s" % str(status_code))
        else:
            if status_code in status_codes_rules:
                return True

    return False
def test_record_event_loop_wait_outside_task():
    # Insert a random trace into the trace cache
    trace = FunctionTrace(name="testing")
    trace_cache()._cache[0] = trace

    @background_task(name="test_record_event_loop_wait_outside_task")
    def _test():
        yield

    for _ in _test():
        pass
示例#12
0
    def __enter__(self):
        # TODO  上下文管理协议,这个设计用的好。进入上下文前,把一些必要的事情先干了,比如设置起始时间;
        # TODO  在离开上下文的时候,又干一些事情
        self.parent = parent = self.parent or current_trace()
        if not parent:
            return self

        # The parent may be exited if the stack is not consistent. This
        # can occur when using ensure_future to schedule coroutines
        # instead of using async/await keywords. In those cases, we
        # must not trace.
        #
        # Don't do any tracing if parent is designated
        # as a terminal node.
        # 如果父节点已经退出或者是末位节点,就不进行任何追踪
        if parent.exited or parent.terminal_node():
            self.parent = None
            return parent

        transaction = parent.root.transaction

        # Don't do further tracing of transaction if
        # it has been explicitly stopped.
        # 如果事务已经停止,不进行任何追踪
        if transaction.stopped or not transaction.enabled:
            self.parent = None
            return self

        parent.increment_child_count()

        self.root = parent.root
        self.should_record_segment_params = (
            transaction.should_record_segment_params)

        # Record start time.

        self.start_time = time.time()

        cache = trace_cache()
        self.thread_id = cache.current_thread_id()  # 获取当前追踪链路的线程id

        # Push ourselves as the current node and store parent.
        try:
            cache.save_trace(self)  # 将线程的追踪保存到缓存里
        except:
            self.parent = None
            raise

        self.activated = True

        return self
示例#13
0
    def __enter__(self):
        self.parent = parent = self.parent or current_trace()
        if not parent:
            return self

        # The parent may be exited if the stack is not consistent. This
        # can occur when using ensure_future to schedule coroutines
        # instead of using async/await keywords. In those cases, we
        # must not trace.
        #
        # Don't do any tracing if parent is designated
        # as a terminal node.
        if parent.exited or parent.terminal_node():
            self.parent = None
            return parent

        transaction = parent.root.transaction

        # Don't do further tracing of transaction if
        # it has been explicitly stopped.
        if transaction.stopped or not transaction.enabled:
            self.parent = None
            return self

        parent.increment_child_count()

        self.root = parent.root
        self.should_record_segment_params = transaction.should_record_segment_params

        # Record start time.

        self.start_time = time.time()

        cache = trace_cache()
        self.thread_id = cache.current_thread_id()

        # Push ourselves as the current node and store parent.
        try:
            cache.save_trace(self)
        except:
            self.parent = None
            raise

        self.activated = True

        # Extract source code context
        if self._source is not None:
            self.add_code_level_metrics(self._source)

        return self
def test_context_propagation(schedule, set_loop):
    import asyncio

    _loop = None
    if set_loop:

        class TestEventLoop(asyncio.SelectorEventLoop):
            def create_task(self, coro, **kwargs):
                return asyncio.tasks.Task(coro, loop=self, **kwargs)

        _loop = asyncio.get_event_loop()
        asyncio.set_event_loop(TestEventLoop())

    loop = asyncio.get_event_loop()

    exceptions = []

    def handle_exception(loop, context):
        exceptions.append(context)

    loop.set_exception_handler(handle_exception)

    schedule = getattr(asyncio, schedule, None) or getattr(loop, schedule)

    # Keep the trace around so that it's not removed from the trace cache
    # through reference counting (for testing)
    _ = loop.run_until_complete(_test(asyncio, schedule))

    # The agent should have removed all traces from the cache since
    # run_until_complete has terminated (all callbacks scheduled inside the
    # task have run)
    assert not trace_cache()._cache

    # Assert that no exceptions have occurred
    assert not exceptions, exceptions
    if _loop:
        asyncio.set_event_loop(_loop)
示例#15
0
    def __enter__(self):
        # If no transaction attempt to create it if first time entering context
        # manager.
        if self.transaction_init:
            current_trace = trace_cache().prepare_for_root()
            current_transaction = current_trace and current_trace.transaction

            # If the current transaction's Sentinel is exited we can ignore it.
            if not current_transaction:
                self.transaction = self.transaction_init(None)
            else:
                self.transaction = self.transaction_init(current_transaction)

            # Set transaction_init to None so we only attempt to create a
            # transaction the first time entering the context.
            self.transaction_init = None
        if not self.transaction:
            return self

        if not self.transaction._state:
            self.transaction.__enter__()

        self.enter_time = time.time()
        return self
示例#16
0
 def __exit__(self, exc, value, tb):
     trace_cache().record_event_loop_wait(self.enter_time, time.time())
示例#17
0
def current_thread_id():
    return trace_cache().current_thread_id()
示例#18
0
def remove_from_cache(task):
    cache = trace_cache()
    cache.task_stop(task)
 async def recorder(ready, wait):
     ready.set()
     await wait.wait()
     trace_cache().record_event_loop_wait(0, 1)
示例#20
0
def propagate_task_context(task):
    trace_cache().task_start(task)
    task.add_done_callback(remove_from_cache)
    return task
示例#21
0
    def __exit__(self, exc, value, tb):
        if not self.parent:
            return

        # Check for violation of context manager protocol where
        # __exit__() is called before __enter__().

        if not self.activated:
            _logger.error(
                'Runtime instrumentation error. The __exit__() '
                'method of %r was called prior to __enter__() being '
                'called. Report this issue to New Relic support.\n%s', self,
                ''.join(traceback.format_stack()[:-1]))

            return

        transaction = self.root.transaction

        # If the transaction has gone out of scope (recorded), there's not much
        # we can do at this point.
        if not transaction:
            return

        # If recording of time for transaction has already been
        # stopped, then that time has to be used.

        if transaction.stopped:
            self.end_time = transaction.end_time
        else:
            self.end_time = time.time()

        # Ensure end time is greater. Should be unless the
        # system clock has been updated.

        if self.end_time < self.start_time:
            self.end_time = self.start_time

        # Calculate duration and exclusive time. Up till now the
        # exclusive time value had been used to accumulate
        # duration from child nodes as negative value, so just
        # add duration to that to get our own exclusive time.

        self.duration = self.end_time - self.start_time

        self.exclusive += self.duration

        if self.exclusive < 0:
            self.exclusive = 0

        self.exited = True

        self.exc_data = (exc, value, tb)

        # in all cases except async, the children will have exited
        # so this will create the node

        if self._ready_to_complete():
            self._complete_trace()
        else:
            # Since we're exited we can't possibly schedule more children but
            # we may have children still running if we're async
            trace_cache().pop_current(self)
示例#22
0
def current_trace():
    return trace_cache().current_trace()
示例#23
0
    def _complete_trace(self):
        # transaction already completed, this is an error
        if self.parent is None:
            _logger.error(
                'Runtime instrumentation error. The transaction '
                'already completed meaning a child called complete trace '
                'after the trace had been finalized. Trace: %r \n%s', self,
                ''.join(traceback.format_stack()[:-1]))

            return

        parent = self.parent

        # Check to see if we're async
        if parent.exited or parent.has_async_children:
            self.is_async = True

        # Pop ourselves as current node. If deferred, we have previously exited
        # and are being completed by a child trace.
        trace_cache().pop_current(self)

        # Wipe out parent reference so can't use object
        # again. Retain reference as local variable for use in
        # this call though.
        self.parent = None

        # Wipe out root reference as well
        transaction = self.root.transaction
        self.root = None

        # wipe out exc data
        exc_data = self.exc_data
        self.exc_data = (None, None, None)

        # Give chance for derived class to finalize any data in
        # this object instance. The transaction is passed as a
        # parameter since the transaction object on this instance
        # will have been cleared above.

        self.finalize_data(transaction, *exc_data)
        exc_data = None

        # Give chance for derived class to create a standin node
        # object to be used in the transaction trace. If we get
        # one then give chance for transaction object to do
        # something with it, as well as our parent node.

        node = self.create_node()

        if node:
            transaction._process_node(node)
            parent.process_child(node)

        # ----------------------------------------------------------------------
        # SYNC  | The parent will not have exited yet, so no node will be
        #       | created. This operation is a NOP.
        # ----------------------------------------------------------------------
        # Async | The parent may have exited already while the child was
        #       | running. If this trace is the last node that's running, this
        #       | complete_trace will create the parent node.
        # ----------------------------------------------------------------------
        parent.complete_trace()
示例#24
0
 def __exit__(self, exc, value, tb):
     if self.transaction:
         start_time = self.transaction._async_start_time
         if start_time:
             trace_cache().record_event_loop_wait(start_time, time.time())
示例#25
0
def error_matches_rules(
    rules_prefix,
    exc_info,
    status_code=None,
    settings=None,
):
    """
    Attempt to match exception to rules based on prefix.

    rules_prefix is one of [ignore, expected]
    exc_info is an exception tuple of (exc, val, tb)
    status_code is an optional value or callable taking in exc_info that returns an int-like object
    origin is either the current application or trace.
    """
    # Delay imports to prevent lockups
    from newrelic.core.trace_cache import trace_cache

    if not settings:
        # Pull from current transaction if no settings provided
        tc = trace_cache()
        transaction = tc.current_transaction()
        settings = transaction and transaction.settings

        if not settings:
            # Pull from active trace if no settings on transaction
            trace = tc.current_trace()
            settings = trace and trace.settings

            if not settings:
                # Unable to find rules to match with
                _logger.debug(
                    "Failed to retrieve exception rules: No settings supplied, or found on transaction or trace."
                )
                return None

    # Retrieve settings based on prefix
    classes_rules = getattr(settings.error_collector,
                            "%s_classes" % rules_prefix, set())
    status_codes_rules = getattr(settings.error_collector,
                                 "%s_status_codes" % rules_prefix, set())

    _, _, fullnames, _ = parse_exc_info(exc_info)
    fullname = fullnames[0]

    # Check class names
    for fullname in fullnames:
        if fullname in classes_rules:
            return True

    # Check status_code
    # For callables, call on exc_info to retrieve status_code.
    # It's possible to return None, in which case no code is evaluated.
    if callable(status_code):
        status_code = status_code(*exc_info)

    # Match status_code if it exists
    if status_code is not None:
        try:
            # Coerce into integer
            status_code = int(status_code)
        except:
            _logger.error(
                "Failed to coerce status code into integer. status_code: %s" %
                str(status_code))
        else:
            if status_code in status_codes_rules:
                return True

    return False
示例#26
0
 def __init__(self, trace_cache_id):
     self.trace_cache = trace_cache()
     self.trace = self.trace_cache._cache.get(trace_cache_id)
     self.thread_id = None
     self.restore = None
 def __exit__(self, exc, value, tb):
     # Remove any context from the current thread as it was force propagated above
     if self.force_propagate:
         trace_cache().thread_stop(self.thread_id)
示例#28
0
def remove_from_cache(task):
    cache = trace_cache()
    cache._cache.pop(id(task), None)
示例#29
0
    a = inspect.getfullargspec(func)
    return (a.args, a.varargs, a.varkw, a.defaults)


if hasattr(inspect, 'getfullargspec'):
    _argspec = _argspec_py3
else:
    _argspec = _argspec_py2

from newrelic.core.agent import agent_instance
from newrelic.core.config import global_settings, flatten_settings
from newrelic.api.transaction import Transaction
from newrelic.api.object_wrapper import ObjectWrapper
from newrelic.core.trace_cache import trace_cache

_trace_cache = trace_cache()


def shell_command(wrapped):
    args, varargs, keywords, defaults = _argspec(wrapped)

    parser = optparse.OptionParser()
    for name in args[1:]:
        parser.add_option('--%s' % name, dest=name)

    @functools.wraps(wrapped)
    def wrapper(self, line):
        result = shlex.split(line)

        (options, args) = parser.parse_args(result)
示例#30
0
def main_wrap_wrapper(wrapped, instance, args, kwargs):
    awaitable = wrapped(*args, **kwargs)
    return context_wrapper_async(awaitable, trace_cache().current_thread_id())