Skip to content

Instantly share code, notes, and snippets.

@GrahamDumpleton
Created September 6, 2012 06:58
Show Gist options
  • Save GrahamDumpleton/3652313 to your computer and use it in GitHub Desktop.
Save GrahamDumpleton/3652313 to your computer and use it in GitHub Desktop.
Nova instrumentation for New Relic
import types
import sys
from newrelic.api.object_wrapper import callable_name
from newrelic.api.error_trace import wrap_error_trace
from newrelic.api.in_function import wrap_in_function
from newrelic.api.transaction_name import wrap_transaction_name
from newrelic.api.function_trace import wrap_function_trace, FunctionTraceWrapper
from newrelic.api.external_trace import wrap_external_trace
from newrelic.api.web_transaction import WSGIApplicationWrapper
# First bunch of instrumentation is core stuff we always want to have.
def instrument_nova_wsgi(module):
# This is wrapping the WSGI application entry point by
# capturing it while being passed into server object for
# use.
def in_server_init(self, name, application, **kwargs):
return ((self, name, WSGIApplicationWrapper(application)),
kwargs)
wrap_in_function(module, 'Server.__init__', in_server_init)
def instrument_nova_api_openstack_wsgi(module):
# This is naming the web transaction after the function which
# corresponds to the controller/action.
def name_transaction_resource_dispatch(resource, request, action,
action_args, **kwargs):
controller_method = getattr(resource.controller, action)
name = callable_name(controller_method)
return name
wrap_transaction_name(module, 'Resource.dispatch',
name=name_transaction_resource_dispatch)
# Same function but this time recording time spent executing it.
def name_function_resource_dispatch(resource, request, action,
action_args, **kwargs):
controller_method = getattr(resource.controller, action)
name = callable_name(controller_method)
return name
# Now wrapping all the controller methods automatically so don't
# need to be doing this any more.
# wrap_function_trace(module, 'Resource.dispatch',
# name=name_function_resource_dispatch)
# We also capture error trace at this point. That is unhandled
# exceptions. The error trace should probably be wrapped around
# method higher up call chain but below where exceptions are caught
# and then converted into error response pages. Here will do for now
# as haven't found better spot yet.
wrap_error_trace(module, 'Resource.dispatch')
# Wrap the constructor for a resource and instrument automatically
# any controller object associated with a resource.
def in_function_resource_init(resource, controller, serializers=None,
deserializers=None, *args, **kwargs):
# We iterate over all attributes of the controller and for
# each instamce method we wrap it with a function trace wrapper.
# Once wrapped that don't have method type so can't wrap twice.
if not controller:
for name in dir(controller):
object = getattr(controller, name)
if type(object) == types.MethodType:
wrapped = FunctionTraceWrapper(object)
setattr(controller, name, wrapped)
return ((resource, controller, serializers, deserializers), kwargs)
wrap_in_function(module, 'Resource.__init__',
in_function_resource_init)
# Following is where we might start instrumenting optional stuff to
# get a better idea of where time is being spent.
def instrument_nova_api_openstack_views_images(module):
wrap_function_trace(module, 'ViewBuilderV10.build')
wrap_function_trace(module, 'ViewBuilderV11.build')
def instrument_nova_image_service(module):
wrap_function_trace(module, 'BaseImageService.index')
def instrument_nova_image_fake(module):
wrap_function_trace(module, '_FakeImageService.index')
def instrument_nova_image_glance(module):
wrap_function_trace(module, 'GlanceImageService.index')
def instrument_nova_image_s3(module):
wrap_function_trace(module, 'S3ImageService.index')
def instrument_glance_client(module):
def url_baseclient_do_request(client, method, action, *args, **kwargs):
return 'http://%s%s/%s' % (client.host, client.doc_root,
action.lstrip("/"))
wrap_external_trace(module, 'V1Client.do_request',
library='glance.client', url=url_baseclient_do_request)
# Could have manually wrapped each method of this class but a
# quicker way which copes with methods being added or removed
# is to enumerate over all attributes of the class and if they
# correspond to an unbound method then wrap it. Note that there
# is not type object in 'types' module for this type of unbound
# class method so get them type by looking at type of one of the
# existing methods. We probably should have this skip special
# methods like __getattr__() and __setattr_() etc. Should have
# function which you can give it a class and do all this for
# you automatically.
# wrap_function_trace(module, 'V1Client.get_images')
# wrap_function_trace(module, 'V1Client.get_images_detailed')
# wrap_function_trace(module, 'V1Client.get_image')
# wrap_function_trace(module, 'V1Client.get_image_meta')
# wrap_function_trace(module, 'V1Client.add_image')
# wrap_function_trace(module, 'V1Client.update_image')
# wrap_function_trace(module, 'V1Client.delete_image')
unbound_method_type = type(getattr(module.V1Client, 'do_request'))
for name in dir(module.V1Client):
object = getattr(module.V1Client, name)
if type(object) == unbound_method_type:
object_path = 'V1Client.%s' % name
wrap_function_trace(module, object_path)
def instrument_db_sqlalchemy_api(module):
# This module has heaps and heaps of functions, no classes. Could
# have enumerated over all of them in style shown commented out
# below, but can't be bothered to manually enumerate them all. So
# cheat and iterate over everything in the module and if its is
# callable and defined in the module then we wrap it.
# wrap_function_trace(module, 'auth_token_get')
for name in dir(module):
object = getattr(module, name)
if callable(object) and hasattr(object, '__module__'):
if getattr(object, '__module__') == module.__name__:
wrap_function_trace(module, name)
def instrument_compute_api(module):
unbound_method_type = type(getattr(module.API, 'get_all'))
for name in dir(module.API):
object = getattr(module.API, name)
if type(object) == unbound_method_type:
object_path = 'API.%s' % name
wrap_function_trace(module, object_path)
def instrument_api_openstack_auth(module):
unbound_method_type = type(getattr(module.AuthMiddleware, '__init__'))
for name in dir(module.AuthMiddleware):
# Don't know whether better to skip '__' methods or not.
if not name.startswith('__'):
object = getattr(module.AuthMiddleware, name)
if type(object) == unbound_method_type:
object_path = 'AuthMiddleware.%s' % name
wrap_function_trace(module, object_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment