Repository URL to install this package:
|
Version:
2.68.0.50 ▾
|
import sys
import os
import newrelic.api.transaction
import newrelic.api.import_hook
import newrelic.api.web_transaction
import newrelic.api.external_trace
import newrelic.api.function_trace
import newrelic.api.transaction_name
import newrelic.api.object_wrapper
import newrelic.api.pre_function
def instrument_gluon_compileapp(module):
# Wrap the run_models_in() function as first phase
# in executing a request after URL has been mapped
# to a specific view. The name given to the web
# transaction is combination of the application name
# and view path.
def transaction_name_run_models_in(environment):
return '%s::%s' % (environment['request'].application,
environment['response'].view)
newrelic.api.transaction_name.wrap_transaction_name(module,
'run_models_in', name=transaction_name_run_models_in,
group='Web2Py')
# Wrap functions which coordinate the execution of
# the separate models, controller and view phases of
# the request handling. This is done for timing how
# long taken within these phases of request
# handling.
def name_function_run_models_in(environment):
return '%s/%s' % (environment['request'].controller,
environment['request'].function)
newrelic.api.function_trace.wrap_function_trace(module,
'run_models_in', name=name_function_run_models_in,
group='Python/Web2Py/Models')
def name_function_run_controller_in(controller, function, environment):
return '%s/%s' % (controller, function)
newrelic.api.function_trace.wrap_function_trace(module,
'run_controller_in', name=name_function_run_controller_in,
group='Python/Web2Py/Controller')
def name_function_run_view_in(environment):
return '%s/%s' % (environment['request'].controller,
environment['request'].function)
newrelic.api.function_trace.wrap_function_trace(module,
'run_view_in', name=name_function_run_view_in,
group='Python/Web2Py/View')
def instrument_gluon_restricted(module):
# Wrap function which executes all the compiled
# Python code files. The name used corresponds to
# path of the resource within the context of the
# application directory. The group used is either
# 'Script/Execute' or 'Template/Render' based on
# whether we can work out whether code object
# corresponded to compiled template file or not.
def name_function_restricted(code, environment={}, layer='Unknown'):
if 'request' in environment:
folder = environment['request'].folder
if layer.startswith(folder):
return layer[len(folder):]
return layer
def group_function_restricted(code, environment={}, layer='Unknown'):
parts = layer.split('.')
if parts[-1] in ['html'] or parts[-2:] in [['html','pyc']] :
return 'Template/Render'
return 'Script/Execute'
newrelic.api.function_trace.wrap_function_trace(module, 'restricted',
name=name_function_restricted, group=group_function_restricted)
def instrument_gluon_main(module):
newrelic.api.web_transaction.wrap_wsgi_application(module, 'wsgibase')
# Wrap main function which dispatches the various
# phases of a request in order to capture any
# errors. Need to use a custom object wrapper as we
# need to ignore exceptions of type HTTP as that
# type of exception is used to programmatically
# return a valid response. For the case of a 404,
# where we want to name the web transactions as
# such, we pick that up later.
class error_serve_controller(object):
def __init__(self, wrapped):
newrelic.api.object_wrapper.update_wrapper(self, wrapped)
self._nr_next_object = wrapped
if not hasattr(self, '_nr_last_object'):
self._nr_last_object = wrapped
def __call__(self, request, response, session):
txn = newrelic.api.transaction.current_transaction()
if txn:
HTTP = newrelic.api.import_hook.import_module('gluon.http').HTTP
try:
return self._nr_next_object(request, response, session)
except HTTP:
raise
except: # Catch all
txn.record_exception(*sys.exc_info())
raise
else:
return self._nr_next_object(request, response, session)
def __getattr__(self, name):
return getattr(self._nr_next_object, name)
newrelic.api.object_wrapper.wrap_object(
module, 'serve_controller', error_serve_controller)
def instrument_gluon_template(module):
# Wrap parsing/compilation of template files, using
# the name of the template relative to the context
# of the application it is contained in. Use a group
# of 'Template/Compile'. Rendering of template is
# picked up when executing the code object created
# from this compilation step.
def name_function_parse_template(filename, path='views/',
context=dict(), *args, **kwargs):
if 'request' in context:
folder = context['request'].folder
if path.startswith(folder):
return '%s/%s' % (path[len(folder):], filename)
else:
return '%s/%s' % (path, filename)
newrelic.api.function_trace.wrap_function_trace(module, 'parse_template',
name=name_function_parse_template, group='Template/Compile')
def instrument_gluon_tools(module):
# Wrap utility function for fetching an external URL.
def url_external_fetch(url, *args, **kwargs):
return url
newrelic.api.external_trace.wrap_external_trace(
module, 'fetch', library='gluon.tools.fetch',
url=url_external_fetch)
# Wrap utility function for fetching GEOCODE data.
# The URL in this case is hardwired in code to point
# at Google service and not part of arguments to we
# need to hard code it here as well.
newrelic.api.external_trace.wrap_external_trace(
module, 'geocode', library='gluon.tools.geocode',
url='http://maps.google.com/maps/geo')
def instrument_gluon_http(module):
# This one is tricky. The only way to pick up that a
# static file is being served up is to wrap the to()
# method of a HTTP response object when actual
# response is being generated. We need to qualify
# this so only actually do anything when called from
# the wsgibase() function within 'gluon.main'. To do
# this need to go stack diving and look back at the
# parent stack frame. Doing that we can look at
# details of where calling code is located as well
# as sneak a peak at local variables in the calling
# stack to determine if we were handling a static
# file and what type of file was being served.
# Normally static file URLs would be left alone but
# don't want to risk black hole rule and instead
# generate custom wildcard URLs with precedence to
# extension. When can work out how to reliably get
# the application name then can incorporate that
# into the pattern as well in style used for web
# transaction names for views. The application name
# should normally be the first path segment, but the
# fact that arbitrary rewrite rules can be used may
# mean that isn't always the case.
def transaction_name_name_not_found(response, *args, **kwargs):
txn = newrelic.api.transaction.current_transaction()
if not txn:
return
frame = sys._getframe(1)
if os.path.split(frame.f_code.co_filename)[-1] == 'pre_function.py':
frame = frame.f_back
if os.path.split(frame.f_code.co_filename)[-1] != 'main.py':
return
if frame.f_code.co_name != 'wsgibase':
return
if response.status == 400:
txn.set_transaction_name('400', 'Uri')
return
if response.status == 404:
txn.set_transaction_name('404', 'Uri')
return
if 'static_file' not in frame.f_locals:
return
if frame.f_locals['static_file']:
if 'environ' in frame.f_locals:
environ = frame.f_locals['environ']
path_info = environ.get('PATH_INFO', '')
if path_info:
parts = os.path.split(path_info)
if parts[1] == '':
if parts[0] == '/':
txn.set_transaction_name('*', 'Web2Py')
else:
name = '%s/*' % parts[0].lstrip('/')
txn.set_transaction_name(name, 'Web2Py')
else:
extension = os.path.splitext(parts[1])[-1]
name = '%s/*%s' % (parts[0].lstrip('/'), extension)
txn.set_transaction_name(name, 'Web2Py')
else:
txn.set_transaction_name('*', 'Web2Py')
else:
txn.set_transaction_name('*', 'Web2Py')
newrelic.api.pre_function.wrap_pre_function(
module, 'HTTP.to', transaction_name_name_not_found)