Repository URL to install this package:
|
Version:
2.68.0.50 ▾
|
"""Instrumentation module for CherryPy framework.
"""
# TODO
#
# * We don't track time spent in a user supplied error response callback.
# We do track time in the handle_error() function which calls it though.
# Because the error_response attribute of the request object could be
# updated dynamically, is tricky to handle. We would need to replace
# error_response attribute at the time the request is created, with a
# data descriptor which detects when it is set and saves away value, and
# then wraps the value with a function trace when later access. May
# have to deal with someone overriding the class attribute version of
# error_response as well, which means need a class level data descriptor
# which gets trickier as it isn't bound to an instance and has to do
# tricks to store the error response handler being set against the
# instance if set via an instance.
#
# * We don't track time spent in hook functions which may be registered
# for events such as before_handler, on_end_request etc.
#
# * We don't handle any sub dispatching that may be occuring due to the
# use of XMLRPCDispatcher.
from newrelic.agent import (current_transaction, wrap_wsgi_application,
FunctionTrace, callable_name, ObjectProxy, function_wrapper,
wrap_function_wrapper, wrap_function_trace, ignore_status_code)
def framework_details():
import cherrypy
return ('CherryPy', getattr(cherrypy, '__version__', None))
def should_ignore(exc, value, tb):
from cherrypy import HTTPError, HTTPRedirect
# Ignore certain exceptions based on HTTP status codes.
if isinstance(value, (HTTPError, HTTPRedirect)):
if ignore_status_code(value.status):
return True
# Ignore certain exceptions based on their name.
module = value.__class__.__module__
name = value.__class__.__name__
fullname = '%s:%s' % (module, name)
ignore_exceptions = ('cherrypy._cperror:InternalRedirect',)
if fullname in ignore_exceptions:
return True
@function_wrapper
def handler_wrapper(wrapped, instance, args, kwargs):
transaction = current_transaction()
# Name the web transaction after the handler function.
name = callable_name(wrapped)
transaction.set_transaction_name(name=name)
# Track how long is spent in the handler and record any exceptions
# except those which are used for controlling the actions of the
# application.
with FunctionTrace(transaction, name=name):
try:
return wrapped(*args, **kwargs)
except: # Catch all
transaction.record_exception(ignore_errors=should_ignore)
raise
class ResourceProxy(ObjectProxy):
def __getattr__(self, name):
# Methods on the wrapped object corresponding to the HTTP
# method will always be upper case. Wrap the method when
# returned with the handler wrapper.
attr = super(ResourceProxy, self).__getattr__(name)
return name.isupper() and handler_wrapper(attr) or attr
def wrapper_Dispatcher_find_handler(wrapped, instance, args, kwargs):
transaction = current_transaction()
if transaction is None:
return wrapped(*args, **kwargs)
try:
# Call the original wrapped function to find the handler.
obj, vpath = wrapped(*args, **kwargs)
except: # Catch all
# Can end up here when a custom _cp_dispatch() method is
# used and that raises an exception.
transaction.record_exception()
raise
if obj:
if instance.__class__.__name__ == 'MethodDispatcher':
# We initially name the web transaction as if the
# corresponding method for the HTTP method will not
# be found and the request will not be allowed. This
# will be overridden with the actual handler name
# when the subsequent wrapper around the handler is
# executed.
transaction.set_transaction_name('405', group='StatusCode')
# We have to use a custom object proxy here in order
# to intercept accesses made by the dispatcher on the
# returned object, after this function returns, to
# retrieve the method of the object corresponding to
# the HTTP method. For each such access we wrap what
# is returned in the handler wrapper.
obj = ResourceProxy(obj)
else:
# Should be the actual handler, wrap it with the
# handler wrapper.
obj = handler_wrapper(obj)
else:
# No handler could be found so name the web transaction
# after the 404 status code.
transaction.set_transaction_name('404', group='StatusCode')
return obj, vpath
def wrapper_RoutesDispatcher_find_handler(wrapped, instance, args, kwargs):
transaction = current_transaction()
if transaction is None:
return wrapped(*args, **kwargs)
try:
# Call the original wrapped function to find the handler.
handler = wrapped(*args, **kwargs)
except: # Catch all
# Can end up here when the URL was invalid in some way.
transaction.record_exception()
raise
if handler:
# Should be the actual handler, wrap it with the handler
# wrapper.
handler = handler_wrapper(handler)
else:
# No handler could be found so name the web transaction
# after the 404 status code.
transaction.set_transaction_name('404', group='StatusCode')
return handler
def instrument_cherrypy__cpreqbody(module):
wrap_function_trace(module, 'process_multipart')
wrap_function_trace(module, 'process_multipart_form_data')
def instrument_cherrypy__cprequest(module):
wrap_function_trace(module, 'Request.handle_error')
def instrument_cherrypy__cpdispatch(module):
wrap_function_wrapper(module, 'Dispatcher.find_handler',
wrapper_Dispatcher_find_handler)
wrap_function_wrapper(module, 'RoutesDispatcher.find_handler',
wrapper_RoutesDispatcher_find_handler)
def instrument_cherrypy__cpwsgi(module):
wrap_wsgi_application(module, 'CPWSGIApp.__call__',
framework=framework_details())
def instrument_cherrypy__cptree(module):
wrap_wsgi_application(module, 'Application.__call__',
framework=framework_details())