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)
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)
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
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
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