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(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(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(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: 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: 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. try: with newrelic.api.function_trace.FunctionTrace(transaction, name='Request/Finish', group='Python/Twisted'): result = self._nr_next_object() except: raise return result