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) |