def instrument_tornado_iostream(module):

    def maybe_run_close_callback_wrapper(wrapped, instance, args, kwargs):
        stream = instance

        if (not stream.closed() or stream._pending_callbacks != 0):
            return wrapped(*args, **kwargs)

        callback = getattr(stream, '_nr_close_callback', None)

        stream._nr_close_callback = None

        if callback:
            callback()

        return wrapped(*args, **kwargs)

    if hasattr(module, 'BaseIOStream'):
        module.BaseIOStream._maybe_run_close_callback = ObjectWrapper(
                module.BaseIOStream._maybe_run_close_callback, None,
                maybe_run_close_callback_wrapper)
    elif hasattr(module.IOStream, '_maybe_run_close_callback'):
        module.IOStream._maybe_run_close_callback = ObjectWrapper(
                module.IOStream._maybe_run_close_callback, None,
                maybe_run_close_callback_wrapper)
def TransactionNameWrapper(wrapped, name=None, group=None, priority=None):
    def dynamic_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        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

        transaction.name_transaction(_name, _group, priority)

        return wrapped(*args, **kwargs)

    def literal_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        _name = name or callable_name(wrapped)

        transaction.name_transaction(_name, group, priority)

        return wrapped(*args, **kwargs)

    if callable(name) or callable(group):
        return ObjectWrapper(wrapped, None, dynamic_wrapper)

    return ObjectWrapper(wrapped, None, literal_wrapper)
Esempio n. 3
0
def instrument_tastypie_resources(module):
    _wrap_view = module.Resource.wrap_view
    module.Resource.wrap_view = ObjectWrapper(_wrap_view, None,
                                              outer_fn_wrapper)

    wrap_function_wrapper(module, 'Resource._handle_500',
                          _nr_wrap_handle_exception)
    def wrapper(wrapped):
        # The middleware if a class method would already be
        # bound at this point, so is safe to determine the name
        # when it is being wrapped rather than on each
        # invocation.

        name = callable_name(wrapped)

        def wrapper(wrapped, instance, args, kwargs):
            transaction = current_transaction()

            if transaction is None:
                return wrapped(*args, **kwargs)

            before = (transaction.name, transaction.group)

            with FunctionTrace(transaction, name=name):
                try:
                    return wrapped(*args, **kwargs)

                finally:
                    # We want to name the transaction after this
                    # middleware but only if the transaction wasn't
                    # named from within the middleware itself explicity.

                    after = (transaction.name, transaction.group)
                    if before == after:
                        transaction.name_transaction(name, priority=2)

        return ObjectWrapper(wrapped, None, wrapper)
def wrap_handle_uncaught_exception(middleware):

    # Wrapper to be applied to handler called when exceptions
    # propagate up to top level from middleware. Records the
    # time spent in the handler as separate function node. Names
    # the web transaction after the name of the handler if not
    # already named at higher priority and capture further
    # errors in the handler.

    name = callable_name(middleware)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        def _wrapped(request, resolver, exc_info):
            transaction.name_transaction(name, priority=1)
            transaction.record_exception(*exc_info)

            try:
                return wrapped(request, resolver, exc_info)

            except:  # Catch all
                transaction.record_exception(*sys.exc_info())
                raise

        with FunctionTrace(transaction, name=name):
            return _wrapped(*args, **kwargs)

    return ObjectWrapper(middleware, None, wrapper)
def wrap_view_dispatch(wrapped):

    # Wrapper to be applied to dispatcher for class based views.

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        def _args(request, *args, **kwargs):
            return request

        view = instance
        request = _args(*args, **kwargs)

        # We can't intercept the delegated view handler when it
        # is looked up by the dispatch() method so we need to
        # duplicate the lookup mechanism.

        if request.method.lower() in view.http_method_names:
            handler = getattr(view, request.method.lower(),
                              view.http_method_not_allowed)
        else:
            handler = view.http_method_not_allowed

        name = callable_name(handler)
        transaction.set_transaction_name(name)

        with FunctionTrace(transaction, name=name):
            return wrapped(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 7
0
def wrap_view_handler(wrapped, priority=3):

    # Ensure we don't wrap the view handler more than once. This
    # looks like it may occur in cases where the resolver is
    # called recursively. We flag that view handler was wrapped
    # using the '_nr_django_view_handler' attribute.

    if hasattr(wrapped, '_nr_django_view_handler'):
        return wrapped

    name = callable_name(wrapped)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        transaction.set_transaction_name(name, priority=priority)

        with FunctionTrace(transaction, name=name):
            try:
                return wrapped(*args, **kwargs)

            except:  # Catch all
                transaction.record_exception(ignore_errors=should_ignore)
                raise

    result = ObjectWrapper(wrapped, None, wrapper)
    result._nr_django_view_handler = True

    return result
    def init_wrapper(wrapped, instance, args, kwargs):
        if instance:
            # Bound method when RequestHandler instantiated
            # directly.

            handler = instance

        elif args:
            # When called from derived class constructor it is
            # not done so as a bound method. Instead the self
            # object will be passed as the first argument.

            handler = args[0]

        else:
            # Incorrect number of arguments. Pass it through so
            # it fails on call.

            return wrapped(*args, **kwargs)

        handler.on_connection_close = ObjectWrapper(
                handler.on_connection_close, None,
                on_connection_close_wrapper)

        return wrapped(*args, **kwargs)
def PreFunctionWrapper(wrapped, function):
    def wrapper(wrapped, instance, args, kwargs):
        if instance and inspect.ismethod(wrapped):
            function(instance, *args, **kwargs)
        else:
            function(*args, **kwargs)

        return wrapped(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
def InFunctionWrapper(wrapped, function):
    def wrapper(wrapped, instance, args, kwargs):
        if instance and inspect.ismethod(wrapped):
            (_args, _kwargs) = function(instance, *args, **kwargs)
            _args = _args[1:]
        else:
            (_args, _kwargs) = function(*args, **kwargs)

        return wrapped(*_args, **_kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 11
0
def ErrorTraceWrapper(wrapped, ignore_errors=None):
    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        with ErrorTrace(transaction, ignore_errors):
            return wrapped(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
def ExternalTraceWrapper(wrapped, library, url, method=None):
    def dynamic_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        if callable(url):
            if instance and inspect.ismethod(wrapped):
                _url = url(instance, *args, **kwargs)
            else:
                _url = url(*args, **kwargs)

        else:
            _url = url

        if callable(method):
            if instance and inspect.ismethod(wrapped):
                _method = method(instance, *args, **kwargs)
            else:
                _method = method(*args, **kwargs)

        else:
            _method = method

        with ExternalTrace(transaction, library, _url, _method):
            return wrapped(*args, **kwargs)

    def literal_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        with ExternalTrace(transaction, library, url, method):
            return wrapped(*args, **kwargs)

    if callable(url) or callable(method):
        return ObjectWrapper(wrapped, None, dynamic_wrapper)

    return ObjectWrapper(wrapped, None, literal_wrapper)
Esempio n. 13
0
def instrument_tornado_template(module):
    def template_generate_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        with FunctionTrace(transaction,
                           name=instance.name,
                           group='Template/Render'):
            return wrapped(*args, **kwargs)

    module.Template.generate = ObjectWrapper(module.Template.generate, None,
                                             template_generate_wrapper)

    def template_generate_wrapper(wrapped, instance, args, kwargs):
        result = wrapped(*args, **kwargs)
        if result is not None:
            return ('import newrelic.api.function_trace as _nr_fxn_trace\n'
                    'import newrelic.api.transaction as _nr_txn\n') + result

    module.Template._generate_python = ObjectWrapper(
        module.Template._generate_python, None, template_generate_wrapper)

    def block_generate_wrapper(wrapped, instance, args, kwargs):
        def execute(writer, *args, **kwargs):
            if not hasattr(instance, 'line'):
                return wrapped(writer, *args, **kwargs)
            writer.write_line(
                'with _nr_fxn_trace.FunctionTrace('
                '_nr_txn.current_transaction(), name=%r, '
                'group="Template/Block"):' % instance.name, instance.line)
            with writer.indent():
                writer.write_line("pass", instance.line)
                return wrapped(writer, *args, **kwargs)

        return execute(*args, **kwargs)

    module._NamedBlock.generate = ObjectWrapper(module._NamedBlock.generate,
                                                None, block_generate_wrapper)
def wrap_url_resolver(wrapped):

    # Wrap URL resolver. If resolver returns valid result then
    # wrap the view handler returned. The type of the result
    # changes across Django versions so need to check and adapt
    # as necessary. For a 404 then a user supplied 404 handler
    # or the default 404 handler should get later invoked and
    # transaction should be named after that.

    name = callable_name(wrapped)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        if hasattr(transaction, '_nr_django_url_resolver'):
            return wrapped(*args, **kwargs)

        # Tag the transaction so we know when we are in the top
        # level call to the URL resolver as don't want to show
        # the inner ones as would be one for each url pattern.

        transaction._nr_django_url_resolver = True

        def _wrapped(path):
            # XXX This can raise a Resolver404. If this is not dealt
            # with, is this the source of our unnamed 404 requests.

            with FunctionTrace(transaction, name=name, label=path):
                result = wrapped(path)

                if type(result) == type(()):
                    callback, callback_args, callback_kwargs = result
                    result = (wrap_view_handler(callback, priority=3),
                              callback_args, callback_kwargs)
                else:
                    result.func = wrap_view_handler(result.func, priority=3)

                return result

        try:
            return _wrapped(*args, **kwargs)

        finally:
            del transaction._nr_django_url_resolver

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 15
0
def wrap_template_block(wrapped):

    name = callable_name(wrapped)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        with FunctionTrace(transaction, name=instance.name,
                group='Template/Block'):
            return wrapped(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
def wrap_url_resolver_nnn(wrapped, priority=1):

    # Wrapper to be applied to the URL resolver for errors.

    name = callable_name(wrapped)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        with FunctionTrace(transaction, name=name):
            callback, param_dict = wrapped(*args, **kwargs)
            return (wrap_view_handler(callback, priority=priority), param_dict)

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 17
0
def wrap_url_reverse(wrapped):

    # Wrap the URL resolver reverse lookup. Where the view
    # handler is passed in we need to strip any instrumentation
    # wrapper to ensure that it doesn't interfere with the
    # lookup process. Technically this may now not be required
    # as we have improved the proxying in the object wrapper,
    # but do it just to avoid any potential for problems.

    def wrapper(wrapped, instance, args, kwargs):
        def execute(viewname, *args, **kwargs):
            if hasattr(viewname, '_nr_last_object'):
                viewname = viewname._nr_last_object
            return wrapped(viewname, *args, **kwargs)
        return execute(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
    def wrapper(wrapped):
        # The middleware if a class method would already be
        # bound at this point, so is safe to determine the name
        # when it is being wrapped rather than on each
        # invocation.

        name = callable_name(wrapped)

        def wrapper(wrapped, instance, args, kwargs):
            transaction = current_transaction()

            if transaction is None:
                return wrapped(*args, **kwargs)

            with FunctionTrace(transaction, name=name):
                return wrapped(*args, **kwargs)

        return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 19
0
def outer_fn_wrapper(outer_fn, instance, args, kwargs):
    view_name = args[0]

    meta = getattr(instance, "_meta", None)

    if meta is None:
        group = 'Python/TastyPie/Api'
        name = instance.api_name
        callback = getattr(instance, 'top_level', None)
    elif meta.api_name is not None:
        group = 'Python/TastyPie/Api'
        name = '%s/%s/%s' % (meta.api_name, meta.resource_name, view_name)
        callback = getattr(instance, view_name, None)
    else:
        group = 'Python/TastyPie/Resource'
        name = '%s/%s' % (meta.resource_name, view_name)
        callback = getattr(instance, view_name, None)

    # Give preference to naming web transaction and trace node after
    # target callback, but fall back to abstract path if for some reason
    # we don't get a valid target callback.

    if callback is not None:
        name = callable_name(callback)
        group = None

    def inner_fn_wrapper(inner_fn, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None or name is None:
            return inner_fn(*args, **kwargs)

        transaction.set_transaction_name(name, group, priority=4)

        with FunctionTrace(transaction, name, group):
            try:
                return inner_fn(*args, **kwargs)
            except:  # Catch all
                transaction.record_exception(*sys.exc_info())
                raise

    result = outer_fn(*args, **kwargs)

    return ObjectWrapper(result, None, inner_fn_wrapper)
def wrap_view_handler(wrapped, priority=3):

    # Ensure we don't wrap the view handler more than once. This
    # looks like it may occur in cases where the resolver is
    # called recursively. We flag that view handler was wrapped
    # using the '_nr_django_view_handler' attribute.

    if hasattr(wrapped, '_nr_django_view_handler'):
        return wrapped

    name = callable_name(wrapped)

    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        transaction.name_transaction(name, priority=priority)

        with FunctionTrace(transaction, name=name):
            try:
                return wrapped(*args, **kwargs)

            except:  # Catch all
                # Python 2.5 doesn't allow *args before keywords.
                # See http://bugs.python.org/issue3473.
                exc_info = sys.exc_info()
                transaction.record_exception(exc_info[0],
                                             exc_info[1],
                                             exc_info[2],
                                             ignore_errors=[
                                                 'django.http:Http404',
                                                 'django.http.response:Http404'
                                             ])
                raise

            finally:
                exc_info = None

    result = ObjectWrapper(wrapped, None, wrapper)
    result._nr_django_view_handler = True

    return result
Esempio n. 21
0
def CeleryTaskWrapper(wrapped, application=None, name=None):
    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

        # Helper for obtaining the appropriate application object. If
        # has an activate() method assume it is a valid application
        # object. Don't check by type so se can easily mock it for
        # testing if need be.

        def _application():
            if hasattr(application, 'activate'):
                return application
            return application_instance(application)

        # Check to see if we are being called within the context of an
        # existing transaction. If we are, then we will record the call
        # as a function trace node instead. This situation can occur
        # when a function wrapped with Celery task decorator is called
        # explicitly in the context of an existing transaction.

        if transaction:
            with FunctionTrace(transaction, callable_name(wrapped)):
                return wrapped(*args, **kwargs)

        # Otherwise treat it as top level background task.

        with BackgroundTask(_application(), _name, 'Celery'):
            return wrapped(*args, **kwargs)

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 22
0
    def wrapper(wrapped):
        # The middleware if a class method would already be
        # bound at this point, so is safe to determine the name
        # when it is being wrapped rather than on each
        # invocation.

        name = callable_name(wrapped)

        def wrapper(wrapped, instance, args, kwargs):
            transaction = current_transaction()

            def _wrapped(request, view_func, view_args, view_kwargs):
                # This strips the view handler wrapper before call.

                if hasattr(view_func, '_nr_last_object'):
                    view_func = view_func._nr_last_object

                return wrapped(request, view_func, view_args, view_kwargs)

            if transaction is None:
                return _wrapped(*args, **kwargs)

            before = (transaction.name, transaction.group)

            with FunctionTrace(transaction, name=name):
                try:
                    return _wrapped(*args, **kwargs)

                finally:
                    # We want to name the transaction after this
                    # middleware but only if the transaction wasn't
                    # named from within the middleware itself explicity.

                    after = (transaction.name, transaction.group)
                    if before == after:
                        transaction.set_transaction_name(name, priority=2)

        return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 23
0
def OutFunctionWrapper(wrapped, function):
    def wrapper(wrapped, instance, args, kwargs):
        return function(wrapped(*args, **kwargs))

    return ObjectWrapper(wrapped, None, wrapper)
def instrument_tornado_web(module):
    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

    module.Application.__call__ = ObjectWrapper(module.Application.__call__,
                                                None, call_wrapper)

    # Also need to wrap the method which executes the request handler.

    def execute_wrapper(wrapped, instance, args, kwargs):
        assert instance is not None

        handler = instance
        request = handler.request

        # 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 is None:
            return wrapped(*args, **kwargs)

        if request.method not in handler.SUPPORTED_METHODS:
            return wrapped(*args, **kwargs)

        name = callable_name(getattr(handler, request.method.lower()))
        transaction.set_transaction_name(name)

        with FunctionTrace(transaction, name=name):
            return wrapped(*args, **kwargs)

    module.RequestHandler._execute = ObjectWrapper(
        module.RequestHandler._execute, None, execute_wrapper)

    def error_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is not None:
            record_exception(transaction, sys.exc_info())

        return wrapped(*args, **kwargs)

    module.RequestHandler._handle_request_exception = ObjectWrapper(
        module.RequestHandler._handle_request_exception, None, error_wrapper)

    def render_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        name = callable_name(wrapped)
        with FunctionTrace(transaction, name=name):
            return wrapped(*args, **kwargs)

    # XXX This mucks up Tornado's calculation of where template file
    # is as it does walking of the stack frames to work it out and the
    # wrapper makes it stop before getting to the users code.

    #module.RequestHandler.render = ObjectWrapper(
    #        module.RequestHandler.render, None, render_wrapper)
    #module.RequestHandler.render_string = ObjectWrapper(
    #        module.RequestHandler.render_string, None, render_wrapper)

    def finish_wrapper(wrapped, instance, args, kwargs):
        assert instance is not None

        handler = instance
        request = handler.request

        # Call finish() method straight away if request object it is
        # being called on is not even associated with a transaction.
        # If we were in a running transaction we still want to record
        # the call though. This will occur when calling finish on
        # another request, but the target request wasn't monitored.

        transaction = getattr(request, '_nr_transaction', None)

        running_transaction = current_transaction()

        if not transaction:
            if running_transaction:
                name = callable_name(wrapped)

                with FunctionTrace(transaction, name):
                    return wrapped(*args, **kwargs)

            else:
                return wrapped(*args, **kwargs)

        # Do we have a running transaction. When we do we need to
        # consider two possiblities. The first is where the current
        # running transaction doesn't match that bound to the request.
        # For this case it would be where from within one transaction
        # there is an attempt to call finish() on a distinct web request
        # which was being monitored. The second is where finish() is
        # being called for the current request.

        if running_transaction:
            if transaction != running_transaction:
                # For this case we need to suspend the current running
                # transaction and call ourselves again. When it returns
                # we need to restore things back the way they were.
                # We still trace the call in the running transaction
                # though so the fact that it called finish on another
                # request is apparent.

                name = callable_name(wrapped)

                with FunctionTrace(running_transaction, name):
                    try:
                        running_transaction.drop_transaction()

                        return finish_wrapper(wrapped, instance, args, kwargs)

                    finally:
                        running_transaction.save_transaction()

            else:
                # For this case we just trace the call.

                name = callable_name(wrapped)

                with FunctionTrace(transaction, name):
                    return wrapped(*args, **kwargs)

        # No current running transaction. If we aren't in a wait state
        # we call finish() straight away.

        if not request._nr_wait_function_trace:
            return wrapped(*args, **kwargs)

        # Now handle the special case where finish() was called while in
        # the wait state. We need to restore the transaction for the
        # request and then call finish(). When it returns we need to
        # either end the transaction or go into a new wait state where
        # we wait on output to be sent.

        transaction.save_transaction()

        try:
            complete = True

            request._nr_wait_function_trace.__exit__(None, None, None)

            name = callable_name(wrapped)

            with FunctionTrace(transaction, name):
                result = wrapped(*args, **kwargs)

            if not request.connection.stream.writing():
                transaction.__exit__(None, None, None)

            else:
                request._nr_wait_function_trace = FunctionTrace(
                    transaction, name='Request/Output', group='Python/Tornado')

                request._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

                complete = False

            return result

        except:  # Catch all
            transaction.__exit__(*sys.exc_info())
            raise

        finally:
            if complete:
                request._nr_wait_function_trace = None
                request._nr_transaction = None

    module.RequestHandler.finish = ObjectWrapper(module.RequestHandler.finish,
                                                 None, finish_wrapper)

    def generate_headers_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        transaction._thread_utilization_start = None

        status = '%d ???' % instance.get_status()

        # The HTTPHeaders class with get_all() only started to
        # be used in Tornado 3.0. For older versions have to fall
        # back to combining the dictionary and list of headers.

        try:
            response_headers = instance._headers.get_all()

        except AttributeError:
            try:
                response_headers = itertools.chain(instance._headers.items(),
                                                   instance._list_headers)

            except AttributeError:
                response_headers = itertools.chain(instance._headers.items(),
                                                   instance._headers)

        additional_headers = transaction.process_response(
            status, response_headers, *args)

        for name, value in additional_headers:
            instance.add_header(name, value)

        return wrapped(*args, **kwargs)

    module.RequestHandler._generate_headers = ObjectWrapper(
        module.RequestHandler._generate_headers, None,
        generate_headers_wrapper)

    def on_connection_close_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction:
            return wrapped(*args, **kwargs)

        handler = instance
        request = handler.request

        transaction = getattr(request, '_nr_transaction', None)

        if not transaction:
            return wrapped(*args, **kwargs)

        transaction.save_transaction()

        if request._nr_wait_function_trace:
            request._nr_wait_function_trace.__exit__(None, None, None)

        name = callable_name(wrapped)

        try:
            with FunctionTrace(transaction, name):
                return wrapped(*args, **kwargs)

        except Exception:
            transaction.record_exception(*sys.exc_info())

        finally:
            transaction.__exit__(None, None, None)

    def init_wrapper(wrapped, instance, args, kwargs):
        if instance:
            # Bound method when RequestHandler instantiated
            # directly.

            handler = instance

        elif args:
            # When called from derived class constructor it is
            # not done so as a bound method. Instead the self
            # object will be passed as the first argument.

            handler = args[0]

        else:
            # Incorrect number of arguments. Pass it through so
            # it fails on call.

            return wrapped(*args, **kwargs)

        handler.on_connection_close = ObjectWrapper(
            handler.on_connection_close, None, on_connection_close_wrapper)

        return wrapped(*args, **kwargs)

    module.RequestHandler.__init__ = ObjectWrapper(
        module.RequestHandler.__init__, None, init_wrapper)
def instrument_tornado_wsgi(module):

    module.WSGIContainer.__call__ = ObjectWrapper(
        module.WSGIContainer.__call__, None, wsgi_container_call_wrapper)
def instrument_tornado_httpserver(module):
    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

    module.HTTPConnection._on_headers = ObjectWrapper(
        module.HTTPConnection._on_headers, None, on_headers_wrapper)

    def on_request_body_wrapper(wrapped, instance, args, kwargs):
        assert instance is not None

        connection = instance
        request = connection._request

        # Wipe out our temporary callback for being notified that the
        # connection is being closed before content is read.

        connection.stream._nr_close_callback = None

        # If no transaction associated with the request we can call
        # through straight away. There should also be a current function
        # trace node.

        if not hasattr(request, '_nr_transaction'):
            return wrapped(*args, **kwargs)

        if not request._nr_transaction:
            return wrapped(*args, **kwargs)

        if not request._nr_wait_function_trace:
            return wrapped(*args, **kwargs)

        # Restore the transaction.

        transaction = request._nr_transaction

        transaction.save_transaction()

        # Exit the function trace node. This should correspond to the
        # reading of the request input.

        try:
            request._nr_wait_function_trace.__exit__(None, None, None)

        finally:
            request._nr_wait_function_trace = None

        try:
            # Now call the orginal wrapped function. It will in turn
            # call the application which will ensure the transaction
            # started here is popped off.

            return wrapped(*args, **kwargs)

        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 '
                              'HTTPConnection._on_request_body().')

            transaction.__exit__(*sys.exc_info())
            request._nr_transaction = None

            raise

    module.HTTPConnection._on_request_body = ObjectWrapper(
        module.HTTPConnection._on_request_body, None, on_request_body_wrapper)

    def finish_request_wrapper(wrapped, instance, args, kwargs):
        assert instance is not None

        request = instance._request

        transaction = current_transaction()

        if transaction:
            request._nr_request_finished = True

            try:
                result = wrapped(*args, **kwargs)

                if (hasattr(request, '_nr_wait_function_trace')
                        and request._nr_wait_function_trace):
                    request._nr_wait_function_trace.__exit__(None, None, None)

            finally:
                request._nr_wait_function_trace = None

            return result

        else:
            if not hasattr(request, '_nr_transaction'):
                return wrapped(*args, **kwargs)

            transaction = request._nr_transaction

            if transaction is None:
                return wrapped(*args, **kwargs)

            transaction.save_transaction()

            request._nr_request_finished = True

            try:
                result = wrapped(*args, **kwargs)

                if request._nr_wait_function_trace:
                    request._nr_wait_function_trace.__exit__(None, None, None)

                transaction.__exit__(None, None, None)

            except:  # Catch all
                transaction.__exit__(*sys.exc_info())
                raise

            finally:
                request._nr_wait_function_trace = None
                request._nr_transaction = None

            return result

    module.HTTPConnection._finish_request = ObjectWrapper(
        module.HTTPConnection._finish_request, None, finish_request_wrapper)

    def finish_wrapper(wrapped, instance, args, kwargs):
        assert instance is not None

        request = instance

        # Call finish() method straight away if request object it is
        # being called on is not even associated with a transaction.

        transaction = getattr(request, '_nr_transaction', None)

        if not transaction:
            return wrapped(*args, **kwargs)

        # Do we have a running transaction. When we do we need to
        # consider two possiblities. The first is where the current
        # running transaction doesn't match that bound to the request.
        # For this case it would be where from within one transaction
        # there is an attempt to call finish() on a distinct web request
        # which was being monitored. The second is where finish() is
        # being called for the current request.

        running_transaction = current_transaction()

        if running_transaction:
            if transaction != running_transaction:
                # For this case we need to suspend the current running
                # transaction and call ourselves again. When it returns
                # we need to restore things back the way they were.

                try:
                    running_transaction.drop_transaction()

                    return finish_wrapper(wrapped, instance, args, kwargs)

                finally:
                    running_transaction.save_transaction()

            else:
                # For this case we just trace the call.

                with FunctionTrace(transaction,
                                   name='Request/Finish',
                                   group='Python/Tornado'):
                    return wrapped(*args, **kwargs)

        # No current running transaction. If we aren't in a wait state
        # we call finish() straight away.

        if not request._nr_wait_function_trace:
            return wrapped(*args, **kwargs)

        # Now handle the special case where finish() was called while in
        # the wait state. We need to restore the transaction for the
        # request and then call finish(). When it returns we need to
        # either end the transaction or go into a new wait state where
        # we wait on output to be sent.

        transaction.save_transaction()

        try:
            complete = True

            request._nr_wait_function_trace.__exit__(None, None, None)

            with FunctionTrace(transaction,
                               name='Request/Finish',
                               group='Python/Tornado'):
                result = wrapped(*args, **kwargs)

            if not request.connection.stream.writing():
                transaction.__exit__(None, None, None)

            else:
                request._nr_wait_function_trace = FunctionTrace(
                    transaction, name='Request/Output', group='Python/Tornado')

                request._nr_wait_function_trace.__enter__()
                transaction.drop_transaction()

                complete = False

            return result

        except:  # Catch all
            transaction.__exit__(*sys.exc_info())
            raise

        finally:
            if complete:
                request._nr_wait_function_trace = None
                request._nr_transaction = None

    module.HTTPRequest.finish = ObjectWrapper(module.HTTPRequest.finish, None,
                                              finish_wrapper)

    if hasattr(module.HTTPRequest, '_parse_mime_body'):
        wrap_function_trace(module.HTTPRequest, '_parse_mime_body')
    def stack_context_wrap_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if not transaction:
            return wrapped(*args, **kwargs)

        def callback_wrapper(wrapped, instance, args, kwargs):

            if current_transaction():
                return wrapped(*args, **kwargs)

            if not hasattr(transaction, '_nr_current_request'):
                return wrapped(*args, **kwargs)

            request = transaction._nr_current_request()

            if not request:
                return wrapped(*args, **kwargs)

            if not hasattr(request, '_nr_wait_function_trace'):
                return wrapped(*args, **kwargs)

            if not request._nr_wait_function_trace:
                return wrapped(*args, **kwargs)

            transaction.save_transaction()

            if request._nr_wait_function_trace:
                request._nr_wait_function_trace.__exit__(None, None, None)

            request._nr_wait_function_trace = None

            try:
                name = callable_name(wrapped)
                with FunctionTrace(transaction, name=name):
                    return wrapped(*args, **kwargs)

            except Exception:
                transaction.record_exception(*sys.exc_info())

                raise

            finally:
                if not request._nr_request_finished:
                    request._nr_wait_function_trace = FunctionTrace(
                        transaction,
                        name='Callback/Wait',
                        group='Python/Tornado')

                    request._nr_wait_function_trace.__enter__()
                    transaction.drop_transaction()

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

        def _fn(fn, *args, **kwargs):
            return fn

        fn = _fn(*args, **kwargs)

        # Tornado 3.1 doesn't use _StackContextWrapper and checks
        # for a '_wrapped' attribute instead which makes this a
        # bit more fragile.

        if hasattr(module, '_StackContextWrapper'):
            if fn is None or fn.__class__ is module._StackContextWrapper:
                return fn
        else:
            if fn is None or hasattr(fn, '_wrapped'):
                return fn

        fn = ObjectWrapper(fn, None, callback_wrapper)

        return wrapped(fn)
Esempio n. 28
0
def FunctionTraceWrapper(wrapped,
                         name=None,
                         group=None,
                         label=None,
                         params=None):
    def dynamic_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        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

        if callable(label):
            if instance and inspect.ismethod(wrapped):
                _label = label(instance, *args, **kwargs)
            else:
                _label = label(*args, **kwargs)

        else:
            _label = label

        if callable(params):
            if instance and inspect.ismethod(wrapped):
                _params = params(instance, *args, **kwargs)
            else:
                _params = params(*args, **kwargs)

        else:
            _params = params

        with FunctionTrace(transaction, _name, _group, _label, _params):
            return wrapped(*args, **kwargs)

    def literal_wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        _name = name or callable_name(wrapped)

        with FunctionTrace(transaction, _name, group, label, params):
            return wrapped(*args, **kwargs)

    if (callable(name) or callable(group) or callable(label)
            or callable(params)):
        return ObjectWrapper(wrapped, None, dynamic_wrapper)

    return ObjectWrapper(wrapped, None, literal_wrapper)
Esempio n. 29
0
def ProfileTraceWrapper(wrapped,
                        name=None,
                        group=None,
                        label=None,
                        params=None,
                        depth=3):
    def wrapper(wrapped, instance, args, kwargs):
        transaction = current_transaction()

        if transaction is None:
            return wrapped(*args, **kwargs)

        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

        if callable(label):
            if instance and inspect.ismethod(wrapped):
                _label = label(instance, *args, **kwargs)
            else:
                _label = label(*args, **kwargs)

        else:
            _label = label

        if callable(params):
            if instance and inspect.ismethod(wrapped):
                _params = params(instance, *args, **kwargs)
            else:
                _params = params(*args, **kwargs)

        else:
            _params = params

        with FunctionTrace(transaction, _name, _group, _label, _params):
            if not hasattr(sys, 'getprofile'):
                return wrapped(*args, **kwargs)

            profiler = sys.getprofile()

            if profiler:
                return wrapped(*args, **kwargs)

            sys.setprofile(ProfileTrace(depth))

            try:
                return wrapped(*args, **kwargs)

            finally:
                sys.setprofile(None)

    return ObjectWrapper(wrapped, None, wrapper)
Esempio n. 30
0
def instrument_tastypie_api(module):
    _wrap_view = module.Api.wrap_view
    module.Api.wrap_view = ObjectWrapper(_wrap_view, None, outer_fn_wrapper)