def test_base_web_transaction(use_bytes): application = application_instance() request_headers = { 'Accept': 'text/plain', 'Content-Length': '0', 'Content-Type': 'text/plain', 'Host': 'localhost', 'Referer': 'http://example.com?q=1&boat=⛵', 'User-Agent': 'potato', 'X-Request-Start': str(time.time() - 0.2), 'newRelic': 'invalid', } if use_bytes: byte_headers = {} for name, value in request_headers.items(): name = name.encode('utf-8') try: value = value.encode('utf-8') except UnicodeDecodeError: assert six.PY2 byte_headers[name] = value request_headers = byte_headers transaction = WebTransaction( application, 'test_base_web_transaction', group='Test', scheme='http', host='localhost', port=8000, request_method='HEAD', request_path='/foobar', query_string='foo=bar&boo=baz', headers=request_headers.items(), ) if use_bytes: response_headers = ((b'Content-Length', b'0'), (b'Content-Type', b'text/plain')) else: response_headers = (('Content-Length', '0'), ('Content-Type', 'text/plain')) with transaction: transaction.process_response(200, response_headers)
def _nr_request_handler_init(wrapped, instance, args, kwargs): if current_transaction() is not None: _logger.error( 'Attempting to make a request (new transaction) when a ' 'transaction is already running. Please report this issue to ' 'New Relic support.\n%s', ''.join(traceback.format_stack()[:-1])) return wrapped(*args, **kwargs) def _bind_params(application, request, *args, **kwargs): return request request = _bind_params(*args, **kwargs) environ = _get_environ(request) if request.method not in instance.SUPPORTED_METHODS: # If the method isn't one of the supported ones, then we expect the # wrapped method to raise an exception for HTTPError(405). In this case # we name the transaction after the wrapped method. name = callable_name(instance) else: # Otherwise we name the transaction after the handler function that # should end up being executed for the request. method = getattr(instance, request.method.lower()) name = callable_name(method) environ = _get_environ(request) app = application_instance() txn = WebTransaction(app, environ) txn.__enter__() if txn.enabled: txn.drop_transaction() instance._nr_transaction = txn txn.set_transaction_name(name) # Record framework information for generation of framework metrics. txn.add_framework_info('Tornado/ASYNC', _VERSION) return wrapped(*args, **kwargs)
def initiate_request_monitoring(request): # Creates a new transaction and associates it with the request. # We always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the key # bits of information we need. environ = request_environment(application, request) # We now start recording the actual web transaction. Bail out though # if it turns out that recording of transactions is not enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return if tornado_settings.debug.transaction_monitoring: global _last_transaction_activation _last_transaction_activation = ''.join(traceback.format_stack()[:-1]) transaction.__enter__() request._nr_transaction = transaction request._nr_wait_function_trace = None request._nr_request_finished = False # We also need to add a reference to the request object in to the # transaction object so we can later access it in a deferred. We # need to use a weakref to avoid an object cycle which may prevent # cleanup of the transaction. transaction._nr_current_request = weakref.ref(request) # Record framework information for generation of framework metrics. import tornado if hasattr(tornado, 'version_info'): version = '.'.join(map(str, tornado.version_info)) else: version = None transaction.add_framework_info('Tornado/ASYNC', version) return transaction
def _wrap_headers_received(wrapped, instance, args, kwargs): start_line, headers = _bind_headers_received(*args, **kwargs) port = None try: # We only want to record port for ipv4 and ipv6 socket families. # Unix socket will just return a string instead of a tuple, so # skip this. sockname = request_conn.stream.socket.getsockname() if isinstance(sockname, tuple): port = sockname[1] except: pass path, sep, query = start_line.path.partition('?') transaction = WebTransaction( application=application_instance(), name=callable_name(instance), port=port, request_method=start_line.method, request_path=path, query_string=query, headers=headers, ) transaction.__enter__() if not transaction.enabled: return wrapped(*args, **kwargs) transaction.add_framework_info('Tornado', _VERSION) # Store the transaction on the HTTPMessageDelegate object since the # transaction lives for the lifetime of that object. request_conn._nr_transaction = transaction # Remove the headers_received circular reference vars(instance).pop('headers_received') return wrapped(*args, **kwargs)
def _nr_lambda_handler_wrapper_(wrapped, instance, args, kwargs): # Check to see if any transaction is present, even an inactive # one which has been marked to be ignored or which has been # stopped already. transaction = current_transaction(active_only=False) if transaction: return wrapped(*args, **kwargs) try: event, context = args[:2] except Exception: return wrapped(*args, **kwargs) target_application = 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'): target_application = application_instance(application) try: request_method = event['httpMethod'] request_path = event['path'] headers = event['headers'] query_params = event.get('multiValueQueryStringParameters') background_task = False except Exception: request_method = None request_path = None headers = None query_params = None background_task = True transaction_name = name or getattr(context, 'function_name', None) transaction = WebTransaction(target_application, transaction_name, group=group, request_method=request_method, request_path=request_path, headers=headers) transaction.background_task = background_task request_id = getattr(context, 'aws_request_id', None) aws_arn = getattr(context, 'invoked_function_arn', None) event_source = extract_event_source_arn(event) if request_id: transaction._add_agent_attribute('aws.requestId', request_id) if aws_arn: transaction._add_agent_attribute('aws.lambda.arn', aws_arn) if event_source: transaction._add_agent_attribute('aws.lambda.eventSource.arn', event_source) # COLD_START_RECORDED is initialized to "False" when the container # first starts up, and will remain that way until the below lines # of code are encountered during the first transaction after the cold # start. We record this occurence on the transaction so that an # attribute is created, and then set COLD_START_RECORDED to False so # that the attribute is not created again during future invocations of # this container. global COLD_START_RECORDED if COLD_START_RECORDED is False: transaction._add_agent_attribute('aws.lambda.coldStart', True) COLD_START_RECORDED = True settings = global_settings() if query_params and not settings.high_security: try: transaction._request_params.update(query_params) except: pass if not settings.aws_lambda_metadata and aws_arn: settings.aws_lambda_metadata['arn'] = aws_arn with transaction: result = wrapped(*args, **kwargs) if not background_task: try: status_code = result.get('statusCode') response_headers = result.get('headers') try: response_headers = response_headers.items() except Exception: response_headers = None transaction.process_response(status_code, response_headers) except Exception: pass return result
def send(self, value): if not self._nr_transaction: # create and start the transaction app = application_instance() txn = WebTransaction(app, self._nr_environ) import sanic txn.add_framework_info( name='Sanic', version=sanic.__version__) self._nr_transaction = txn if txn.enabled: txn.__enter__() txn.drop_transaction() txn = self._nr_transaction # transaction may not be active if not txn.enabled: return self.__wrapped__.send(value) txn.save_transaction() try: r = self.__wrapped__.send(value) txn.drop_transaction() return r except (GeneratorExit, StopIteration): self._nr_transaction.__exit__(None, None, None) self._nr_request = None raise except: self._nr_transaction.__exit__(*sys.exc_info()) self._nr_request = None raise
def send(self, value): if not self._nr_transaction: # create and start the transaction app = application_instance() txn = WebTransaction(app, self._nr_environ) import aiohttp txn.add_framework_info(name='aiohttp', version=aiohttp.__version__) self._nr_transaction = txn if txn.enabled: txn.__enter__() txn.drop_transaction() txn = self._nr_transaction # transaction may not be active if not txn.enabled: return self.__wrapped__.send(value) import aiohttp.web as _web txn.save_transaction() try: r = self.__wrapped__.send(value) txn.drop_transaction() return r except (GeneratorExit, StopIteration) as e: try: response = e.value _nr_process_response(response, txn) except: pass self._nr_transaction.__exit__(None, None, None) self._nr_request = None raise except _web.HTTPException as e: exc_info = sys.exc_info() try: _nr_process_response(e, txn) except: pass if should_ignore(*exc_info): self._nr_transaction.__exit__(None, None, None) else: self._nr_transaction.__exit__(*exc_info) self._nr_request = None raise except: exc_info = sys.exc_info() try: nr_headers = txn.process_response('500', ()) self._nr_request._nr_headers = dict(nr_headers) except: pass self._nr_transaction.__exit__(*exc_info) self._nr_request = None raise
def on_headers_wrapper(wrapped, instance, args, kwargs): assert instance is not None connection = instance # 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 but check anyway. transaction = current_transaction() if transaction: return wrapped(*args, **kwargs) # Execute the wrapped function as we are only going to do # something after it has been called. The function doesn't # return anything. 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 if connection._request_finished: return # Check to see if have already associated a transaction with # the request, because if we have, even if not finished, then # do not need to do anything. request = connection._request if request is None: return if hasattr(request, '_nr_transaction'): return # Always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the # key bits of information we need. environ = request_environment(application, request) # Now start recording the actual web transaction. Bail out # though if turns out that recording transactions is not # enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return transaction.__enter__() request._nr_transaction = transaction request._nr_wait_function_trace = None request._nr_request_finished = False # Add a callback variable to the connection object so we can # be notified when the connection is closed before all content # has been read. def _close(): transaction.save_transaction() try: if request._nr_wait_function_trace: request._nr_wait_function_trace.__exit__(None, None, None) finally: request._nr_wait_function_trace = None transaction.__exit__(None, None, None) request._nr_transaction = None connection.stream._nr_close_callback = _close # Name transaction initially after the wrapped function so # that if connection dropped before request content read, # then don't get metric grouping issues with it being named # after the URL. name = callable_name(wrapped) transaction.set_transaction_name(name) # We need to add a reference to the request object in to the # transaction object 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(request) try: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Input', group='Python/Tornado') request._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. connection.stream._nr_close_callback = None _logger.exception('Unexpected exception raised by Tornado ' 'HTTPConnection._on_headers().') transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise
def call_wrapper(wrapped, instance, args, kwargs): # We have to deal with a special case here because when using # tornado.wsgi.WSGIApplication() to host the async API within # a WSGI application, Tornado will call the wrapped method via # the class method rather than via an instance. This means the # instance will be None and the self argument will actually # be the first argument. The args are still left intact for # when we call the wrapped function. def _request_unbound(instance, request, *args, **kwargs): return instance, request def _request_bound(request, *args, **kwargs): return request if instance is None: instance, request = _request_unbound(*args, **kwargs) else: request = _request_bound(*args, **kwargs) # If no transaction associated with request already, need to # create a new one. The exception is when the the ASYNC API is # being executed within a WSGI application, in which case a # transaction will already be active. For that we execute # straight away. if instance._wsgi: transaction = current_transaction() with FunctionTrace(transaction, name='Request/Process', group='Python/Tornado'): return wrapped(*args, **kwargs) elif not hasattr(request, '_nr_transaction'): # Always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the # key bits of information we need. environ = request_environment(application, request) # Now start recording the actual web transaction. Bail out # though if turns out that recording transactions is not # enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return wrapped(*args, **kwargs) transaction.__enter__() request._nr_transaction = transaction request._nr_wait_function_trace = None request._nr_request_finished = False # We need to add a reference to the request object in to the # transaction object 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(request) else: # If there was a transaction associated with the request, # only continue if a transaction is active though. transaction = current_transaction() if not transaction: return wrapped(*args, **kwargs) try: # Call the original method in a trace object to give better # context in transaction traces. with FunctionTrace(transaction, name='Request/Process', group='Python/Tornado'): handler = wrapped(*args, **kwargs) # In the case of an immediate result or an exception # occuring, then finish() will have been called on the # request already. 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 it has been flagged that # it is finished, which Tornado does by setting the request # object in the connection to None, 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 handler._finished: if not request.connection.stream.writing(): transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Output', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Callback/Wait', group='Python/Tornado') request._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. _logger.exception('Unexpected exception raised by Tornado ' 'Application.__call__().') transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise return handler
def wsgi_container_call_wrapper(wrapped, instance, args, kwargs): def _args(request, *args, **kwargs): return request request = _args(*args, **kwargs) transaction = getattr(request, '_nr_transaction', None) name = callable_name(instance.wsgi_application) if not transaction: # Always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the # key bits of information we need. environ = request_environment(application, request) # Now start recording the actual web transaction. Bail out # though if turns out that recording transactions is not # enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return wrapped(*args, **kwargs) transaction.__enter__() request._nr_transaction = transaction request._nr_wait_function_trace = None request._nr_request_finished = False # We need to add a reference to the request object in to the # transaction object 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(request) try: # Call the original method in a trace object to give better # context in transaction traces. # XXX This is a temporary fiddle to preserve old default # URL naming convention until will move away from that # as a default. if transaction._request_uri is not None: transaction.set_transaction_name( transaction._request_uri, 'Uri', priority=1) with FunctionTrace(transaction, name='WSGI/Application', group='Python/Tornado'): with FunctionTrace(transaction, name=name): wrapped(*args, **kwargs) if not request.connection.stream.writing(): transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Output', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() except: # Catch all # If an error occurs assume that transaction should be # exited. transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise else: try: # XXX This is a temporary fiddle to preserve old default # URL naming convention until will move away from that # as a default. if transaction._request_uri is not None: transaction.set_transaction_name( transaction._request_uri, 'Uri', priority=1) with FunctionTrace(transaction, name='WSGI/Application', group='Python/Tornado'): with FunctionTrace(transaction, name=name): wrapped(*args, **kwargs) if not request.connection.stream.writing(): transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Output', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() except: # Catch all # If an error occurs assume that transaction should be # exited. transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise
def wsgi_container_call_wrapper(wrapped, instance, args, kwargs): def _args(request, *args, **kwargs): return request request = _args(*args, **kwargs) transaction = getattr(request, '_nr_transaction', None) name = callable_name(instance.wsgi_application) if not transaction: # Always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the # key bits of information we need. environ = request_environment(application, request) # Now start recording the actual web transaction. Bail out # though if turns out that recording transactions is not # enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return wrapped(*args, **kwargs) transaction.__enter__() request._nr_transaction = transaction request._nr_wait_function_trace = None request._nr_request_finished = False # We need to add a reference to the request object in to the # transaction object 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(request) try: # Call the original method in a trace object to give better # context in transaction traces. # XXX This is a temporary fiddle to preserve old default # URL naming convention until will move away from that # as a default. if transaction._request_uri is not None: transaction.set_transaction_name(transaction._request_uri, 'Uri', priority=1) with FunctionTrace(transaction, name='WSGI/Application', group='Python/Tornado'): with FunctionTrace(transaction, name=name): wrapped(*args, **kwargs) if not request.connection.stream.writing(): transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Output', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() except: # Catch all # If an error occurs assume that transaction should be # exited. transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise else: try: # XXX This is a temporary fiddle to preserve old default # URL naming convention until will move away from that # as a default. if transaction._request_uri is not None: transaction.set_transaction_name(transaction._request_uri, 'Uri', priority=1) with FunctionTrace(transaction, name='WSGI/Application', group='Python/Tornado'): with FunctionTrace(transaction, name=name): wrapped(*args, **kwargs) if not request.connection.stream.writing(): transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Request/Output', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() except: # Catch all # If an error occurs assume that transaction should be # exited. transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise
def start_wrapper(wrapped, instance, args, kwargs): assert instance is not None request = args[0] # 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 but check anyway. transaction = current_transaction() if transaction: return wrapped(*args, **kwargs) # Always use the default application specified in the agent # configuration. 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'] = request.uri # Now start recording the actual web transaction. Bail out though # if turns out that recording transactions is not enabled. transaction = WebTransaction(application, environ) if not transaction.enabled: return wrapped(*args, **kwargs) transaction.__enter__() request._nr_transaction = transaction request._nr_is_deferred_callback = False request._nr_wait_function_trace = None # We need to add a reference to the request object in to the # transaction object 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(request) try: # Call the original method in a trace object to give better # context in transaction traces. with FunctionTrace(transaction, name='Request/Process', group='Python/Tornado'): result = wrapped(*args, **kwargs) # In the case of an immediate result or an exception # occuring, then finish() will have been called on the # request already. 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 it has been flagged that # it is finished, which Tornado does by setting the request # object in the connection to None, 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 request.connection._request is None: transaction.__exit__(None, None, None) request._nr_transaction = None else: request._nr_wait_function_trace = FunctionTrace( transaction, name='Callback/Wait', group='Python/Tornado') request._nr_wait_function_trace.__enter__() transaction.drop_transaction() except: # 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. _logger.exception('Unexpected exception raised by Tornado ' 'Application.__call__().') transaction.__exit__(*sys.exc_info()) request._nr_transaction = None raise return result
def initiate_request_monitoring(request): # Creates a new transaction and associates it with the request. # We always use the default application specified in the agent # configuration. application = application_instance() # We need to fake up a WSGI like environ dictionary with the key # bits of information we need. environ = request_environment(request) # We now start recording the actual web transaction. purge_current_transaction() transaction = WebTransaction(application, environ) if not transaction.enabled: return None transaction.__enter__() # Immediately purge the transaction from the cache, so we don't associate # Tornado internals inappropriately with this transaction. purge_current_transaction() # We also need to add a reference to the request object in to the # transaction object so we can later access it in a deferred. We # need to use a weakref to avoid an object cycle which may prevent # cleanup of the transaction. transaction._nr_current_request = weakref.ref(request) # Records state of transaction transaction._is_finalized = False transaction._ref_count = 0 # For requests that complete normally, a transaction can only be closed # after the `finish()` method is called on both the `_ServerRequestAdapter` # and the `RequestHandler`. transaction._request_handler_finalize = False transaction._server_adapter_finalize = False # Record framework information for generation of framework metrics. import tornado if hasattr(tornado, 'version_info'): version = '.'.join(map(str, tornado.version_info)) else: version = None transaction.add_framework_info('Tornado/ASYNC', version) return transaction
def _nr_lambda_handler_wrapper_(wrapped, instance, args, kwargs): # Check to see if any transaction is present, even an inactive # one which has been marked to be ignored or which has been # stopped already. transaction = current_transaction(active_only=False) if transaction: return wrapped(*args, **kwargs) try: event, context = args[:2] except Exception: return wrapped(*args, **kwargs) target_application = 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'): target_application = application_instance(application) # Extract the environment from the event environ, background_task, query_params = process_event(event) # Now start recording the actual web transaction. transaction = WebTransaction(target_application, environ) transaction.background_task = background_task transaction._aws_request_id = getattr(context, 'aws_request_id', None) transaction._aws_arn = getattr(context, 'invoked_function_arn', None) transaction._aws_event_source_arn = extract_event_source_arn(event) # COLD_START_RECORDED is initialized to "False" when the container # first starts up, and will remain that way until the below lines # of code are encountered during the first transaction after the cold # start. We record this occurence on the transaction so that an # attribute is created, and then set COLD_START_RECORDED to False so # that the attribute is not created again during future invocations of # this container. global COLD_START_RECORDED if COLD_START_RECORDED is False: transaction._is_cold_start = True COLD_START_RECORDED = True settings = global_settings() if query_params and not settings.high_security: try: transaction._request_params.update(query_params) except: pass if not settings.aws_arn and transaction._aws_arn: settings.aws_arn = transaction._aws_arn # Override the initial transaction name. transaction_name = name or getattr(context, 'function_name', None) if transaction_name: transaction.set_transaction_name(transaction_name, group, priority=1) with transaction: result = wrapped(*args, **kwargs) try: if not background_task: status_code = result.get('statusCode', None) status_code = str(status_code) response_headers = result.get('headers', None) try: response_headers = list(response_headers.items()) except Exception: response_headers = None transaction.process_response(status_code, response_headers) except Exception: pass return result