def record_exception(exc_info): # Record the details of any exception ignoring status codes which # have been configured to be ignored. import tornado.web exc = exc_info[0] value = exc_info[1] # Not an error so we just return. if exc is tornado.web.Finish: return if exc is tornado.web.HTTPError: if ignore_status_code(value.status_code): return transaction = retrieve_current_transaction() if transaction: transaction.record_exception(*exc_info) else: # If we are not in a transaction we record the exception to the default # application specified in the agent configuration. application = application_instance() if application and application.enabled: application.record_exception(*exc_info)
def monitored(fn): # pragma: no cover """Provides monitoring capabilities for task methods.""" # TODO(jvrbanac): Figure out how we should test third-party monitoring # Support NewRelic Monitoring if newrelic_loaded: # Create a NewRelic app instance app = application.application_instance() def newrelic_wrapper(*args, **kwargs): # Resolve real name since decorators are wrapper the method if len(args) > 0 and hasattr(args[0], fn.__name__): cls = type(args[0]) task_name = '{0}:{1}.{2}'.format(cls.__module__, cls.__name__, fn.__name__) else: task_name = newrelic.agent.callable_name(fn) # Execute task under a monitored context with newrelic.agent.BackgroundTask(app, task_name): fn(*args, **kwargs) return newrelic_wrapper return fn
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 get_service_linking_metadata(application=None, settings=None): metadata = { "entity.type": "SERVICE", } trace = current_trace() if settings is None and trace: txn = trace.transaction if txn: settings = txn.settings if not settings: if application is None: from newrelic.api.application import application_instance application = application_instance(activate=False) if application is not None: settings = application.settings if settings: metadata["entity.name"] = settings.app_name entity_guid = settings.entity_guid if entity_guid: metadata["entity.guid"] = entity_guid metadata["hostname"] = platform.uname()[1] return metadata
def test_sentinel_exited_complete_root_exception(): """ This test forces a transaction to exit while it still has an active trace this causes an exception to be raised in TraceCache complete_root(). It verifies that the sentinel.exited property is set to true if an exception is raised in complete_root() """ expected_error = "not the current trace" try: txn = None sentinel = None txn = BackgroundTask(application_instance(), "Parent") txn.__enter__() sentinel = txn.root_span trace = FunctionTrace("trace") trace.__enter__() txn.__exit__(None, None, None) assert False, "Did not raise exception" except RuntimeError as e: assert str(e) == expected_error finally: assert sentinel.exited # Make sure to exit properly so cleanup is performed trace.__exit__(None, None, None) txn.__exit__(None, None, None)
def create_transaction(transaction): if transaction: # If there is any active transaction we will return without # applying a new WSGI application wrapper context. In the # case of a transaction which is being ignored or which has # been stopped, we do that without doing anything further. if transaction.ignore_transaction or transaction.stopped: return None if not transaction.background_task: transaction.background_task = True transaction.set_transaction_name( *MessageTransaction.get_transaction_name( _library, _destination_type, _destination_name)) return None if type(application) != Application: _application = application_instance(application) else: _application = application return MessageTransaction(library=_library, destination_type=_destination_type, destination_name=_destination_name, application=_application, routing_key=_routing_key, exchange_type=_exchange_type, headers=_headers, queue_name=_queue_name, reply_to=_reply_to, correlation_id=_correlation_id)
def _possibly_create_traces(yielded): # This generator can be called either outside of a transaction, or # within the context of an existing transaction. There are 3 # possibilities we need to handle: (Note that this is similar to # our Celery instrumentation) # # 1. In an inactive transaction # # If the end_of_transaction() or ignore_transaction() API # calls have been invoked, this generator may be called in the # context of an inactive transaction. In this case, don't wrap # the generator in any way. Just run the original generator. # # 2. In an active transaction # # Do nothing. # # 3. Outside of a transaction # # Since it's not running inside of an existing transaction, we # want to create a new background transaction for it but only # when we've subscribed. transaction = current_transaction(active_only=False) method, properties, _ = yielded if transaction: # 1. In an inactive transaction # 2. In an active transaction return else: # 3. Outside of a transaction exchange = method.exchange or 'Default' routing_key = getattr(method, 'routing_key', None) headers = None reply_to = None correlation_id = None if properties is not None: headers = getattr(properties, 'headers', None) reply_to = getattr(properties, 'reply_to', None) correlation_id = getattr(properties, 'correlation_id', None) # Create a messagebroker task for each iteration through the # generator. This is important because it is foreseeable that # the generator process lasts a long time and consumes many # many messages. bt = MessageTransaction(application=application_instance(), library='RabbitMQ', destination_type='Exchange', destination_name=exchange, routing_key=routing_key, headers=headers, reply_to=reply_to, correlation_id=correlation_id) bt.__enter__() return bt
def monitored(fn): # pragma: no cover """Provides monitoring capabilities for task methods.""" # TODO(jvrbanac): Figure out how we should test third-party monitoring # Support NewRelic Monitoring if newrelic_loaded: # Create a NewRelic app instance app = application.application_instance() def newrelic_wrapper(*args, **kwargs): # Resolve real name since decorators are wrapper the method if len(args) > 0 and hasattr(args[0], fn.__name__): cls = type(args[0]) task_name = '{0}:{1}.{2}'.format( cls.__module__, cls.__name__, fn.__name__ ) else: task_name = newrelic.agent.callable_name(fn) # Execute task under a monitored context with newrelic.agent.BackgroundTask(app, task_name): fn(*args, **kwargs) return newrelic_wrapper return fn
def _nr_log_forwarder(message_instance): transaction = current_transaction() record = message_instance.record message = record.get("_nr_original_message", record["message"]) if transaction: settings = transaction.settings else: settings = global_settings() # Return early if application logging not enabled if settings and settings.application_logging and settings.application_logging.enabled: level = record["level"] level_name = "UNKNOWN" if not level else (level.name or "UNKNOWN") if settings.application_logging.metrics and settings.application_logging.metrics.enabled: if transaction: transaction.record_custom_metric("Logging/lines", {"count": 1}) transaction.record_custom_metric( "Logging/lines/%s" % level_name, {"count": 1}) else: application = application_instance(activate=False) if application and application.enabled: application.record_custom_metric("Logging/lines", {"count": 1}) application.record_custom_metric( "Logging/lines/%s" % level_name, {"count": 1}) if settings.application_logging.forwarding and settings.application_logging.forwarding.enabled: try: record_log_event(message, level_name, int(record["time"].timestamp())) except Exception: pass
def test_nested_context_managers(): app = application_instance() outer = BackgroundTask(app, 'outer') inner = BackgroundTask(app, 'inner') with outer: with inner: assert not inner.enabled
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 exercise(override_expected=None, override_ignore=None, status_code=None): try: raise RuntimeError(_error_message) except RuntimeError: if current_transaction() is not None: # Record exception inside transaction notice_error( expected=override_expected, ignore=override_ignore, status_code=status_code, ) else: # Record exception outside context of transaction application_instance().notice_error( expected=override_expected, ignore=override_ignore, status_code=status_code, )
def callback_wrapper(wrapped, instance, args, kwargs): name = callable_name(wrapped) transaction = current_transaction(active_only=False) if transaction and (transaction.ignore_transaction or transaction.stopped): return wrapped(*args, **kwargs) elif transaction: with FunctionTrace(name=name): return wrapped(*args, **kwargs) else: if hasattr(channel, '_nr_disable_txn_tracing'): return wrapped(*args, **kwargs) # Keyword arguments are unknown since this is a user # defined callback exchange = 'Unknown' routing_key = None headers = None reply_to = None correlation_id = None unknown_kwargs = False if not kwargs: method, properties = args[1:3] exchange = method.exchange or 'Default' routing_key = getattr(method, 'routing_key', None) if properties is not None: headers = getattr(properties, 'headers', None) reply_to = getattr(properties, 'reply_to', None) correlation_id = getattr( properties, 'correlation_id', None) else: unknown_kwargs = True with MessageTransaction( application=application_instance(), library='RabbitMQ', destination_type='Exchange', destination_name=exchange, routing_key=routing_key, headers=headers, queue_name=queue, reply_to=reply_to, correlation_id=correlation_id) as mt: # Improve transaction naming _new_txn_name = 'RabbitMQ/Exchange/%s/%s' % (exchange, name) mt.set_transaction_name(_new_txn_name, group='Message') # Record that something went horribly wrong if unknown_kwargs: m = mt._transaction_metrics.get(KWARGS_ERROR, 0) mt._transaction_metrics[KWARGS_ERROR] = m + 1 return wrapped(*args, **kwargs)
def _make_test_transaction(): application = application_instance() if not web_transaction: return BackgroundTask(application, transaction_name) environ = {'REQUEST_URI': '/trace_ends_after_txn'} tn = WSGIWebTransaction(application, environ) tn.set_transaction_name(transaction_name) return tn
def error_matches_rules( rules_prefix, exc_info, status_code=None, ): # Delay imports to prevent lockups from newrelic.api.application import application_instance from newrelic.core.trace_cache import trace_cache trace = trace_cache().current_trace() settings = trace and trace.settings if not settings: # Retrieve application settings application = application_instance() settings = application and application.settings # Default to global settings settings = settings or global_settings() if not settings: return False # Retrieve settings based on prefix classes_rules = getattr(settings.error_collector, "%s_classes" % rules_prefix, set()) status_codes_rules = getattr(settings.error_collector, "%s_status_codes" % rules_prefix, set()) module, name, fullnames, message = parse_exc_info(exc_info) fullname = fullnames[0] # Check class names for fullname in fullnames: if fullname in classes_rules: return True # Check status_code # For callables, call on exc_info to retrieve status_code. # It's possible to return None, in which case no code is evaluated. if callable(status_code): status_code = status_code(*exc_info) # Match status_code if it exists if status_code is not None: try: # Coerce into integer status_code = int(status_code) except: _logger.error("Failed to coerce status code into integer. " "status_code: %s" % str(status_code)) else: if status_code in status_codes_rules: return True return False
def _make_test_transaction(): application = application_instance() request = TestAsgiRequest() if not web_transaction: return BackgroundTask(application, transaction_name) tn = ASGIWebTransaction(application, request.scope, request.send, request.receive) tn.set_transaction_name(transaction_name) return tn
def serverless_application(request): settings = global_settings() orig = settings.serverless_mode.enabled settings.serverless_mode.enabled = True application_name = 'Python Agent Test (test_serverless_mode:%s)' % ( request.node.name) application = application_instance(application_name) application.activate() yield application settings.serverless_mode.enabled = orig
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 test_dead_transaction_ends(circular): if circular and six.PY2: pytest.skip("Circular references in py2 result in a memory leak. " "There is no way to remove transactions from the weakref " "cache in this case.") transaction = BackgroundTask(application_instance(), "test_dead_transaction_ends") if circular: transaction._self = transaction transaction.__enter__() del transaction gc.collect()
def exercise(expected=None, ignore=None, status_code=None, application=None): try: raise RuntimeError(_error_message) except RuntimeError: if current_transaction() is None: # Record exception inside transaction application = application or application_instance() notice_error( expected=expected, ignore=ignore, status_code=status_code, application=application, )
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 wrap_callHandlers(wrapped, instance, args, kwargs): transaction = current_transaction() record = bind_callHandlers(*args, **kwargs) logger_name = getattr(instance, "name", None) if logger_name and logger_name.split(".")[0] == "newrelic": return wrapped(*args, **kwargs) if transaction: settings = transaction.settings else: settings = global_settings() # Return early if application logging not enabled if settings and settings.application_logging and settings.application_logging.enabled: level_name = str(getattr(record, "levelname", "UNKNOWN")) if settings.application_logging.metrics and settings.application_logging.metrics.enabled: if transaction: transaction.record_custom_metric("Logging/lines", {"count": 1}) transaction.record_custom_metric( "Logging/lines/%s" % level_name, {"count": 1}) else: application = application_instance(activate=False) if application and application.enabled: application.record_custom_metric("Logging/lines", {"count": 1}) application.record_custom_metric( "Logging/lines/%s" % level_name, {"count": 1}) if settings.application_logging.forwarding and settings.application_logging.forwarding.enabled: try: message = record.getMessage() record_log_event(message, level_name, int(record.created * 1000)) except Exception: pass if settings.application_logging.local_decorating and settings.application_logging.local_decorating.enabled: record._nr_original_message = record.getMessage record.getMessage = wrap_getMessage(record.getMessage) return wrapped(*args, **kwargs)
def test_sentinel_exited_complete_root_exception(): """ This test forces a transaction to exit while it still has an active trace this causes an exception to be raised in TraceCache complete_root(). It verifies that the sentinel.exited property is set to true if an exception is raised in complete_root(), and that the exception is caught. """ txn = None sentinel = None txn = BackgroundTask(application_instance(), "Parent") txn.__enter__() sentinel = txn.root_span trace = FunctionTrace("trace") trace.__enter__() txn.__exit__(None, None, None) assert sentinel.exited # Make sure to exit properly so cleanup is performed trace.__exit__(None, None, None) txn.__exit__(None, None, None)
def wait_for_transaction_completion(fn): CALLED = threading.Event() application = application_instance() record_transaction = application.record_transaction def record_transaction_wrapper(*args, **kwargs): record_transaction(*args, **kwargs) CALLED.set() @functools.wraps(fn) def _waiter(*args, **kwargs): application.record_transaction = record_transaction_wrapper try: result = fn(*args, **kwargs) CALLED.wait(timeout=1) return result finally: application.record_transaction = record_transaction return _waiter
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 _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 test_inbound_distributed_trace(mock_grpc_server, method_name, streaming_request, stub): request = create_request(streaming_request) transaction = Transaction(application_instance()) dt_headers = ExternalTrace.generate_request_headers(transaction) @validate_transaction_metrics( 'sample_application:SampleApplicationServicer.' + method_name, rollup_metrics=(('Supportability/TraceContext/Accept/Success', 1), ), ) @wait_for_transaction_completion def _test(): method = getattr(stub, method_name) response = method(request, metadata=dt_headers) try: list(response) except Exception: pass _test()
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 wrapper(wrapped, instance, args, kwargs): transaction = current_transaction() if callable(name): if instance and inspect.ismethod(wrapped): _name = name(instance, *args, **kwargs) else: _name = name(*args, **kwargs) elif name is None: _name = callable_name(wrapped) else: _name = name if callable(group): if instance and inspect.ismethod(wrapped): _group = group(instance, *args, **kwargs) else: _group = group(*args, **kwargs) else: _group = group # Check to see if we are being called within the context # of a web transaction. If we are, then we will just # flag the current web transaction as a background task # if not already marked as such and name the web # transaction as well. In any case, if nested in another # transaction be it a web transaction or background # task, then we don't do anything else and just called # the wrapped function. if transaction: if type(transaction) == WebTransaction: if not transaction.background_task: transaction.background_task = True transaction.name_transaction(_name, _group) return wrapped(*args, **kwargs) # Otherwise treat it as top level transaction. if type(application) != Application: _application = application_instance(application) else: _application = application try: success = True manager = BackgroundTask(_application, _name, _group) manager.__enter__() try: return wrapped(*args, **kwargs) except: # Catch all success = False if not manager.__exit__(*sys.exc_info()): raise finally: if success: manager.__exit__(None, None, None)
def _application(): if hasattr(application, 'activate'): return application return application_instance(application)
def process_initializer(*args, **kwargs): application_instance().activate() return _process_initializer(*args, **kwargs)
def force_application_activation(*args, **kwargs): application_instance().activate()
def _application(): if hasattr(application, 'activate'): return application return application_instance(application)
def test_inbound_distributed_trace(app): transaction = Transaction(application_instance()) dt_headers = ExternalTrace.generate_request_headers(transaction) response = app.fetch('get', '/', headers=dict(dt_headers)) assert response.status == 200
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 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 _nr_wsgi_application_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: # If there is any active transaction we will return without # applying a new WSGI application wrapper context. In the # case of a transaction which is being ignored or which has # been stopped, we do that without doing anything further. if transaction.ignore_transaction or transaction.stopped: return wrapped(*args, **kwargs) # For any other transaction, we record the details of any # framework against the transaction for later reporting as # supportability metrics. if framework: transaction.add_framework_info( name=framework[0], version=framework[1]) # Also 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 name is None and settings: if framework is not None: naming_scheme = settings.transaction_name.naming_scheme if naming_scheme in (None, 'framework'): transaction.set_transaction_name( callable_name(wrapped), priority=1) elif name: transaction.set_transaction_name(name, group, priority=1) return wrapped(*args, **kwargs) # 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. def _args(environ, start_response, *args, **kwargs): return environ, start_response environ, start_response = _args(*args, **kwargs) target_application = application if 'newrelic.app_name' in environ: app_name = environ['newrelic.app_name'] if ';' in app_name: app_names = [n.strip() for n in app_name.split(';')] app_name = app_names[0] target_application = application_instance(app_name) for altname in app_names[1:]: target_application.link_to_application(altname) else: target_application = application_instance(app_name) else: # 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) # Now start recording the actual web transaction. transaction = WSGIWebTransaction(target_application, environ) transaction.__enter__() # Record details of framework against the transaction for later # reporting as supportability metrics. if framework: transaction.add_framework_info( name=framework[0], version=framework[1]) # 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 name is None and settings: naming_scheme = settings.transaction_name.naming_scheme if framework is not None: if naming_scheme in (None, 'framework'): transaction.set_transaction_name( callable_name(wrapped), priority=1) elif naming_scheme in ('component', 'framework'): transaction.set_transaction_name( callable_name(wrapped), priority=1) elif name: transaction.set_transaction_name(name, 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']) with FunctionTrace( name='Application', group='Python/WSGI'): with FunctionTrace(name=callable_name(wrapped)): if (settings and settings.browser_monitoring.enabled and not transaction.autorum_disabled): result = _WSGIApplicationMiddleware(wrapped, environ, _start_response, transaction) else: result = wrapped(environ, _start_response) except: # Catch all transaction.__exit__(*sys.exc_info()) raise return _WSGIApplicationIterable(transaction, result)
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