Example #1
0
def _increment_ref_count(callback, wrapped, instance, args, kwargs):
    transaction = retrieve_current_transaction()

    if hasattr(callback, '_nr_transaction'):

        if callback._nr_transaction is not None:
            if current_thread_id() != callback._nr_transaction.thread_id:
                # Callback being added not in the main thread; ignore.

                # Since we are not incrementing the counter for this callback,
                # we need to remove the transaction from the callback, so it
                # doesn't get decremented either.
                callback._nr_transaction = None
                return wrapped(*args, **kwargs)

        if transaction is not callback._nr_transaction:
            _logger.error(
                'Attempt to add callback to ioloop with different '
                'transaction attached than in the cache. Please report this'
                ' issue to New Relic support.\n%s',
                ''.join(traceback.format_stack()[:-1]))

            # Since we are not incrementing the counter for this callback,
            # we need to remove the transaction from the callback, so it doesn't
            # get decremented either.
            callback._nr_transaction = None
            return wrapped(*args, **kwargs)

    if transaction is None:
        return wrapped(*args, **kwargs)

    transaction._ref_count += 1

    return wrapped(*args, **kwargs)
Example #2
0
def _requesthandler_function_trace(wrapped, instance, args, kwargs):
    # Use this function tracer when a function you want to trace is called
    # synchronously from a function that is already in the transaction, such as
    # web.RequestHandler._execute.
    transaction = retrieve_current_transaction()

    name = callable_name(wrapped)
    with FunctionTrace(transaction, name=name):
        return wrapped(*args, **kwargs)
Example #3
0
def _nr_wrapper_Runner_run_(wrapped, instance, args, kwargs):
    result = wrapped(*args, **kwargs)

    transaction = retrieve_current_transaction()
    if transaction is None:
        return result

    if instance.finished:
        transaction._ref_count -= 1

    return result
Example #4
0
def _nr_wrapper_curl_httpclient_CurlAsyncHTTPClient_fetch_impl_(
        wrapped, instance, args, kwargs):
    def _bind_params(request, callback, *args, **kwargs):
        return request, callback

    request, callback = _bind_params(*args, **kwargs)

    # There's a delay between when streaming_callback is wrapped (and the
    # transaction is attached) and when it gets added to the IOLoop (no
    # transaction in the Transaction Cache).
    #
    # We want to bypass the check that makes sure the transaction in the
    # Transaction Cache matches the transaction attached to the callback
    # in order to add the callback without producing a logged error.
    #
    # Setting `_nr_transaction` to `None` will:
    #   1. Allow the callback to be added to the IOLoop without error message.
    #   2. Prevent transaction._ref_count from incrementing when added.
    #   3. Prevent transaction._ref_count from decrementing when run.
    #
    # Note that streaming_callback will still be traced when it runs.

    if request.streaming_callback:
        request.streaming_callback._nr_transaction = None

    if callback:
        transaction = retrieve_current_transaction()

        if transaction:
            transaction._ref_count += 1

            @function_wrapper
            def _nr_wrapper_decrementer(_wrapped, _instance, _args, _kwargs):
                try:
                    return _wrapped(*_args, **_kwargs)
                finally:
                    transaction._ref_count -= 1
                    possibly_finalize_transaction(transaction)

            wrapped_callback = _nr_wrapper_decrementer(callback)

            # Replace callback with one that will decrement the ref_count
            # when it runs.

            if len(args) > 1:
                args = list(args)
                args[1] = wrapped_callback
            else:
                kwargs['callback'] = wrapped_callback

    return wrapped(*args, **kwargs)
Example #5
0
def _wrap_decorated(wrapped, instance, args, kwargs):
    # Wraps the output of a tornado decorator.
    #
    # For an example see our wrapper for coroutine,
    # _nr_wrapper_coroutine_wrapper_.

    transaction = retrieve_current_transaction()

    if transaction is None:
        return wrapped(*args, **kwargs)

    name = callable_name(wrapped)
    with FunctionTrace(transaction, name=name):
        return wrapped(*args, **kwargs)
Example #6
0
def _nr_wrapper_stack_context_wrap_(wrapped, instance, args, kwargs):
    # Lots of wrapping going on here. There's the original function, and
    # then 2 layers of wrapping around it.
    #
    # unwrapped_fxn (unwrapped original):
    #     The original function passed into `stack_context.wrap()`.
    #
    # wrapped_fxn (wrapped by Tornado):
    #    The resulting function after `unwrapped_fxn` has been wrapped by
    #    `stack_context.wrap()`.
    #
    # transaction_aware_fxn (wrapped by NR agent):
    #    The resulting function after our `create_transaction_aware_fxn()`
    #    has wrapped `wrapped_fxn` and associated it with the current
    #    transaction.

    def _fxn_arg_extractor(fn, *args, **kwargs):
        # fn is the name of the callable argument in stack_context.wrap
        return fn

    unwrapped_fxn = _fxn_arg_extractor(*args, **kwargs)
    wrapped_fxn = wrapped(*args, **kwargs)

    should_trace = not hasattr(unwrapped_fxn, '_nr_last_object')

    # There are circumstances we want to make a function transaction aware
    # even if we don't want to trace it. If a function is decorated with
    # @function_trace, it will be traced but it will not yet be linked to a
    # transaction. Calling create_transaction_aware_fxn with
    # `should_trace=False` will link the transaction to the function without
    # re-tracing it.
    transaction_aware_fxn = create_transaction_aware_fxn(
        wrapped_fxn, fxn_for_name=unwrapped_fxn, should_trace=should_trace)

    if transaction_aware_fxn is None:
        return wrapped_fxn

    # To prevent stack_context.wrap from re-wrapping this function we attach
    # Tornado's attribute indicating the function was wrapped here.
    transaction_aware_fxn._wrapped = True

    # To prevent us from re-wrapping and to associate the transaction with the
    # function, we attach the transaction as an attribute.
    transaction_aware_fxn._nr_transaction = retrieve_current_transaction()

    return transaction_aware_fxn
Example #7
0
def _nr_wrapper_Future_add_done_callback(wrapped, instance, args, kwargs):
    def _fxn_arg_extractor(fn, *args, **kwargs):
        return fn

    fxn = _fxn_arg_extractor(*args, **kwargs)

    should_trace = not hasattr(fxn, '_nr_last_object')

    # In Tornado, the coroutine runs inside of a lambda or a closure. In this
    # case, we don't know the name of the actual function until it's run. The
    # name is passed as an attribute on the future.
    if hasattr(instance, '_nr_coroutine_name'):
        old_fxn = fxn

        def new_fxn(*_args, **_kwargs):
            return old_fxn(*_args, **_kwargs)

        fxn = new_fxn
        fxn._nr_coroutine_name = instance._nr_coroutine_name

        # Clear name off of future in case the future is reused
        delattr(instance, '_nr_coroutine_name')

    transaction_aware_fxn = create_transaction_aware_fxn(
        fxn, should_trace=should_trace)

    # If transaction_aware_fxn is None then it is already wrapped, or the fxn
    # is None.
    if transaction_aware_fxn is None:
        return wrapped(*args, **kwargs)

    transaction = retrieve_current_transaction()

    transaction_aware_fxn._nr_transaction = transaction

    # We replace the function we call in the callback with the transaction
    # aware version of the function.
    if len(args) > 0:
        args = list(args)
        args[0] = transaction_aware_fxn
    else:
        # Keyword argument name for the callable function is 'fn'.
        kwargs['fn'] = transaction_aware_fxn

    return wrapped(*args, **kwargs)
Example #8
0
def _nr_wrapper_httpclient_AsyncHTTPClient_fetch_(wrapped, instance, args,
                                                  kwargs):

    transaction = retrieve_current_transaction()

    if transaction is None:
        return wrapped(*args, **kwargs)

    req, _cb, _raise_error = _prepare_request(*args, **kwargs)

    # Prepare outgoing CAT headers
    outgoing_headers = ExternalTrace.generate_request_headers(transaction)
    for header_name, header_value in outgoing_headers:
        req.headers[header_name] = header_value

    trace = ExternalTrace(transaction, 'tornado.httpclient', req.url)

    def external_trace_done(future):
        exc_info = future.exc_info()
        if exc_info:
            trace.__exit__(*exc_info)
        else:
            response = future.result()
            # Process CAT response headers
            trace.process_response_headers(response.headers.get_all())
            trace.__exit__(None, None, None)
        transaction._ref_count -= 1

    transaction._ref_count += 1
    trace.__enter__()

    # Because traces are terminal but can be generated concurrently in
    # tornado, pop the trace immediately after entering.
    if trace.transaction and trace.transaction.current_node is trace:
        trace.transaction._pop_current(trace)

    try:
        future = wrapped(req, _cb, _raise_error)
        future.add_done_callback(external_trace_done)
    except Exception:
        transaction._ref_count -= 1
        trace.__exit__(*sys.exc_info())
        raise
    return future
Example #9
0
def _nr_wrapper_Runner__init__(wrapped, instance, args, kwargs):
    # We want to associate a function name from _make_coroutine_wrapper with
    # a call to Runner.__init__. This way we know the name of the function
    # running in Runner and can associate metrics with it.
    # One strategy would be to store the function name on the transaction.
    # This can be problematic because an asynchronous/blocking call can occur
    # in _make_coroutine_wrapper.wrapper before the call to Runner.__init__.
    # This means other coroutines could run in the transaction between when we
    # record the function name in the transaction and before Runner.__init__
    # is called. Since these coroutines run asynchronously and don't nest, we
    # can't use a stack to keep track of which _make_coroutine_wrapper.wrapper
    # call belongs to which Runner.__init__ call.
    # Instead of storing the name in the transaction, we will look up the call
    # stack to get the function name.

    transaction = retrieve_current_transaction()
    if transaction is None:
        return wrapped(*args, **kwargs)

    try:
        # The only place in the Tornado code where a Runner is instantiated
        # is in _make_coroutine_wrapper in Tornado's gen.py. We look up the
        # stack from this call to __init__ to get the function name.
        frame = get_frame(1)

    except ValueError:
        _logger.debug(
            'tornado.gen.Runner is being created at the top of the '
            'stack. That means the Runner object is being created outside '
            'of a tornado.gen decorator. NewRelic will not be able to '
            'name this instrumented function meaningfully (it will be '
            'named lambda.')
        return wrapped(*args, **kwargs)

    # In Python 2 we look up one frame. In Python 3 and PyPy, our wrapping
    # gets in the way and we have to look up 2 frames. In general, we want
    # to go up the call stack until we first encounter the tornado.gen
    # module. We restrict our search to 5 frames.
    max_frame_depth = 5
    frame_depth = 1
    while frame and frame_depth <= max_frame_depth:
        if frame.f_globals['__name__'] == 'tornado.gen':
            break
        frame = frame.f_back
        frame_depth += 1

    # Verify that we are in the frame we think we are.
    if ('__name__' in frame.f_globals
            and frame.f_globals['__name__'] == 'tornado.gen'
            and 'func' in frame.f_locals
            and 'replace_callback' in frame.f_locals
            and frame.f_code.co_name == 'wrapper'):
        instance._nr_coroutine_name = _coroutine_name(frame.f_locals['func'])
    else:
        _logger.debug(
            'tornado.gen.Runner is being called outside of a '
            'tornado.gen decorator (or the tornado implemenation has '
            'changed). NewRelic will not be able to name this instrumented'
            ' function meaningfully (it will be named lambda or inner).')

    # Bump the ref count, so we don't end the transaction before
    # the Runner finishes.

    transaction._ref_count += 1

    return wrapped(*args, **kwargs)