Files
2020-02-26 08:57:16 -08:00

111 lines
4.4 KiB
Python

import sys
from webob import Request
from pylons import config
from .renderer import trace_rendering
from .constants import CONFIG_MIDDLEWARE
from ...compat import reraise
from ...constants import ANALYTICS_SAMPLE_RATE_KEY
from ...ext import SpanTypes, http
from ...internal.logger import get_logger
from ...propagation.http import HTTPPropagator
from ...settings import config as ddconfig
log = get_logger(__name__)
class PylonsTraceMiddleware(object):
def __init__(self, app, tracer, service='pylons', distributed_tracing=True):
self.app = app
self._service = service
self._distributed_tracing = distributed_tracing
self._tracer = tracer
# register middleware reference
config[CONFIG_MIDDLEWARE] = self
# add template tracing
trace_rendering()
def __call__(self, environ, start_response):
if self._distributed_tracing:
# retrieve distributed tracing headers
request = Request(environ)
propagator = HTTPPropagator()
context = propagator.extract(request.headers)
# only need to active the new context if something was propagated
if context.trace_id:
self._tracer.context_provider.activate(context)
with self._tracer.trace('pylons.request', service=self._service, span_type=SpanTypes.WEB) as span:
# Set the service in tracer.trace() as priority sampling requires it to be
# set as early as possible when different services share one single agent.
# set analytics sample rate with global config enabled
span.set_tag(
ANALYTICS_SAMPLE_RATE_KEY,
ddconfig.pylons.get_analytics_sample_rate(use_global_config=True)
)
if not span.sampled:
return self.app(environ, start_response)
# tentative on status code, otherwise will be caught by except below
def _start_response(status, *args, **kwargs):
""" a patched response callback which will pluck some metadata. """
http_code = int(status.split()[0])
span.set_tag(http.STATUS_CODE, http_code)
if http_code >= 500:
span.error = 1
return start_response(status, *args, **kwargs)
try:
return self.app(environ, _start_response)
except Exception as e:
# store current exceptions info so we can re-raise it later
(typ, val, tb) = sys.exc_info()
# e.code can either be a string or an int
code = getattr(e, 'code', 500)
try:
code = int(code)
if not 100 <= code < 600:
code = 500
except Exception:
code = 500
span.set_tag(http.STATUS_CODE, code)
span.error = 1
# re-raise the original exception with its original traceback
reraise(typ, val, tb=tb)
except SystemExit:
span.set_tag(http.STATUS_CODE, 500)
span.error = 1
raise
finally:
controller = environ.get('pylons.routes_dict', {}).get('controller')
action = environ.get('pylons.routes_dict', {}).get('action')
# There are cases where users re-route requests and manually
# set resources. If this is so, don't do anything, otherwise
# set the resource to the controller / action that handled it.
if span.resource == span.name:
span.resource = '%s.%s' % (controller, action)
span.set_tags({
http.METHOD: environ.get('REQUEST_METHOD'),
http.URL: '%s://%s:%s%s' % (environ.get('wsgi.url_scheme'),
environ.get('SERVER_NAME'),
environ.get('SERVER_PORT'),
environ.get('PATH_INFO')),
'pylons.user': environ.get('REMOTE_USER', ''),
'pylons.route.controller': controller,
'pylons.route.action': action,
})
if ddconfig.pylons.trace_query_string:
span.set_tag(http.QUERY_STRING, environ.get('QUERY_STRING'))