Пример #1
0
    def __call__(self, environ, start_response):
        transaction = newrelic.api.transaction.current_transaction()

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

        if transaction:
            # Record details of framework against the transaction
            # for later reporting as supportability metrics.

            if self._nr_framework:
                transaction._frameworks.add(self._nr_framework)

            # Override the web transaction name to be the name of the
            # wrapped callable if not explicitly named, and we want the
            # default name to be that of the WSGI component for the
            # framework. This will override the use of a raw URL which
            # can result in metric grouping issues where a framework is
            # not instrumented or is leaking URLs.

            settings = transaction._settings

            if self._nr_name is None and settings:
                if self._nr_framework is not None:
                    naming_scheme = settings.transaction_name.naming_scheme
                    if naming_scheme in (None, 'framework'):
                        name = newrelic.api.object_wrapper.callable_name(
                                self._nr_next_object)
                        transaction.name_transaction(name, priority=1)

            elif self._nr_name:
                transaction.name_transaction(self._nr_name, self._nr_group,
                      priority=1)

            return self._nr_next_object(environ, start_response)

        # Otherwise treat it as top level transaction.
        # We have to though look first to see whether the
        # application name has been overridden through
        # the WSGI environ dictionary.

        app_name = environ.get('newrelic.app_name')

        if app_name:
            if app_name.find(';') != -1:
                app_names = [string.strip(n) for n in app_name.split(';')]
                app_name = app_names[0]
                application = newrelic.api.application.application_instance(
                        app_name)
                for altname in app_names[1:]:
                    application.link_to_application(altname)
            else:
                application = newrelic.api.application.application_instance(
                        app_name)
        else:
            application = self._nr_application

            # If application has an activate() method we assume it is an
            # actual application. Do this rather than check type so that
            # can easily mock it for testing.

            # FIXME Should this allow for multiple apps if a string.

            if not hasattr(application, 'activate'):
                application = newrelic.api.application.application_instance(
                        application)

        # Now start recording the actual web transaction.

        transaction = WebTransaction(application, environ)
        transaction.__enter__()

        # Record details of framework against the transaction
        # for later reporting as supportability metrics.

        if self._nr_framework:
            transaction._frameworks.add(self._nr_framework)

        # Override the initial web transaction name to be the supplied
        # name, or the name of the wrapped callable if wanting to use
        # the callable as the default. This will override the use of a
        # raw URL which can result in metric grouping issues where a
        # framework is not instrumented or is leaking URLs.
        #
        # Note that at present if default for naming scheme is still
        # None and we aren't specifically wrapping a designated
        # framework, then we still allow old URL based naming to
        # override. When we switch to always forcing a name we need to
        # check for naming scheme being None here.

        settings = transaction._settings

        if self._nr_name is None and settings:
            naming_scheme = settings.transaction_name.naming_scheme

            if self._nr_framework is not None:
                if naming_scheme in (None, 'framework'):
                    name = newrelic.api.object_wrapper.callable_name(
                            self._nr_next_object)
                    transaction.name_transaction(name, priority=1)

            elif naming_scheme in ('component', 'framework'):
                name = newrelic.api.object_wrapper.callable_name(
                        self._nr_next_object)
                transaction.name_transaction(name, priority=1)

        elif self._nr_name:
            transaction.name_transaction(self._nr_name, self._nr_group,
                  priority=1)

        def _start_response(status, response_headers, *args):

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

            _write = start_response(status,
                    response_headers+additional_headers, *args)

            def write(data):
                if not transaction._sent_start:
                    transaction._sent_start = time.time()
                result = _write(data)
                transaction._calls_write += 1
                try:
                    transaction._bytes_sent += len(data)
                except Exception:
                    pass
                transaction._sent_end = time.time()
                return result

            return write

        try:
            # Should always exist, but check as test harnesses may not
            # have it.

            if 'wsgi.input' in environ:
                environ['wsgi.input'] = WSGIInputWrapper(transaction,
                        environ['wsgi.input'])

            application = newrelic.api.function_trace.FunctionTraceWrapper(
                    self._nr_next_object)

            with newrelic.api.function_trace.FunctionTrace(
                    transaction, name='Application', group='Python/WSGI'):
                result = application(environ, _start_response)
        except:  # Catch all
            transaction.__exit__(*sys.exc_info())
            raise

        return _WSGIApplicationIterable(transaction, result)
    def __call__(self, environ, start_response):
        transaction = newrelic.api.transaction.current_transaction()

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

        if transaction:
            return self._nr_next_object(environ, start_response)

        # Otherwise treat it as top level transaction.
        # We have to though look first to see whether the
        # application name has been overridden through
        # the WSGI environ dictionary.

        app_name = environ.get('newrelic.app_name')

        if app_name:
            if app_name.find(';') != -1:
                app_names = [string.strip(n) for n in app_name.split(';')]
                app_name = app_names[0]
                application = newrelic.api.application.application_instance(
                        app_name)
                for altname in app_names[1:]:
                    application.link_to_application(altname)
            else:
                application = newrelic.api.application.application_instance(
                        app_name)
        else:
            application = self._nr_application

            # FIXME Should this allow for multiple apps if a string.

            if type(application) != newrelic.api.application.Application:
                application = newrelic.api.application.application_instance(
                        application)

        # Now start recording the actual web transaction.

        transaction = WebTransaction(application, environ)
        transaction.__enter__()

        def _start_response(status, response_headers, *args):
            try:
                transaction.response_code = int(status.split(' ')[0])
            except:
                pass

            try:
                header = filter(lambda x: x[0].lower() == 'content-length',
                                response_headers)[-1:]
                if header:
                    transaction._response_properties['CONTENT_LENGTH'] = \
                            header[0][1]
            except:
                pass

            _write = start_response(status, response_headers, *args)

            def write(data):
                if not transaction._sent_start:
                    transaction._sent_start = time.time()
                result = _write(data)
                transaction._calls_write += 1
                try:
                    transaction._bytes_sent += len(data)
                except:
                    pass
                transaction._sent_end = time.time()
                return result

            return write

        try:
            # Should always exist, but check as test harnesses may not
            # have it.

            if 'wsgi.input' in environ:
                environ['wsgi.input'] = WSGIInputWrapper(transaction,
                        environ['wsgi.input'])

            application = newrelic.api.function_trace.FunctionTraceWrapper(
                    self._nr_next_object)

            with newrelic.api.function_trace.FunctionTrace(
                    transaction, name='Application', group='Python/WSGI'):
                result = application(environ, _start_response)
        except:
            transaction.__exit__(*sys.exc_info())
            raise

        return _WSGIApplicationIterable(transaction, result)
Пример #3
0
    def __call__(self):
        assert self._nr_instance != None

        transaction = newrelic.api.transaction.current_transaction()

        # If there is an active transaction then deferred is being
        # called within a context of another deferred so simply call the
        # callback and return.

        if transaction:
            return self._nr_next_object()

        # If there is no transaction recorded against the deferred then
        # don't need to do anything and can simply call the callback and
        # return.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        transaction = self._nr_instance._nr_transaction

        # If we can't find a Twisted.Web request object associated with
        # the transaction or it is no longer valid then simply call the
        # callback and return.

        if not hasattr(transaction, '_nr_current_request'):
            return self._nr_next_object()

        request = transaction._nr_current_request()

        if not request:
            return self._nr_next_object()

        try:
            # Save the transaction recorded against the deferred as the
            # active transaction.

            transaction.save_transaction()

            # Record that are calling a deferred. This changes what we
            # do if the request finish() method is being called.

            request._nr_is_deferred_callback = True

            # We should always be calling into a deferred when we are
            # in the wait state for the request. We need to exit that
            # wait state.

            if request._nr_wait_function_trace:
                request._nr_wait_function_trace.__exit__(None, None, None)
                request._nr_wait_function_trace = None

            else:
                _logger.debug('Called a Twisted.Web deferred when we were '
                        'not in a wait state.')

            # Call the deferred and capture any errors that may come
            # back from it.

            with newrelic.api.error_trace.ErrorTrace(transaction):
                with newrelic.api.function_trace.FunctionTrace(
                        transaction, name='Deferred/Call',
                        group='Python/Twisted'):
                    return self._nr_next_object()

        finally:
            # If the request finish() method was called from the
            # deferred then we need to exit the transaction. Other wise
            # we need to create a new function trace node for a new wait
            # state and pop the transaction.

            if request._nr_is_request_finished:
                transaction.__exit__(None, None, None)
                self._nr_instance._nr_transaction = None

            else:
                # XXX Should we be removing the transaction from the
                # deferred object as well. Can the same deferred be
                # called multiple times for same request. It probably
                # can be reregistered.

                request._nr_wait_function_trace = \
                        newrelic.api.function_trace.FunctionTrace(
                        transaction, name='Deferred/Wait',
                        group='Python/Twisted')

                request._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

            request._nr_is_deferred_callback = False
Пример #4
0
    def __call__(self, environ, start_response):
        transaction = newrelic.api.transaction.current_transaction()

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

        if transaction:
            # Record details of framework against the transaction
            # for later reporting as supportability metrics.

            if self._nr_framework:
                transaction._frameworks.add(self._nr_framework)

            # Override the web transaction name to be the name of the
            # wrapped callable if not explicitly named, and we want the
            # default name to be that of the WSGI component for the
            # framework. This will override the use of a raw URL which
            # can result in metric grouping issues where a framework is
            # not instrumented or is leaking URLs.

            settings = transaction._settings

            if self._nr_name is None and settings:
                if self._nr_framework is not None:
                    naming_scheme = settings.transaction_name.naming_scheme
                    if naming_scheme in (None, 'framework'):
                        name = newrelic.api.object_wrapper.callable_name(
                                self._nr_next_object)
                        transaction.set_transaction_name(name, priority=1)

            elif self._nr_name:
                transaction.set_transaction_name(self._nr_name,
                        self._nr_group, priority=1)

            return self._nr_next_object(environ, start_response)

        # Otherwise treat it as top level transaction.
        # We have to though look first to see whether the
        # application name has been overridden through
        # the WSGI environ dictionary.

        app_name = environ.get('newrelic.app_name')

        if app_name:
            if app_name.find(';') != -1:
                app_names = [string.strip(n) for n in app_name.split(';')]
                app_name = app_names[0]
                application = newrelic.api.application.application_instance(
                        app_name)
                for altname in app_names[1:]:
                    application.link_to_application(altname)
            else:
                application = newrelic.api.application.application_instance(
                        app_name)
        else:
            application = self._nr_application

            # If application has an activate() method we assume it is an
            # actual application. Do this rather than check type so that
            # can easily mock it for testing.

            # FIXME Should this allow for multiple apps if a string.

            if not hasattr(application, 'activate'):
                application = newrelic.api.application.application_instance(
                        application)

        # Now start recording the actual web transaction.

        transaction = WebTransaction(application, environ)
        transaction.__enter__()

        # Record details of framework against the transaction
        # for later reporting as supportability metrics.

        if self._nr_framework:
            transaction._frameworks.add(self._nr_framework)

        # Override the initial web transaction name to be the supplied
        # name, or the name of the wrapped callable if wanting to use
        # the callable as the default. This will override the use of a
        # raw URL which can result in metric grouping issues where a
        # framework is not instrumented or is leaking URLs.
        #
        # Note that at present if default for naming scheme is still
        # None and we aren't specifically wrapping a designated
        # framework, then we still allow old URL based naming to
        # override. When we switch to always forcing a name we need to
        # check for naming scheme being None here.

        settings = transaction._settings

        if self._nr_name is None and settings:
            naming_scheme = settings.transaction_name.naming_scheme

            if self._nr_framework is not None:
                if naming_scheme in (None, 'framework'):
                    name = newrelic.api.object_wrapper.callable_name(
                            self._nr_next_object)
                    transaction.set_transaction_name(name, priority=1)

            elif naming_scheme in ('component', 'framework'):
                name = newrelic.api.object_wrapper.callable_name(
                        self._nr_next_object)
                transaction.set_transaction_name(name, priority=1)

        elif self._nr_name:
            transaction.set_transaction_name(self._nr_name, self._nr_group,
                  priority=1)

        def _start_response(status, response_headers, *args):

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

            _write = start_response(status,
                    response_headers+additional_headers, *args)

            def write(data):
                if not transaction._sent_start:
                    transaction._sent_start = time.time()
                result = _write(data)
                transaction._calls_write += 1
                try:
                    transaction._bytes_sent += len(data)
                except Exception:
                    pass
                transaction._sent_end = time.time()
                return result

            return write

        try:
            # Should always exist, but check as test harnesses may not
            # have it.

            if 'wsgi.input' in environ:
                environ['wsgi.input'] = WSGIInputWrapper(transaction,
                        environ['wsgi.input'])

            application = newrelic.api.function_trace.FunctionTraceWrapper(
                    self._nr_next_object)

            with newrelic.api.function_trace.FunctionTrace(
                    transaction, name='Application', group='Python/WSGI'):
                result = application(environ, _start_response)
        except:  # Catch all
            transaction.__exit__(*sys.exc_info())
            raise

        return _WSGIApplicationIterable(transaction, result)
Пример #5
0
    def __call__(self):
        assert self._nr_instance != None

        transaction = newrelic.api.transaction.current_transaction()

        # Check to see if we are being called within the context of any
        # sort of transaction. If we are, then we don't bother doing
        # anything and just call the wrapped function. This should not
        # really ever occur with Twisted.Web wrapper but check anyway.

        if transaction:
            return self._nr_next_object()

        # Always use the default application specified in the agent
        # configuration.

        application = newrelic.api.application.application_instance()

        # We need to fake up a WSGI like environ dictionary with the key
        # bits of information we need.

        environ = {}

        environ['REQUEST_URI'] = self._nr_instance.path

        # Now start recording the actual web transaction.

        transaction = newrelic.api.web_transaction.WebTransaction(
                application, environ)

        if not transaction.enabled:
            return self._nr_next_object()

        transaction.__enter__()

        self._nr_instance._nr_transaction = transaction

        self._nr_instance._nr_is_deferred_callback = False
        self._nr_instance._nr_is_request_finished = False
        self._nr_instance._nr_wait_function_trace = None

        # We need to add a reference to the Twisted.Web request object
        # in the transaction as only able to stash the transaction in a
        # deferred. Need to use a weakref to avoid an object cycle which
        # may prevent cleanup of transaction.

        transaction._nr_current_request = weakref.ref(self._nr_instance)

        try:
            # Call the original method in a trace object to give better
            # context in transaction traces. Three things can happen
            # within this call. The render() function which is in turn
            # called can return a result immediately which means user
            # code should have called finish() on the request, it can
            # raise an exception which is caught in process() function
            # where error handling calls finish(), or it can return that
            # it is not done yet and register deferred callbacks to
            # complete the request.

            with newrelic.api.function_trace.FunctionTrace(transaction,
                    name='Request/Process', group='Python/Twisted'):
                result = self._nr_next_object()

            # In the case of a result having being returned or an
            # exception occuring, then finish() will have been called.
            # We can't just exit the transaction in the finish call
            # however as need to still pop back up through the above
            # function trace. So if flagged that have finished, then we
            # exit the transaction here. Otherwise we setup a function
            # trace to track wait time for deferred and manually pop the
            # transaction as being the current one for this thread.

            if self._nr_instance._nr_is_request_finished:
                transaction.__exit__(None, None, None)
                self._nr_instance._nr_transaction = None
                self._nr_instance = None

            else:
                self._nr_instance._nr_wait_function_trace = \
                        newrelic.api.function_trace.FunctionTrace(
                        transaction, name='Deferred/Wait',
                        group='Python/Twisted')

                self._nr_instance._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

        except:  # Catch all
            # If an error occurs assume that transaction should be
            # exited. Technically don't believe this should ever occur
            # unless our code here has an error or Twisted.Web is
            # broken.

            _logger.exception('Unexpected exception raised by Twisted.Web '
                    'Request.process() exception.')

            transaction.__exit__(*sys.exc_info())
            self._nr_instance._nr_transaction = None
            self._nr_instance = None

            raise

        return result
Пример #6
0
    def __call__(self):
        assert self._nr_instance != None

        # Call finish() method straight away if request is not even
        # associated with a transaction.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        # Technically we should only be able to be called here without
        # an active transaction if we are in the wait state. If we
        # are called in context of original request process() function
        # or a deferred the transaction should already be registered.

        transaction = self._nr_instance._nr_transaction

        if self._nr_instance._nr_wait_function_trace:
            if newrelic.api.transaction.current_transaction():
                _logger.debug('The Twisted.Web request finish() method is '
                        'being called while in wait state but there is '
                        'already a current transaction.')
            else:
                transaction.save_transaction()

        elif not newrelic.api.transaction.current_transaction():
            _logger.debug('The Twisted.Web request finish() method is '
                    'being called from request process() method or a '
                    'deferred but there is not a current transaction.')

        # Except for case of being called when in wait state, we can't
        # actually exit the transaction at this point as may be called
        # in context of an outer function trace node. We thus flag that
        # are finished and pop back out allowing outer scope to actually
        # exit the transaction.

        self._nr_instance._nr_is_request_finished = True

        # Now call the original finish() function.

        if self._nr_instance._nr_is_deferred_callback:

            # If we are in a deferred callback log any error against the
            # transaction here so we know we will capture it. We
            # possibly don't need to do it here as outer scope may catch
            # it anyway. Duplicate will be ignored so not too important.
            # Most likely the finish() call would never fail anyway.

            try:
                with newrelic.api.function_trace.FunctionTrace(transaction,
                        name='Request/Finish', group='Python/Twisted'):
                    result = self._nr_next_object()

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

        elif self._nr_instance._nr_wait_function_trace:

            # Now handle the special case where finish() was called
            # while in the wait state. We might get here through
            # Twisted.Web itself somehow calling finish() when still
            # waiting for a deferred. If this were to occur though then
            # the transaction will not be popped if we simply marked
            # request as finished as no outer scope to see that and
            # clean up. We will thus need to end the function trace and
            # exit the transaction. We end function trace here and then
            # the transaction down below.

            try:
                self._nr_instance._nr_wait_function_trace.__exit__(
                        None, None, None)

                with newrelic.api.function_trace.FunctionTrace(transaction,
                        name='Request/Finish', group='Python/Twisted'):
                    result = self._nr_next_object()

                transaction.__exit__(None, None, None)

            except:  # Catch all
                transaction.__exit__(*sys.exc_info())
                raise

            finally:
                self._nr_instance._nr_wait_function_trace = None
                self._nr_instance._nr_transaction = None
                self._nr_instance = None

        else:
            # This should be the case where finish() is being called in
            # the original render() function.

            with newrelic.api.function_trace.FunctionTrace(transaction,
                    name='Request/Finish', group='Python/Twisted'):
                result = self._nr_next_object()

        return result
    def __call__(self):
        assert self._nr_instance != None

        transaction = newrelic.api.transaction.current_transaction()

        # Check to see if we are being called within the context of any
        # sort of transaction. If we are, then we don't bother doing
        # anything and just call the wrapped function. This should not
        # really ever occur with Twisted.Web wrapper but check anyway.

        if transaction:
            return self._nr_next_object()

        # Always use the default application specified in the agent
        # configuration.

        application = newrelic.api.application.application_instance()

        # We need to fake up a WSGI like environ dictionary with the key
        # bits of information we need.

        environ = {}

        environ['REQUEST_URI'] = self._nr_instance.path

        # Now start recording the actual web transaction.

        transaction = newrelic.api.web_transaction.WSGIWebTransaction(
                application, environ, source=self._nr_next_object)

        if not transaction.enabled:
            return self._nr_next_object()

        transaction.__enter__()

        self._nr_instance._nr_transaction = transaction

        self._nr_instance._nr_is_deferred_callback = False
        self._nr_instance._nr_is_request_finished = False
        self._nr_instance._nr_wait_function_trace = None

        # We need to add a reference to the Twisted.Web request object
        # in the transaction as only able to stash the transaction in a
        # deferred. Need to use a weakref to avoid an object cycle which
        # may prevent cleanup of transaction.

        transaction._nr_current_request = weakref.ref(self._nr_instance)

        try:
            # Call the original method in a trace object to give better
            # context in transaction traces. Three things can happen
            # within this call. The render() function which is in turn
            # called can return a result immediately which means user
            # code should have called finish() on the request, it can
            # raise an exception which is caught in process() function
            # where error handling calls finish(), or it can return that
            # it is not done yet and register deferred callbacks to
            # complete the request.

            result = newrelic.api.function_trace.FunctionTraceWrapper(self._nr_next_object, name='Request/Process', group='Python/Twisted')

            # In the case of a result having being returned or an
            # exception occuring, then finish() will have been called.
            # We can't just exit the transaction in the finish call
            # however as need to still pop back up through the above
            # function trace. So if flagged that have finished, then we
            # exit the transaction here. Otherwise we setup a function
            # trace to track wait time for deferred and manually pop the
            # transaction as being the current one for this thread.

            if self._nr_instance._nr_is_request_finished:
                transaction.__exit__(None, None, None)
                self._nr_instance._nr_transaction = None
                self._nr_instance = None

            else:
                self._nr_instance._nr_wait_function_trace = \
                        newrelic.api.function_trace.FunctionTrace(
                        name='Deferred/Wait',
                        group='Python/Twisted',
                        source=self._nr_next_object)

                self._nr_instance._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

        except:  # Catch all
            # If an error occurs assume that transaction should be
            # exited. Technically don't believe this should ever occur
            # unless our code here has an error or Twisted.Web is
            # broken.

            _logger.exception('Unexpected exception raised by Twisted.Web '
                    'Request.process() exception.')

            transaction.__exit__(*sys.exc_info())
            self._nr_instance._nr_transaction = None
            self._nr_instance = None

            raise

        return result
    def __call__(self):
        assert self._nr_instance != None

        transaction = newrelic.api.transaction.current_transaction()

        # If there is an active transaction then deferred is being
        # called within a context of another deferred so simply call the
        # callback and return.

        if transaction:
            return self._nr_next_object()

        # If there is no transaction recorded against the deferred then
        # don't need to do anything and can simply call the callback and
        # return.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        transaction = self._nr_instance._nr_transaction

        # If we can't find a Twisted.Web request object associated with
        # the transaction or it is no longer valid then simply call the
        # callback and return.

        if not hasattr(transaction, '_nr_current_request'):
            return self._nr_next_object()

        request = transaction._nr_current_request()

        if not request:
            return self._nr_next_object()

        try:
            # Save the transaction recorded against the deferred as the
            # active transaction.

            transaction.save_transaction()

            # Record that are calling a deferred. This changes what we
            # do if the request finish() method is being called.

            request._nr_is_deferred_callback = True

            # We should always be calling into a deferred when we are
            # in the wait state for the request. We need to exit that
            # wait state.

            if request._nr_wait_function_trace:
                request._nr_wait_function_trace.__exit__(None, None, None)
                request._nr_wait_function_trace = None

            else:
                _logger.debug('Called a Twisted.Web deferred when we were '
                        'not in a wait state.')

            # Call the deferred and capture any errors that may come
            # back from it.

            with newrelic.api.error_trace.ErrorTrace():
                return newrelic.api.function_trace.FunctionTraceWrapper(self._nr_next_object, name='Deferred/Call', group='Python/Twisted')

        finally:
            # If the request finish() method was called from the
            # deferred then we need to exit the transaction. Other wise
            # we need to create a new function trace node for a new wait
            # state and pop the transaction.

            if request._nr_is_request_finished:
                transaction.__exit__(None, None, None)
                self._nr_instance._nr_transaction = None

            else:
                # XXX Should we be removing the transaction from the
                # deferred object as well. Can the same deferred be
                # called multiple times for same request. It probably
                # can be reregistered.

                request._nr_wait_function_trace = \
                        newrelic.api.function_trace.FunctionTrace(
                        name='Deferred/Wait',
                        group='Python/Twisted',
                        source=self._nr_next_object)

                request._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

            request._nr_is_deferred_callback = False
    def __call__(self):
        assert self._nr_instance != None

        # Call finish() method straight away if request is not even
        # associated with a transaction.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        # Technically we should only be able to be called here without
        # an active transaction if we are in the wait state. If we
        # are called in context of original request process() function
        # or a deferred the transaction should already be registered.

        transaction = self._nr_instance._nr_transaction

        if self._nr_instance._nr_wait_function_trace:
            if newrelic.api.transaction.current_transaction():
                _logger.debug('The Twisted.Web request finish() method is '
                        'being called while in wait state but there is '
                        'already a current transaction.')
            else:
                transaction.save_transaction()

        elif not newrelic.api.transaction.current_transaction():
            _logger.debug('The Twisted.Web request finish() method is '
                    'being called from request process() method or a '
                    'deferred but there is not a current transaction.')

        # Except for case of being called when in wait state, we can't
        # actually exit the transaction at this point as may be called
        # in context of an outer function trace node. We thus flag that
        # are finished and pop back out allowing outer scope to actually
        # exit the transaction.

        self._nr_instance._nr_is_request_finished = True

        # Now call the original finish() function.

        if self._nr_instance._nr_is_deferred_callback:

            # If we are in a deferred callback log any error against the
            # transaction here so we know we will capture it. We
            # possibly don't need to do it here as outer scope may catch
            # it anyway. Duplicate will be ignored so not too important.
            # Most likely the finish() call would never fail anyway.

            try:
                result = newrelic.api.function_trace.FunctionTraceWrapper(self._nr_next_object, name='Request/Finish', group='Python/Twisted')
            except:  # Catch all
                notice_error(sys.exc_info())
                raise

        elif self._nr_instance._nr_wait_function_trace:

            # Now handle the special case where finish() was called
            # while in the wait state. We might get here through
            # Twisted.Web itself somehow calling finish() when still
            # waiting for a deferred. If this were to occur though then
            # the transaction will not be popped if we simply marked
            # request as finished as no outer scope to see that and
            # clean up. We will thus need to end the function trace and
            # exit the transaction. We end function trace here and then
            # the transaction down below.

            try:
                self._nr_instance._nr_wait_function_trace.__exit__(
                        None, None, None)

                result = newrelic.api.function_trace.FunctionTraceWrapper(self._nr_next_object, name='Request/Finish', group='Python/Twisted')


                transaction.__exit__(None, None, None)

            except:  # Catch all
                transaction.__exit__(*sys.exc_info())
                raise

            finally:
                self._nr_instance._nr_wait_function_trace = None
                self._nr_instance._nr_transaction = None
                self._nr_instance = None

        else:
            # This should be the case where finish() is being called in
            # the original render() function.

            result = newrelic.api.function_trace.FunctionTraceWrapper(self._nr_next_object, name='Request/Finish', group='Python/Twisted')

        return result