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)
示例#2
0
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)
示例#3
0
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
示例#4
0
    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)
示例#5
0
    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
示例#6
0
    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
示例#8
0
    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
示例#9
0
    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
示例#10
0
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 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
示例#14
0
    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
示例#15
0
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