opentelemetry-python-contrib/reference/ddtrace/contrib/pyramid/patch.py

85 lines
3.2 KiB
Python

import os
from .trace import trace_pyramid, DD_TWEEN_NAME
from .constants import (
SETTINGS_SERVICE, SETTINGS_DISTRIBUTED_TRACING,
SETTINGS_ANALYTICS_ENABLED, SETTINGS_ANALYTICS_SAMPLE_RATE,
)
from ...utils.formats import asbool, get_env
import pyramid.config
from pyramid.path import caller_package
from ddtrace.vendor import wrapt
DD_PATCH = '_datadog_patch'
def patch():
"""
Patch pyramid.config.Configurator
"""
if getattr(pyramid.config, DD_PATCH, False):
return
setattr(pyramid.config, DD_PATCH, True)
_w = wrapt.wrap_function_wrapper
_w('pyramid.config', 'Configurator.__init__', traced_init)
def traced_init(wrapped, instance, args, kwargs):
settings = kwargs.pop('settings', {})
service = os.environ.get('DATADOG_SERVICE_NAME') or 'pyramid'
distributed_tracing = asbool(get_env('pyramid', 'distributed_tracing', True))
# DEV: integration-specific analytics flag can be not set but still enabled
# globally for web frameworks
analytics_enabled = get_env('pyramid', 'analytics_enabled')
if analytics_enabled is not None:
analytics_enabled = asbool(analytics_enabled)
analytics_sample_rate = get_env('pyramid', 'analytics_sample_rate', True)
trace_settings = {
SETTINGS_SERVICE: service,
SETTINGS_DISTRIBUTED_TRACING: distributed_tracing,
SETTINGS_ANALYTICS_ENABLED: analytics_enabled,
SETTINGS_ANALYTICS_SAMPLE_RATE: analytics_sample_rate,
}
# Update over top of the defaults
# DEV: If we did `settings.update(trace_settings)` then we would only ever
# have the default values.
trace_settings.update(settings)
# If the tweens are explicitly set with 'pyramid.tweens', we need to
# explicitly set our tween too since `add_tween` will be ignored.
insert_tween_if_needed(trace_settings)
kwargs['settings'] = trace_settings
# `caller_package` works by walking a fixed amount of frames up the stack
# to find the calling package. So if we let the original `__init__`
# function call it, our wrapper will mess things up.
if not kwargs.get('package', None):
# Get the packge for the third frame up from this one.
# - ddtrace.contrib.pyramid.path
# - ddtrace.vendor.wrapt
# - (this is the frame we want)
# DEV: Default is `level=2` which will give us the package from `wrapt`
kwargs['package'] = caller_package(level=3)
wrapped(*args, **kwargs)
trace_pyramid(instance)
def insert_tween_if_needed(settings):
tweens = settings.get('pyramid.tweens')
# If the list is empty, pyramid does not consider the tweens have been
# set explicitly.
# And if our tween is already there, nothing to do
if not tweens or not tweens.strip() or DD_TWEEN_NAME in tweens:
return
# pyramid.tweens.EXCVIEW is the name of built-in exception view provided by
# pyramid. We need our tween to be before it, otherwise unhandled
# exceptions will be caught before they reach our tween.
idx = tweens.find(pyramid.tweens.EXCVIEW)
if idx == -1:
settings['pyramid.tweens'] = tweens + '\n' + DD_TWEEN_NAME
else:
settings['pyramid.tweens'] = tweens[:idx] + DD_TWEEN_NAME + '\n' + tweens[idx:]