Example #1
0
def _nr_stack_context_wrap_wrapper_(wrapped, instance, args, kwargs):
    def _args(fn, *args, **kwargs):
        return fn

    transaction = retrieve_current_transaction()

    if not transaction:
        return wrapped(*args, **kwargs)

    request = retrieve_transaction_request(transaction)

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

    fn = _args(*args, **kwargs)

    # We need to filter out certain out types of wrapped functions.
    # Tornado 3.1 doesn't use _StackContextWrapper and checks for a
    # '_wrapped' attribute instead which makes this a bit more fragile.

    if hasattr(module_stack_context, '_StackContextWrapper'):
        if (fn is None
                or fn.__class__ is module_stack_context._StackContextWrapper):
            return fn
    else:
        if fn is None or hasattr(fn, '_wrapped'):
            return fn

    fn = FunctionWrapper(fn, _nr_stack_context_wrap_wrapped_(request))

    return wrapped(fn)
Example #2
0
def template_generate_wrapper(wrapped, instance, args, kwargs):
    transaction = retrieve_current_transaction()

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

    with FunctionTrace(transaction,
                       name=instance.name,
                       group='Template/Render'):
        return wrapped(*args, **kwargs)
Example #3
0
def _nr_wrapper_Application___call__wsgi_(wrapped, instance, args, kwargs):
    # This variant of the Application.__call__() wrapper is used when it
    # is believed that we are being called via the WSGI application
    # adapter. That is, someone is trying to use the blocking subset of
    # the Tornado ASYNC APIs in a WSGI application. It is required that
    # there is already a current active transaction at this point.

    transaction = retrieve_current_transaction()

    with FunctionTrace(transaction,
                       name='Request/Process',
                       group='Python/Tornado'):
        return wrapped(*args, **kwargs)
Example #4
0
def _nr_wrapper_Application___call__body_(wrapped, instance, args, kwargs):
    # This variant of the Application.__call__() wrapper is used when it
    # is believed that we are being called for a HTTP request where
    # there is request content. There should already be a transaction
    # associated with the Tornado request object and also a current
    # active transaction.

    transaction = retrieve_current_transaction()

    with FunctionTrace(transaction,
                       name='Request/Process',
                       group='Python/Tornado'):
        return wrapped(*args, **kwargs)
Example #5
0
def _nr_wrapper_RequestHandler__handle_request_exception_(
        wrapped, instance, args, kwargs):

    # The RequestHandler._handle_request_exception() method is called
    # with the details of any unhandled exception. It is believed to
    # always be called in the context of an except block and so we can
    # safely use sys.exc_info() to get the actual details.

    transaction = retrieve_current_transaction()

    if transaction is not None:
        record_exception(transaction, sys.exc_info())

    return wrapped(*args, **kwargs)
Example #6
0
def _nr_wrapper_Task_start_(wrapped, instance, args, kwargs):
    # This wrapper is used around the start() method of the Task class.
    # The Task class executes a callback and we track that as a function
    # trace, naming it after the function the Task holds.

    transaction = retrieve_current_transaction()

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

    name = callable_name(instance.func)

    with FunctionTrace(transaction, name):
        return wrapped(*args, **kwargs)
Example #7
0
def _nr_wrapper_Application___call___(wrapped, instance, args, kwargs):
    # The Application.__call__() method can be called in a number of
    # different circumstances.
    #
    # The first is that it is being called in the context of a WSGI
    # application via the WSGI adapter. There should be no transaction
    # associated with the Tornado request object, but there would be
    # a current active transaction.
    #
    # The second is that it is called for a HTTP request by the Tornado
    # HTTP server where there is no request content. This would occur
    # from the HTTPConnection._on_headers() method. There should be no
    # transaction associated with the Tornado request object and also
    # no current active transaction.
    #
    # The third and final one is where it can be called for a HTTP
    # request by the Tornado HTTP server where there is request content.
    # This would occur from the HTTPConnection._on_request_body()
    # method. There should be a transaction associated with the request
    # object and also a current active transaction.
    #
    # The key method that __call__() in turn calls is the _execute()
    # method of the target RequestHandler.

    def _params(request, *args, **kwargs):
        return request

    request = _params(*args, **kwargs)

    # Check first for the case where we are called via a WSGI adapter.
    # The presumption here is that there can be no ASYNC callbacks.

    transaction = retrieve_request_transaction(request)

    if transaction is None and retrieve_current_transaction():
        return _nr_wrapper_Application___call__wsgi_(wrapped, instance, args,
                                                     kwargs)

    # Now check for where we are being called on a HTTP request where
    # there is no request content.

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

    # Finally have case where being called on a HTTP request where there
    # is request content.

    return _nr_wrapper_Application___call__body_(wrapped, instance, args,
                                                 kwargs)
Example #8
0
def _nr_wrapper_WSGIContainer___call___(wrapped, instance, args, kwargs):
    # This wrapper is used around the call which processes the complete
    # WSGI application execution. We do not have to deal with WSGI
    # itererable responses as that is all handled within the wrapped
    # function.
    #
    # We have to deal with a few cases in this wrapper, made more
    # complicated based on whether WSGIContainer is used in conjunction
    # with a FallbackHandler or whether it is used directly with a
    # HTTPServer instance.
    #
    # If WSGIContainer is used directly with a HTTPServer instance, if
    # we are called when there is no request content, there will be no
    # active transaction. If we are called when there is request content
    # then there will be an active transaction.
    #
    # If WSGIContainer is used in conjunction with a FallbackHandler
    # then there will always be an active transaction whether or not
    # there is any request content. We treat this case the same is if
    # there was request content even though there may not have been.

    def _params(request, *args, **kwargs):
        return request

    request = _params(*args, **kwargs)

    # We need to check to see if an existing transaction object has
    # already been created for the request. If there isn't one but there
    # is an active transaction, something is not right and we don't do
    # anything.

    transaction = retrieve_request_transaction(request)

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

    # Now check for where we are being called on a HTTP request where
    # there is no request content. This will only be where used with
    # HTTPServer directly.

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

    # Finally have case where being called on a HTTP request where there
    # is request content and used directly with HTTPServer or where being
    # used with FallbackHandler.

    return _nr_wrapper_WSGIContainer___call__body_(wrapped, instance,
            args, kwargs)
Example #9
0
    def __call__(self, environ, start_response): 
        transaction = retrieve_current_transaction() 

        if transaction is None: 
            return self.wsgi_application(environ, start_response) 

        name = callable_name(self.wsgi_application) 

        with FunctionTrace(transaction, name='Application', 
                group='Python/WSGI'): 
            with FunctionTrace(transaction, name=name): 
                result = self.wsgi_application(environ, start_response) 

        return _WSGIApplicationIterable(transaction, result)
Example #10
0
def _nr_wrapper_RequestHandler__generate_headers_(wrapped, instance, args,
                                                  kwargs):

    # The RequestHandler._generate_headers() method is where the
    # response headers are injected into the response being written back
    # to the client. We process the response headers before being sent
    # to capture any details from them, but we also inject our own
    # additional headers for support cross process transactions etc.

    transaction = retrieve_current_transaction()

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

    # Thread utilization doesn't make sense in the context of Tornado
    # so we need to stop anything being generated for it.

    transaction._thread_utilization_start = None

    # The HTTPHeaders class with get_all() only started to be used in
    # Tornado 3.0. For older versions have to fall back to combining the
    # dictionary and list of headers. The get_status() method only got
    # added in Tornado 1.2.

    if hasattr(instance, 'get_status'):
        status = '%d ???' % instance.get_status()
    else:
        status = '%d ???' % instance._status_code

    try:
        response_headers = instance._headers.get_all()

    except AttributeError:
        try:
            response_headers = itertools.chain(instance._headers.items(),
                                               instance._list_headers)

        except AttributeError:
            response_headers = itertools.chain(instance._headers.items(),
                                               instance._headers)

    additional_headers = transaction.process_response(status, response_headers,
                                                      *args)

    for name, value in additional_headers:
        instance.add_header(name, value)

    return wrapped(*args, **kwargs)
Example #11
0
def _nr_wrapper_RequestHandler__execute_(wrapped, instance, args, kwargs):
    # Prior to Tornado 3.1, the calling of the handler request method
    # was performed from within RequestHandler._execute(). Any prepare()
    # method was called immediately and could not be a coroutine. For
    # later versions of Tornado, if the prepare() method is a coroutine
    # and the future cannot be completed immediately, then the handler
    # request method will be called from _execute_method() instead when
    # prepare() completes.

    handler = instance
    request = handler.request

    # Check to see if we are being called within the context of any sort
    # of transaction. If we aren't, then we don't bother doing anything and
    # just call the wrapped function.

    transaction = retrieve_current_transaction()

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

    # If the method isn't one of the supported ones, then we expect the
    # wrapped method to raise an exception for HTTPError(405). Name the
    # transaction after the wrapped method first so it is used if that
    # occurs.

    name = callable_name(wrapped)
    transaction.set_transaction_name(name)

    if request.method not in handler.SUPPORTED_METHODS:
        return wrapped(*args, **kwargs)

    # Otherwise we name the transaction after the handler function that
    # should end up being executed for the request. We don't create a
    # function trace node at this point as that is handled by the fact
    # that we wrapped the exposed methods from the wrapper for the
    # constructor of the request handler.

    name = callable_name(getattr(handler, request.method.lower()))
    transaction.set_transaction_name(name)

    # Call the original RequestHandler._execute(). So long as the
    # prepare() method is not a coroutine which doesn't complete
    # straight away, then the actual handler function handler should
    # also be called at this point.

    return wrapped(*args, **kwargs)
Example #12
0
def _nr_wrapper_IOLoop_add_callback_(wrapped, instance, args, kwargs):
    transaction = retrieve_current_transaction()

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

    name = callable_name(wrapped)

    def _args(callback, *args, **kwargs):
        return callback

    callback = _args(*args, **kwargs)

    params = dict(callback=callable_name(callback))

    with FunctionTrace(transaction, name, params=params, terminal=True):
        return wrapped(*args, **kwargs)
Example #13
0
def _nr_wrapper_RequestHandler_render_(wrapped, instance, args, kwargs):
    # Intended to track time spent rendering response content, but
    # adding in the wrapper causes Tornado's calculation of where
    # templates are stored to fail as it walks the stack and looks at
    # where the calling code was stored and assumes templates exist in
    # that directory if an absolute path is not provided. Thus not being
    # used for now.

    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 #14
0
def _nr_wrapper_gen_Runner___init___(wrapped, instance, args, kwargs):
    # This wrapper intercepts the initialisation function of the Runner
    # class and wraps the generator.

    transaction = retrieve_current_transaction()

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

    def _params(gen, *args, **kwargs):
        return gen, args, kwargs

    generator, _args, _kwargs = _params(*args, **kwargs)

    generator = _nr_wrapper_gen_coroutine_generator_(generator)

    return wrapped(generator, *_args, **_kwargs)
Example #15
0
    def _nr_stack_context_function_wrapper_(wrapped, instance, args, kwargs):

        # We can come in here with either an active transaction
        # or a request with a transaction bound to it. If there
        # is an active transaction then call the wrapped function
        # within function trace and return immediately.

        transaction = retrieve_current_transaction()

        if transaction is not None:
            name = callable_name(wrapped)

            with FunctionTrace(transaction, name=name):
                return wrapped(*args, **kwargs)

        # For the case of a request with a transaction bound to
        # it, we need to resume the transaction and then call it.
        # As we resumed the transaction, need to handle
        # suspending or finalizing it.

        transaction = resume_request_monitoring(request)

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

        try:
            name = callable_name(wrapped)

            with FunctionTrace(transaction, name=name):
                return wrapped(*args, **kwargs)

        except:  # Catch all.
            record_exception(transaction, sys.exc_info())
            raise

        finally:
            if not request_finished(request):
                suspend_request_monitoring(request, name='Callback/Wait')

            elif not request.connection.stream.writing():
                finalize_request_monitoring(request)

            else:
                suspend_request_monitoring(request, name='Request/Output')
Example #16
0
def _nr_wrapper_WSGIContainer___call__body_(wrapped, instance, args, kwargs):
    # This variant of the WSGIContainer.__call__() wrapper is used when
    # being used with HTTPServer directly and it is believed that we are
    # being called for a HTTP request where there is request content.
    # This would also be used where FallbackHandler was being used.
    # There should already be a transaction associated with the Tornado
    # request object and also a current active transaction.

    def _params(request, *args, **kwargs):
        return request

    request = _params(*args, **kwargs)

    transaction = retrieve_current_transaction()

    transaction.set_transaction_name(request.uri, 'Uri', priority=1)

    with FunctionTrace(transaction, name='Request/Process',
            group='Python/Tornado'):
        return wrapped(*args, **kwargs)
Example #17
0
def _nr_wrapper_gen_coroutine_function_(wrapped, instance, args, kwargs):
    # This wrapper is applied outside of any instance of the gen.engine
    # and gen.coroutine decorators. We record the call as a function
    # trace, flagging it as a special coroutine instance. This also
    # works on conjunction with the wrapper on the Runner class, which
    # applies a wrapper to the generator when passed to the generator.
    # When this wrapped function is called, if the result of the
    # function that gen.engine or gen.coroutine calls is a generator,
    # the Runner is created and run. If running it, it will always call
    # into the generator at least once. On that first time the wrapper
    # for the generator will see the active transaction and can keep a
    # reference to it to allow the transaction to be reinstated across
    # calls of the generator to yield an item.

    transaction = retrieve_current_transaction()

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

    name = callable_name(wrapped)

    with FunctionTrace(transaction, '%s (coroutine)' % name):
        return wrapped(*args, **kwargs)
Example #18
0
def _nr_wrapper_RequestHandler_on_connection_close(wrapped, instance, args,
                                                   kwargs):

    # The RequestHandler.on_connection_close() method is called when the
    # client closes the connection prematurely before the request had
    # been completed. The callback itself wasn't registered at a point
    # where there was any request tracking so there shouldn't be any
    # active transaction when this is called. We track the call of the
    # wrapped method and then finalize the whole transaction.

    transaction = retrieve_current_transaction()

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

    handler = instance
    request = handler.request

    transaction = resume_request_monitoring(request)

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

    name = callable_name(wrapped)

    try:
        with FunctionTrace(transaction, name):
            result = wrapped(*args, **kwargs)

    except:  # Catch all
        finalize_request_monitoring(request, *sys.exc_info())
        raise

    else:
        finalize_request_monitoring(request)

        return result
Example #19
0
def _nr_wrapper_gen_coroutine_generator_(generator):
    # This wrapper is applied around the generator returned by any
    # function which was wrapped by gen.engine or gen.coroutine. This is
    # to allows us to track separately each call back into the
    # generator. The first time we are called we should be in the
    # context of an active transaction. We need to cache that so we can
    # reinstate the transaction each time we return back into the
    # generator. We also reach back to the active node of the transaction
    # to get the name to be used for each function trace created when
    # the generator is re-entered.

    try:
        value = None
        exc = None

        active_transaction = retrieve_current_transaction()

        transaction = None
        request = None
        name = None

        # Cache the request object associated with the active
        # transaction. If there is a request, then calculate the name
        # for tracking each call into the generator from the current
        # active node. That node should be the function trace node added
        # when our wrapper around gen.engine or gen.coroutine was
        # called.

        request = (active_transaction is not None
                   and retrieve_transaction_request(active_transaction))

        if request is not None:
            active_node = active_transaction.active_node()

            if hasattr(active_node, 'name'):
                active_name = active_node.name

                if active_name.endswith(' (coroutine)'):
                    name = active_name.replace(' (coroutine)', ' (yield)')

        while True:
            # The first time in the loop we should already have
            # inherited an active transaction. On subsequent times
            # however there may not be, in which case we need to resume
            # the transaction associated with the request. We need to
            # remember if we resumed the transaction as we need to
            # then make sure we suspend it again as the caller isn't
            # going to do that for us.

            suspend = None

            if name is not None:
                if retrieve_current_transaction() is None:
                    transaction = resume_request_monitoring(request)
                    suspend = transaction
                else:
                    transaction = active_transaction

            # The following code sits between the consumer of the
            # generator and the generator itself. It will add a function
            # trace around each call into the generator to yield a
            # value. Annotate the function trace with the location of
            # the code within the generator which will be executed.

            try:
                params = {}

                gi_frame = generator.gi_frame

                params['filename'] = gi_frame.f_code.co_filename
                params['lineno'] = gi_frame.f_lineno

                with FunctionTrace(transaction, name, params=params):
                    try:
                        if exc is not None:
                            yielded = generator.throw(*exc)
                            exc = None
                        else:
                            yielded = generator.send(value)

                    except (GeneratorReturn, StopIteration):
                        raise

                    except:  # Catch all.
                        # We need to record exceptions at this point
                        # as the call back into the generator could
                        # have been triggered by a future direct from
                        # the main loop. There isn't therefore anywhere
                        # else it can be captured.

                        if transaction is not None:
                            record_exception(transaction, sys.exc_info())

                        raise

            finally:
                if suspend is not None:
                    suspend_request_monitoring(request, name='Callback/Wait')

            # XXX This could present a problem if we are yielding a
            # future as the future will be scheduled outside of the
            # context of the active transaction if we had to do a
            # suspend of the transaction since we resumed it. We can't
            # do the suspend after the yield as it is during the yield
            # that control is returned back to the main loop.

            try:
                value = yield yielded

            except Exception:
                exc = sys.exc_info()

    finally:
        generator.close()
Example #20
0
def _nr_wrapper_HTTPConnection__finish_request(wrapped, instance, args,
                                               kwargs):

    # Normally called when the request is all complete meaning that we
    # have to finalize our own transaction. We may actually enter here
    # with the transaction already being the current one.

    connection = instance
    request = connection._request

    transaction = retrieve_request_transaction(request)

    # The wrapped function could be called more than once. If it is then
    # the transaction should already have been completed. In this case
    # the transaction should be None. To be safe also check whether the
    # request itself was already flagged as finished. If transaction was
    # the same as the current transaction the following check would have
    # just marked it as finished again, but this first check will cover
    # where the current transaction is for some reason different.

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

    if request_finished(request):
        return wrapped(*args, **kwargs)

    # Deal now with the possibility that the transaction is already the
    # current active transaction.

    if transaction == retrieve_current_transaction():
        finish_request_monitoring(request)

        return wrapped(*args, **kwargs)

    # If we enter here with an active transaction and it isn't the one
    # we expect, then not sure what we should be doing, so simply
    # return. This should hopefully never occur.

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

    # Not the current active transaction and so we need to try and
    # resume the transaction associated with the request.

    transaction = resume_request_monitoring(request)

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

    finish_request_monitoring(request)

    try:
        result = wrapped(*args, **kwargs)

    except:  # Catch all
        # There should never be an error from wrapped function but
        # in case there is, try finalizing transaction.

        finalize_request_monitoring(request, *sys.exc_info())
        raise

    finalize_request_monitoring(request)

    return result
Example #21
0
def _nr_wrapper_HTTPConnection__on_headers_(wrapped, instance, args, kwargs):
    # This is the first point at which we should ever be called for a
    # request. It is called when the request headers have been read in.
    # The next phase would be to read in any request content but we
    # can't tell whether that will happen or not at this point. We do
    # need to setup a callback when connection is closed due to client
    # disconnecting.

    assert instance is not None

    connection = instance

    # Check to see if we are being called within the context of any sort
    # of transaction. This should never occur, but if we are, then we
    # don't bother doing anything and just call the wrapped function
    # immediately as can't be sure what else to do.

    transaction = retrieve_current_transaction()

    if transaction is not None:
        _logger.error(
            'Runtime instrumentation error. Starting a new '
            'Tornado web request but there is a transaction active '
            'already. Report this issue to New Relic support.\n%s',
            ''.join(traceback.format_stack()[:-1]))

        last = last_transaction_activation()

        if last is not None:
            _logger.info(
                'The currently active transaction was possibly '
                'initiated or resumed from %r.', last)

        return wrapped(*args, **kwargs)

    # Execute the wrapped function as we are only going to do something
    # after it has been called.

    result = wrapped(*args, **kwargs)

    # Check to see if the connection has already been closed or the
    # request finished. The connection can be closed where request
    # content length was too big.

    if connection.stream.closed():
        return result

    if connection._request_finished:
        return result

    # Check to see if we have already associated a transaction with the
    # request. This would happen if there was actually no request
    # content and so the application was called immediately. We can
    # return straight away in that case.

    request = connection._request

    if request is None:
        return result

    transaction = retrieve_request_transaction(request)

    if transaction is not None:
        return result

    # If we get here it is because there was request content which first
    # had to be read. No transaction should have been created as yet.
    # Create the transaction but if it is None then it means recording
    # of transactions is not enabled then do not need to do anything.

    transaction = initiate_request_monitoring(request)

    if transaction is None:
        return result

    # Add a callback variable to the connection object so that we can
    # be notified when the connection is closed before all the request
    # content has been read. This will be invoked from the method
    # BaseIOStream._maybe_run_close_callback().

    def _close():
        transaction = resume_request_monitoring(request)

        if transaction is None:
            return

        # Force a function trace to record the fact that the socket
        # connection was closed due to client disconnection.

        with FunctionTrace(transaction,
                           name='Request/Close',
                           group='Python/Tornado'):
            pass

        # We finish up the transaction and nothing else should occur.

        finalize_request_monitoring(request)

    connection.stream._nr_close_callback = _close

    # Name transaction initially after the wrapped function so that if
    # the connection is dropped before all the request content is read,
    # then we don't get metric grouping issues with it being named after
    # the URL.

    name = callable_name(wrapped)

    transaction.set_transaction_name(name)

    # Now suspend monitoring of current transaction until next callback.

    suspend_request_monitoring(request, name='Request/Input')

    return result
Example #22
0
def _nr_wrapper_RequestHandler_finish_(wrapped, instance, args, kwargs):
    # The RequestHandler.finish() method will either be called explicitly
    # by the user, but called also be called automatically by Tornado.
    # It is possible that it can be called twice so it is necessary to
    # protect against that.

    handler = instance
    request = handler.request

    # Bail out out if we think the request as a whole has been completed.

    if request_finished(request):
        return wrapped(*args, **kwargs)

    # Call wrapped method straight away if request object it is being
    # called on is not even associated with a transaction. If we were in
    # a running transaction we still want to record the call though.
    # This will occur when calling finish on another request, but the
    # target request wasn't monitored.

    transaction = retrieve_request_transaction(request)

    active_transaction = retrieve_current_transaction()

    if transaction is None:
        if active_transaction is not None:
            name = callable_name(wrapped)

            with FunctionTrace(active_transaction, name):
                return wrapped(*args, **kwargs)

        else:
            return wrapped(*args, **kwargs)

    # If we have an active transaction, we we need to consider two
    # possiblities. The first is where the current running transaction
    # doesn't match that bound to the request. For this case it would be
    # where from within one transaction there is an attempt to call
    # finish() on a distinct web request which was being monitored. The
    # second is where finish() is being called for the current request.

    if active_transaction is not None:
        if transaction != active_transaction:
            # For this case we need to suspend the current running
            # transaction and call ourselves again. When it returns
            # we need to restore things back the way they were.
            # We still trace the call in the running transaction
            # though so the fact that it called finish on another
            # request is apparent.

            name = callable_name(wrapped)

            with FunctionTrace(active_transaction, name):
                try:
                    active_transaction.drop_transaction()

                    return _nr_wrapper_RequestHandler_finish_(
                        wrapped, instance, args, kwargs)

                finally:
                    active_transaction.save_transaction()

        else:
            # For this case we just trace the call.

            name = callable_name(wrapped)

            with FunctionTrace(active_transaction, name):
                return wrapped(*args, **kwargs)

    # Attempt to resume the transaction, calling the wrapped method
    # straight away if there isn't one. Otherwise trace the call.

    transaction = resume_request_monitoring(request)

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

    try:
        name = callable_name(wrapped)

        with FunctionTrace(transaction, name):
            result = wrapped(*args, **kwargs)

    except:  # Catch all
        finalize_request_monitoring(request, *sys.exc_info())
        raise

    else:
        if not request.connection.stream.writing():
            finalize_request_monitoring(request)

        else:
            suspend_request_monitoring(request, name='Request/Output')

        return result