src/pyams_apm/tween.py
changeset 15 1cedda2f8455
parent 12 64c0d548a45c
child 21 75bafb4ce0b0
equal deleted inserted replaced
14:3d0e5eb0a181 15:1cedda2f8455
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
    13 __docformat__ = 'restructuredtext'
    13 """PyAMS_apm.tween module
       
    14 
       
    15 This module provides a custom tween which is used to integrate the Pyramid application
       
    16 with APM, sending requests frames to the APM server.
       
    17 """
    14 
    18 
    15 import sys
    19 import sys
    16 
    20 
    17 import elasticapm
    21 import elasticapm
    18 import pkg_resources
    22 import pkg_resources
    19 from elasticapm.utils import compat, get_url_dict
    23 from elasticapm.utils import compat, get_url_dict
    20 from pkg_resources import DistributionNotFound
    24 from pkg_resources import DistributionNotFound
    21 from pyramid.compat import reraise
    25 from pyramid.compat import reraise
    22 from pyramid.settings import asbool
    26 from pyramid.settings import asbool
    23 
    27 
       
    28 __docformat__ = 'restructuredtext'
       
    29 
    24 
    30 
    25 def list_from_setting(config, setting):
    31 def list_from_setting(config, setting):
       
    32     """Split configuration setting"""
    26     value = config.get(setting)
    33     value = config.get(setting)
    27     if not value:
    34     if not value:
    28         return None
    35         return None
    29     return value.split()
    36     return value.split()
    30 
    37 
    31 
    38 
    32 def get_data_from_request(request):
    39 def get_data_from_request(request):
       
    40     """Extract main APM data from request properties"""
    33     data = {
    41     data = {
    34         "headers": dict(**request.headers),
    42         "headers": dict(**request.headers),
    35         "method": request.method,
    43         "method": request.method,
    36         "socket": {
    44         "socket": {
    37             "remote_address": request.remote_addr,
    45             "remote_address": request.remote_addr,
    44     data["headers"].pop("Cookie", None)
    52     data["headers"].pop("Cookie", None)
    45     return data
    53     return data
    46 
    54 
    47 
    55 
    48 def get_data_from_response(response):
    56 def get_data_from_response(response):
       
    57     """Extract APM data from response properties"""
    49     data = {"status_code": response.status_int}
    58     data = {"status_code": response.status_int}
    50     if response.headers:
    59     if response.headers:
    51         data["headers"] = {
    60         data["headers"] = {
    52             key: ";".join(response.headers.getall(key))
    61             key: ";".join(response.headers.getall(key))
    53             for key in compat.iterkeys(response.headers)
    62             for key in compat.iterkeys(response.headers)
    54         }
    63         }
    55     return data
    64     return data
    56 
    65 
    57 
    66 
    58 class elastic_apm_tween_factory(object):
    67 class elastic_apm_tween_factory:  # pylint: disable=invalid-name
    59     """Elasticsearch APM tween factory"""
    68     """Elasticsearch APM tween factory"""
    60 
    69 
    61     def __init__(self, handler, registry):
    70     def __init__(self, handler, registry):
    62         self.handler = handler
    71         self.handler = handler
    63         self.registry = registry
    72         self.registry = registry
    87         try:
    96         try:
    88             response = self.handler(request)
    97             response = self.handler(request)
    89             transaction_result = response.status[0] + "xx"
    98             transaction_result = response.status[0] + "xx"
    90             elasticapm.set_context(lambda: get_data_from_response(response), "response")
    99             elasticapm.set_context(lambda: get_data_from_response(response), "response")
    91             return response
   100             return response
    92         except Exception:
   101         except Exception:  # pylint: disable=broad-except
    93             transaction_result = '5xx'
   102             transaction_result = '5xx'
    94             self.client.capture_exception(
   103             self.client.capture_exception(
    95                 context={
   104                 context={
    96                     "request": get_data_from_request(request)
   105                     "request": get_data_from_request(request)
    97                 },
   106                 },
   105                     view_name = request.traversed[-1] if request.traversed else '/'
   114                     view_name = request.traversed[-1] if request.traversed else '/'
   106             except AttributeError:
   115             except AttributeError:
   107                 view_name = ''
   116                 view_name = ''
   108             transaction_name = request.matched_route.pattern if request.matched_route else view_name
   117             transaction_name = request.matched_route.pattern if request.matched_route else view_name
   109             # prepend request method
   118             # prepend request method
   110             transaction_name = " ".join((request.method, transaction_name)) if transaction_name else ""
   119             transaction_name = " ".join((request.method, transaction_name)) \
       
   120                 if transaction_name else ""
   111             elasticapm.set_context(lambda: get_data_from_request(request), "request")
   121             elasticapm.set_context(lambda: get_data_from_request(request), "request")
   112             self.client.end_transaction(transaction_name, transaction_result)
   122             self.client.end_transaction(transaction_name, transaction_result)