cavisson-pythonagent 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
- cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
- cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
- cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
- cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
- pythonagent/__init__.py +22 -0
- pythonagent/agent/__init__.py +219 -0
- pythonagent/agent/internal/__init__.py +1 -0
- pythonagent/agent/internal/agent.py +1651 -0
- pythonagent/agent/internal/framesinfo.py +152 -0
- pythonagent/agent/internal/heap_dump.py +39 -0
- pythonagent/agent/internal/intercept_module.py +70 -0
- pythonagent/agent/internal/logs.py +122 -0
- pythonagent/agent/internal/metadata/__init__.py +0 -0
- pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
- pythonagent/agent/internal/proc_compat.py +75 -0
- pythonagent/agent/internal/profile.py +47 -0
- pythonagent/agent/internal/provider.py +83 -0
- pythonagent/agent/internal/thread_dump.py +85 -0
- pythonagent/agent/internal/udp.py +245 -0
- pythonagent/agent/internal/udp_message.py +800 -0
- pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
- pythonagent/agent/probes/Instrumentation/find.py +243 -0
- pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
- pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
- pythonagent/agent/probes/Instrumentation/parser.py +129 -0
- pythonagent/agent/probes/__init__.py +219 -0
- pythonagent/agent/probes/base.py +303 -0
- pythonagent/agent/probes/cache/__init__.py +51 -0
- pythonagent/agent/probes/cache/redis.py +119 -0
- pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
- pythonagent/agent/probes/coroutines/__init__.py +1 -0
- pythonagent/agent/probes/coroutines/asyncio.py +63 -0
- pythonagent/agent/probes/elasticdb/__init__.py +7 -0
- pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
- pythonagent/agent/probes/frameworks/__init__.py +27 -0
- pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
- pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
- pythonagent/agent/probes/frameworks/aisess.py +151 -0
- pythonagent/agent/probes/frameworks/asgi.py +340 -0
- pythonagent/agent/probes/frameworks/bottle.py +27 -0
- pythonagent/agent/probes/frameworks/cherry.py +25 -0
- pythonagent/agent/probes/frameworks/django.py +128 -0
- pythonagent/agent/probes/frameworks/falcon.py +21 -0
- pythonagent/agent/probes/frameworks/fastapi.py +35 -0
- pythonagent/agent/probes/frameworks/flask.py +30 -0
- pythonagent/agent/probes/frameworks/pyramid.py +56 -0
- pythonagent/agent/probes/frameworks/test.py +108 -0
- pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
- pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
- pythonagent/agent/probes/frameworks/wsgi.py +353 -0
- pythonagent/agent/probes/grpc/__init__.py +76 -0
- pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
- pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
- pythonagent/agent/probes/havoc/__init__.py +0 -0
- pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
- pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
- pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
- pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
- pythonagent/agent/probes/http/__init__.py +49 -0
- pythonagent/agent/probes/http/aiohttp_client.py +59 -0
- pythonagent/agent/probes/http/boto.py +12 -0
- pythonagent/agent/probes/http/httplib.py +110 -0
- pythonagent/agent/probes/http/httpx_client.py +116 -0
- pythonagent/agent/probes/http/requests.py +15 -0
- pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
- pythonagent/agent/probes/http/urllib3.py +16 -0
- pythonagent/agent/probes/langchain/__init__.py +21 -0
- pythonagent/agent/probes/langchain/base_tool.py +95 -0
- pythonagent/agent/probes/langchain/langchain_community.py +136 -0
- pythonagent/agent/probes/langchain/langchain_core.py +32 -0
- pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
- pythonagent/agent/probes/logging/__init__.py +106 -0
- pythonagent/agent/probes/message_brokers/__init__.py +4 -0
- pythonagent/agent/probes/message_brokers/pika.py +126 -0
- pythonagent/agent/probes/mongodb/__init__.py +6 -0
- pythonagent/agent/probes/mongodb/pymongo.py +286 -0
- pythonagent/agent/probes/openai/__init__.py +3 -0
- pythonagent/agent/probes/openai/openai.py +797 -0
- pythonagent/agent/probes/span.py +101 -0
- pythonagent/agent/probes/sql/__init__.py +13 -0
- pythonagent/agent/probes/sql/botocores3.py +51 -0
- pythonagent/agent/probes/sql/dbapi.py +285 -0
- pythonagent/agent/probes/sql/dynamodb.py +90 -0
- pythonagent/agent/probes/sql/mysql_connector.py +24 -0
- pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
- pythonagent/agent/probes/sql/mysqldb.py +43 -0
- pythonagent/agent/probes/sql/psycopg2.py +174 -0
- pythonagent/agent/probes/sql/pymysql.py +25 -0
- pythonagent/bootstrap/__init__.py +0 -0
- pythonagent/bootstrap/cav_gunicorn.py +26 -0
- pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
- pythonagent/bootstrap/run.py +47 -0
- pythonagent/bootstrap/sitecustomize.py +287 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
- pythonagent/config.py +279 -0
- pythonagent/find.py +72 -0
- pythonagent/find_mod_cls_name.py +54 -0
- pythonagent/lang.py +131 -0
- pythonagent/lib.py +91 -0
- pythonagent/main/__init__.py +0 -0
- pythonagent/main/pytrace/__init__.py +79 -0
- pythonagent/main/pytrace/commands/__init__.py +0 -0
- pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
- pythonagent/main/pytrace/commands/run.py +401 -0
- pythonagent/main/pytrace/pytrace.py +133 -0
- pythonagent/main/wsgi.py +6 -0
- pythonagent/main.py +27 -0
- pythonagent/run.py +46 -0
- pythonagent/sqins.py +8 -0
- pythonagent/test.py +73 -0
- pythonagent/utils.py +168 -0
- pythonagent/vendor/__init__.py +0 -0
- pythonagent/vendor/pympler/__init__.py +1 -0
- pythonagent/vendor/pympler/asizeof.py +2810 -0
- pythonagent/vendor/pympler/charts.py +62 -0
- pythonagent/vendor/pympler/classtracker.py +590 -0
- pythonagent/vendor/pympler/classtracker_stats.py +780 -0
- pythonagent/vendor/pympler/garbagegraph.py +80 -0
- pythonagent/vendor/pympler/mprofile.py +97 -0
- pythonagent/vendor/pympler/muppy.py +275 -0
- pythonagent/vendor/pympler/panels.py +115 -0
- pythonagent/vendor/pympler/process.py +238 -0
- pythonagent/vendor/pympler/py.typed +0 -0
- pythonagent/vendor/pympler/refbrowser.py +451 -0
- pythonagent/vendor/pympler/refgraph.py +350 -0
- pythonagent/vendor/pympler/summary.py +321 -0
- pythonagent/vendor/pympler/tracker.py +267 -0
- pythonagent/vendor/pympler/util/__init__.py +0 -0
- pythonagent/vendor/pympler/util/bottle.py +3809 -0
- pythonagent/vendor/pympler/util/compat.py +23 -0
- pythonagent/vendor/pympler/util/stringutils.py +77 -0
- pythonagent/vendor/pympler/web.py +346 -0
- pythonagent/vendor/werkzeug/__init__.py +20 -0
- pythonagent/vendor/werkzeug/_compat.py +228 -0
- pythonagent/vendor/werkzeug/_internal.py +473 -0
- pythonagent/vendor/werkzeug/_reloader.py +341 -0
- pythonagent/vendor/werkzeug/datastructures.py +3120 -0
- pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
- pythonagent/vendor/werkzeug/debug/console.py +218 -0
- pythonagent/vendor/werkzeug/debug/repr.py +297 -0
- pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
- pythonagent/vendor/werkzeug/exceptions.py +829 -0
- pythonagent/vendor/werkzeug/filesystem.py +64 -0
- pythonagent/vendor/werkzeug/formparser.py +584 -0
- pythonagent/vendor/werkzeug/http.py +1307 -0
- pythonagent/vendor/werkzeug/local.py +420 -0
- pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
- pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
- pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
- pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
- pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
- pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
- pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
- pythonagent/vendor/werkzeug/posixemulation.py +117 -0
- pythonagent/vendor/werkzeug/routing.py +2210 -0
- pythonagent/vendor/werkzeug/security.py +249 -0
- pythonagent/vendor/werkzeug/serving.py +1117 -0
- pythonagent/vendor/werkzeug/test.py +1123 -0
- pythonagent/vendor/werkzeug/testapp.py +241 -0
- pythonagent/vendor/werkzeug/urls.py +1138 -0
- pythonagent/vendor/werkzeug/useragents.py +202 -0
- pythonagent/vendor/werkzeug/utils.py +778 -0
- pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
- pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
- pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
- pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
- pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
- pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
- pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
- pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
- pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
- pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
- pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
- pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
- pythonagent/vendor/werkzeug/wsgi.py +1000 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
import pythonagent.agent
|
|
5
|
+
from pythonagent import config
|
|
6
|
+
from ..base import EntryPointInterceptor
|
|
7
|
+
from pythonagent.utils import get_current_thread_time_in_us, get_true_false, get_current_timestamp_in_us, get_validated_duration
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ASGIInterceptor(EntryPointInterceptor):
|
|
13
|
+
|
|
14
|
+
def attach(self, application, wrapper_func=None, patched_method_name=None):
|
|
15
|
+
super(ASGIInterceptor, self).attach(application, patched_method_name='application_callable')
|
|
16
|
+
|
|
17
|
+
async def application_callable(self, application, instance, scope, receive, send):
|
|
18
|
+
# Pass lifespan and WebSocket scopes through without starting a BT.
|
|
19
|
+
# Lifespan has no path/method/headers; WebSocket BTs are not meaningful.
|
|
20
|
+
if scope.get("type") != "http":
|
|
21
|
+
return await application(instance, scope, receive, send)
|
|
22
|
+
|
|
23
|
+
bt = None
|
|
24
|
+
cpu_time_before = None
|
|
25
|
+
other_info = {}
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
try:
|
|
29
|
+
bt_name = scope.get("path") + "|" + scope.get("method")
|
|
30
|
+
other_info["path"] = scope.get("path")
|
|
31
|
+
except Exception:
|
|
32
|
+
bt_name = "/default|GET"
|
|
33
|
+
other_info["path"] = "/default"
|
|
34
|
+
|
|
35
|
+
correlation_header = ""
|
|
36
|
+
|
|
37
|
+
request_headers_list = scope.get("headers")
|
|
38
|
+
req_headers = self.parse_headers(request_headers_list, "$", "|")
|
|
39
|
+
bt_header_value = self.parse_headers(request_headers_list, "=", "&")
|
|
40
|
+
|
|
41
|
+
fp_instance = None
|
|
42
|
+
nd_cookie_value = None
|
|
43
|
+
nv_cookie_value = None
|
|
44
|
+
|
|
45
|
+
nd_cookie_key = self.agent.nd_cookie_key
|
|
46
|
+
nv_cookie_key = self.agent.nv_cookie_key
|
|
47
|
+
|
|
48
|
+
if scope.get("headers"):
|
|
49
|
+
for header_key_val in scope.get("headers"):
|
|
50
|
+
header_key = header_key_val[0].decode()
|
|
51
|
+
header_val = header_key_val[1].decode()
|
|
52
|
+
|
|
53
|
+
if header_key.lower() == 'cavndfpinstance':
|
|
54
|
+
fp_instance = header_val
|
|
55
|
+
|
|
56
|
+
if header_key.lower() == 'cookie':
|
|
57
|
+
cookie_list = header_val.split("; ")
|
|
58
|
+
for cookie in cookie_list:
|
|
59
|
+
# maxsplit=1 preserves Base64 padding (=) in cookie values
|
|
60
|
+
cookie_key_val = cookie.split("=", 1)
|
|
61
|
+
if len(cookie_key_val) < 2:
|
|
62
|
+
continue
|
|
63
|
+
cookie_key = cookie_key_val[0]
|
|
64
|
+
cookie_val = cookie_key_val[1]
|
|
65
|
+
if cookie_key == nd_cookie_key:
|
|
66
|
+
nd_cookie_value = cookie_val
|
|
67
|
+
if cookie_key == nv_cookie_key:
|
|
68
|
+
nv_cookie_value = cookie_val
|
|
69
|
+
|
|
70
|
+
cpu_time_before = get_current_thread_time_in_us()
|
|
71
|
+
bt = self.start_business_transaction(bt_name, correlation_header, nd_cookie_value, nv_cookie_value, bt_header_value, fp_instance)
|
|
72
|
+
|
|
73
|
+
# Build FQM using same approach as MethodInterceptor
|
|
74
|
+
fqmforentry = "default.default.default"
|
|
75
|
+
try:
|
|
76
|
+
app_cls = type(instance)
|
|
77
|
+
if hasattr(app_cls, "__module__"): # Class Level Method
|
|
78
|
+
fqmforentry = app_cls.__module__ + "." + app_cls.__name__ + "." + application.__name__
|
|
79
|
+
else: # Module Level Method
|
|
80
|
+
fqmforentry = app_cls.__name__ + "." + application.__name__
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.agent.logger.debug("Could not build entry_point_fqm: {}".format(e))
|
|
83
|
+
|
|
84
|
+
self.agent.http_req_resp_wrapper(bt, req_headers, "req", 200)
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
self.agent.logger.exception("Exception in Start BT for ASGI {}".format(e))
|
|
88
|
+
|
|
89
|
+
# Invoking inbound havoc failure at the entry of the instrumented method
|
|
90
|
+
# so that the transaction fails at the start and does not continue
|
|
91
|
+
# processing to capture callouts or other downstream operations.
|
|
92
|
+
try:
|
|
93
|
+
context = self.agent.get_transaction_context()
|
|
94
|
+
btname = context.btname if context else None
|
|
95
|
+
self.agent.havoc_monitor.apply_inbound_service_failure(other_info.get("path"), btname)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
if isinstance(e, NDHavocException):
|
|
98
|
+
cpu_time_after = get_current_thread_time_in_us()
|
|
99
|
+
cpu_time = cpu_time_after - (cpu_time_before or 0)
|
|
100
|
+
self.agent.set_current_status_code(503)
|
|
101
|
+
self.agent.end_business_transaction(bt, cpu_time)
|
|
102
|
+
self.agent.havoc_monitor.refresh_configs(self.agent)
|
|
103
|
+
raise e
|
|
104
|
+
else:
|
|
105
|
+
self.agent.logger.debug("Non-Havoc Exception {}".format(e))
|
|
106
|
+
|
|
107
|
+
other_info["cpu_time_before"] = cpu_time_before
|
|
108
|
+
other_info["fqmforentry"] = fqmforentry
|
|
109
|
+
# Store bt in other_info so send_wrapper uses the per-request bt instead of
|
|
110
|
+
# self.bt (which is thread-local and wrong under concurrent async requests).
|
|
111
|
+
other_info["bt"] = bt
|
|
112
|
+
|
|
113
|
+
before_time = get_current_timestamp_in_us()
|
|
114
|
+
other_info["before_time"] = before_time
|
|
115
|
+
if bt:
|
|
116
|
+
self.agent.method_entry(bt, fqmforentry, before_time)
|
|
117
|
+
try:
|
|
118
|
+
return await application(instance, scope, receive, self.wrapped_send(send, other_info))
|
|
119
|
+
except Exception as e:
|
|
120
|
+
if isinstance(e, NDHavocException):
|
|
121
|
+
try:
|
|
122
|
+
cpu_time_after = get_current_thread_time_in_us()
|
|
123
|
+
cpu_time = cpu_time_after - (cpu_time_before or 0)
|
|
124
|
+
self.agent.set_current_status_code(503)
|
|
125
|
+
fqmforentry = other_info.get("fqmforentry", "default.default.default")
|
|
126
|
+
before_time = other_info.get("before_time", 0)
|
|
127
|
+
after_time = get_current_timestamp_in_us()
|
|
128
|
+
duration = get_validated_duration(before_time, after_time, fqmforentry)
|
|
129
|
+
if bt:
|
|
130
|
+
self.agent.method_exit(bt, fqmforentry, duration, cpu_time, before_time)
|
|
131
|
+
self.agent.end_business_transaction(bt, cpu_time)
|
|
132
|
+
except Exception as cleanup_error:
|
|
133
|
+
self.agent.logger.exception(
|
|
134
|
+
"Failed to close ASGI transaction after Havoc failure: {}".format(cleanup_error)
|
|
135
|
+
)
|
|
136
|
+
raise
|
|
137
|
+
|
|
138
|
+
def wrapped_send(self, send, other_info):
|
|
139
|
+
@wraps(send)
|
|
140
|
+
async def send_wrapper(*args, **kwargs):
|
|
141
|
+
send_event_dict = args[0]
|
|
142
|
+
# Use the closed-over bt — not self.bt — to avoid thread-local races
|
|
143
|
+
# when multiple async requests share one OS thread.
|
|
144
|
+
bt = other_info.get("bt")
|
|
145
|
+
|
|
146
|
+
if send_event_dict.get('type') == 'http.response.start':
|
|
147
|
+
status = send_event_dict.get('status', 200)
|
|
148
|
+
|
|
149
|
+
# Inject session cookies before forwarding the response start event
|
|
150
|
+
try:
|
|
151
|
+
self._inject_cookies(bt, send_event_dict)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
self.agent.logger.debug("Exception injecting ASGI cookies: {}".format(e))
|
|
154
|
+
|
|
155
|
+
resp_headers = self.parse_headers(send_event_dict.get('headers', []), "$", "|")
|
|
156
|
+
self.agent.http_req_resp_wrapper(bt, resp_headers, "resp", status)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
self.handle_http_status_code(bt, status, "")
|
|
160
|
+
except Exception as e:
|
|
161
|
+
self.agent.logger.debug("Exception in handle_http_status_code for ASGI: {}".format(e))
|
|
162
|
+
|
|
163
|
+
await send(*args, **kwargs)
|
|
164
|
+
|
|
165
|
+
if send_event_dict.get("type") == "http.response.body":
|
|
166
|
+
more_body = send_event_dict.get("more_body", False)
|
|
167
|
+
if not more_body:
|
|
168
|
+
try:
|
|
169
|
+
if bt:
|
|
170
|
+
cpu_time_after = get_current_thread_time_in_us()
|
|
171
|
+
cpu_time_before = other_info.get("cpu_time_before")
|
|
172
|
+
|
|
173
|
+
if cpu_time_before:
|
|
174
|
+
cpu_time = cpu_time_after - cpu_time_before
|
|
175
|
+
else:
|
|
176
|
+
cpu_time = cpu_time_after - 0
|
|
177
|
+
|
|
178
|
+
fqmforentry = other_info.get("fqmforentry", "default.default.default")
|
|
179
|
+
before_time = other_info.get("before_time", 0)
|
|
180
|
+
after_time = get_current_timestamp_in_us()
|
|
181
|
+
duration = get_validated_duration(before_time, after_time, fqmforentry)
|
|
182
|
+
self.agent.method_exit(bt, fqmforentry, duration, cpu_time, before_time)
|
|
183
|
+
|
|
184
|
+
self.agent.end_business_transaction(bt, cpu_time)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
self.agent.logger.warning("Exception in End BT for ASGI {}".format(e))
|
|
187
|
+
|
|
188
|
+
return send_wrapper
|
|
189
|
+
|
|
190
|
+
def _inject_cookies(self, bt, send_event_dict):
|
|
191
|
+
"""Mirror WSGI cookie injection: append CavNV / X-CavNV headers to
|
|
192
|
+
an http.response.start event dict before it is forwarded to the client."""
|
|
193
|
+
nd_cookie_key = self.agent.nd_cookie_key
|
|
194
|
+
cookie_domain = self.agent.cookie_domain
|
|
195
|
+
x_cav_nv = None
|
|
196
|
+
|
|
197
|
+
# Copy headers list so the original is not mutated unexpectedly
|
|
198
|
+
response_headers = list(send_event_dict.get('headers', []))
|
|
199
|
+
|
|
200
|
+
if self.agent.header_in_response:
|
|
201
|
+
x_cav_nv = get_true_false(self.agent.header_in_response)
|
|
202
|
+
|
|
203
|
+
if x_cav_nv:
|
|
204
|
+
nd_cookie_value = self.agent.sdk_getNDSessionCookie(bt)
|
|
205
|
+
if nd_cookie_value:
|
|
206
|
+
response_headers.append((b'x-cavnv', nd_cookie_value))
|
|
207
|
+
|
|
208
|
+
if nd_cookie_key:
|
|
209
|
+
if nd_cookie_value:
|
|
210
|
+
if cookie_domain:
|
|
211
|
+
nd_cookie_str = "{}={}; Path=/; Domain={}".format(
|
|
212
|
+
nd_cookie_key, nd_cookie_value.decode(), cookie_domain)
|
|
213
|
+
else:
|
|
214
|
+
nd_cookie_str = "{}={}; Path=/".format(
|
|
215
|
+
nd_cookie_key, nd_cookie_value.decode())
|
|
216
|
+
response_headers.append((b'set-cookie', nd_cookie_str.encode()))
|
|
217
|
+
else:
|
|
218
|
+
if self.agent.disable_nd_nv:
|
|
219
|
+
if cookie_domain:
|
|
220
|
+
nd_cookie_str = "CavNV=None; Max-Age=0; Path=/; Domain={}".format(cookie_domain)
|
|
221
|
+
else:
|
|
222
|
+
nd_cookie_str = "CavNV=None; Max-Age=0; Path=/"
|
|
223
|
+
response_headers.append((b'set-cookie', nd_cookie_str.encode()))
|
|
224
|
+
self.agent.disable_nd_nv = False
|
|
225
|
+
else:
|
|
226
|
+
if nd_cookie_key:
|
|
227
|
+
nd_cookie_value = self.agent.sdk_getNDSessionCookie(bt)
|
|
228
|
+
if nd_cookie_value:
|
|
229
|
+
if cookie_domain:
|
|
230
|
+
nd_cookie_str = "{}={}; Path=/; Domain={}".format(
|
|
231
|
+
nd_cookie_key, nd_cookie_value.decode(), cookie_domain)
|
|
232
|
+
else:
|
|
233
|
+
nd_cookie_str = "{}={}; Path=/".format(
|
|
234
|
+
nd_cookie_key, nd_cookie_value.decode())
|
|
235
|
+
response_headers.append((b'set-cookie', nd_cookie_str.encode()))
|
|
236
|
+
else:
|
|
237
|
+
if self.agent.disable_nd_nv:
|
|
238
|
+
if cookie_domain:
|
|
239
|
+
nd_cookie_str = "CavNV=None; Max-Age=0; Path=/; Domain={}".format(cookie_domain)
|
|
240
|
+
else:
|
|
241
|
+
nd_cookie_str = "CavNV=None; Max-Age=0; Path=/"
|
|
242
|
+
response_headers.append((b'set-cookie', nd_cookie_str.encode()))
|
|
243
|
+
self.agent.disable_nd_nv = False
|
|
244
|
+
|
|
245
|
+
send_event_dict['headers'] = response_headers
|
|
246
|
+
|
|
247
|
+
def parse_headers(self, input_list, key_val_sep, item_value_sep):
|
|
248
|
+
output_str = ""
|
|
249
|
+
if input_list:
|
|
250
|
+
for key, value in input_list:
|
|
251
|
+
key = key.decode()
|
|
252
|
+
value = value.decode()
|
|
253
|
+
value = value.replace("|", "%7C") # CavSF has | in value
|
|
254
|
+
value = value.replace(":", "%3A") # Host as : in value
|
|
255
|
+
value = value.replace("=", "%3D") # Accept, Accept-Language, and ND-NV cookies have = in value
|
|
256
|
+
new_str = key + key_val_sep + value + item_value_sep
|
|
257
|
+
output_str += new_str
|
|
258
|
+
output_str = output_str[:-1] # Remove last |
|
|
259
|
+
return output_str
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class ASGIMiddleware:
|
|
263
|
+
"""Generic ASGI middleware that instruments any ASGI callable for transaction capture.
|
|
264
|
+
|
|
265
|
+
Analogous to WSGIMiddleware in wsgi.py. Supports two usage modes:
|
|
266
|
+
1. Wrap an existing app directly: ASGIMiddleware(app)
|
|
267
|
+
2. Load app from config: set ASGI_MODULE env var, use module-level `application` singleton
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
def __init__(self, application=None):
|
|
271
|
+
self._application = application
|
|
272
|
+
self._interceptor = None # lazy — created on first request to avoid circular import
|
|
273
|
+
self._load_application_lock = threading.Lock()
|
|
274
|
+
|
|
275
|
+
def _load_application(self):
|
|
276
|
+
asgi_callable = config.ASGI_CALLABLE_OBJECT or 'application'
|
|
277
|
+
module_str = os.environ.get('ASGI_MODULE') or config.ASGI_MODULE
|
|
278
|
+
|
|
279
|
+
if not module_str:
|
|
280
|
+
raise AttributeError(
|
|
281
|
+
'Cannot load ASGI application: set ASGI_MODULE '
|
|
282
|
+
'(e.g. myapp.asgi:application).')
|
|
283
|
+
|
|
284
|
+
if ':' in module_str:
|
|
285
|
+
module_str, asgi_callable = module_str.split(':', 1)
|
|
286
|
+
|
|
287
|
+
__import__(module_str)
|
|
288
|
+
asgi_module = sys.modules[module_str]
|
|
289
|
+
|
|
290
|
+
if asgi_callable.endswith('()'):
|
|
291
|
+
app = getattr(asgi_module, asgi_callable[:-2])()
|
|
292
|
+
else:
|
|
293
|
+
app = getattr(asgi_module, asgi_callable)
|
|
294
|
+
|
|
295
|
+
self._application = app
|
|
296
|
+
|
|
297
|
+
async def asgi_application(self, scope, receive, send):
|
|
298
|
+
return await self._application(scope, receive, send)
|
|
299
|
+
|
|
300
|
+
async def __call__(self, scope, receive, send):
|
|
301
|
+
if self._interceptor is None:
|
|
302
|
+
self._interceptor = ASGIInterceptor(pythonagent.agent.get_agent_instance(), None)
|
|
303
|
+
if not self._application:
|
|
304
|
+
with self._load_application_lock:
|
|
305
|
+
if not self._application:
|
|
306
|
+
self._load_application()
|
|
307
|
+
|
|
308
|
+
return await self._interceptor.application_callable(
|
|
309
|
+
ASGIMiddleware.asgi_application, self, scope, receive, send
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# Module-level singleton — allows: uvicorn pythonagent.agent.probes.frameworks.asgi:application
|
|
314
|
+
application = ASGIMiddleware()
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def intercept_uvicorn_config(agent, mod):
|
|
318
|
+
"""Patch uvicorn Config.load to auto-wrap any ASGI app with our interceptor.
|
|
319
|
+
|
|
320
|
+
Fires when uvicorn.config is imported. After Config.load() runs, self.loaded_app
|
|
321
|
+
is the real ASGI callable. We wrap it with ASGIMiddleware so all HTTP requests
|
|
322
|
+
go through our instrumentation regardless of which ASGI framework is used.
|
|
323
|
+
|
|
324
|
+
Frameworks already instrumented at method level (Django ASGI, FastAPI/Starlette)
|
|
325
|
+
have _cav_asgi_patched set on their base class and are skipped to avoid double BTs.
|
|
326
|
+
"""
|
|
327
|
+
original_load = mod.Config.load
|
|
328
|
+
|
|
329
|
+
def patched_load(self):
|
|
330
|
+
original_load(self)
|
|
331
|
+
loaded = getattr(self, 'loaded_app', None)
|
|
332
|
+
if loaded is None or isinstance(loaded, ASGIMiddleware):
|
|
333
|
+
return
|
|
334
|
+
# Skip if the app's class hierarchy has already been instrumented at method level.
|
|
335
|
+
for klass in type(loaded).__mro__:
|
|
336
|
+
if klass.__dict__.get('_cav_asgi_patched'):
|
|
337
|
+
return
|
|
338
|
+
self.loaded_app = ASGIMiddleware(loaded)
|
|
339
|
+
|
|
340
|
+
mod.Config.load = patched_load
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Interceptor for Bottle.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
8
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BottleInterceptor(BaseInterceptor):
|
|
12
|
+
def add_exception(self, func, *args, **kwargs):
|
|
13
|
+
self.agent.logger.info('Modulename: BottleInterceptor class')
|
|
14
|
+
with self.log_exceptions():
|
|
15
|
+
bt = self.bt
|
|
16
|
+
self.agent.logger.info("Modulename: BottleInterceptor class inside add_exception function bt value is :{0}".format(bt))
|
|
17
|
+
if bt:
|
|
18
|
+
bt.add_exception(*sys.exc_info())
|
|
19
|
+
return func(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def intercept_bottle(agent, mod):
|
|
24
|
+
agent.logger.warning("Instrument module: bottle{}".format(", mod: {}".format(mod) if mod else ""))
|
|
25
|
+
WSGIInterceptor(agent, mod.Bottle).attach('__call__')
|
|
26
|
+
#print("framework.bottle.intercept_bottle: intercepting bottle ......")
|
|
27
|
+
BottleInterceptor(agent, mod.HTTPError).attach('__init__', patched_method_name='add_exception')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Interceptor for Cherry framework.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import unicode_literals
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
8
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
9
|
+
|
|
10
|
+
class CherrypyExceptionAdder(BaseInterceptor):
|
|
11
|
+
def add_exception(self, func, *args, **kwargs):
|
|
12
|
+
with self.log_exceptions():
|
|
13
|
+
bt = self.bt
|
|
14
|
+
self.agent.logger.info("Modulename: cherry exception class || bt value is {0} :".format(bt))
|
|
15
|
+
if bt:
|
|
16
|
+
bt.add_exception(*sys.exc_info())
|
|
17
|
+
self.agent.logger.debug("Exception occured as !!! {0}".format(sys.exc_info()))
|
|
18
|
+
return func(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def intercept_cherrypy(agent, mod):
|
|
22
|
+
agent.logger.warning("Instrument module: cherrypy{}".format(", mod: {}".format(mod) if mod else ""))
|
|
23
|
+
WSGIInterceptor(agent, mod.Application).attach('__call__')
|
|
24
|
+
CherrypyExceptionAdder(agent, mod._cprequest.Request).attach('handle_error', patched_method_name='add_exception')
|
|
25
|
+
CherrypyExceptionAdder(agent, mod.HTTPError).attach('set_response', patched_method_name='add_exception')
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Interceptor for Django.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
10
|
+
from pythonagent.agent.probes.frameworks.asgi import ASGIInterceptor
|
|
11
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
12
|
+
import logging
|
|
13
|
+
#from django.conf import settings
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_exception(interceptor, exc_info):
|
|
18
|
+
with interceptor.log_exceptions():
|
|
19
|
+
bt = interceptor.bt
|
|
20
|
+
if bt:
|
|
21
|
+
bt.add_exception(*exc_info)
|
|
22
|
+
|
|
23
|
+
#from django.utils.deprecation import MiddlewareMixin
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DjangoBaseHandlerInterceptor(BaseInterceptor):
|
|
29
|
+
def _load_middleware(self, load_middleware, base_handler, **kwargs):
|
|
30
|
+
self.agent.logger.info("Loading middleware of django !")
|
|
31
|
+
self.agent.logger.info('Modulename: DjangoBaseHandlerInterceptor class inside _load_middleware function : loading middleware of django !')
|
|
32
|
+
#settings.MIDDLEWARE.append('agent.my_middleware.CustomMiddleware')
|
|
33
|
+
load_middleware(base_handler, **kwargs)
|
|
34
|
+
|
|
35
|
+
if hasattr(base_handler, '_exception_middleware'):
|
|
36
|
+
base_handler._exception_middleware.insert(0, pythonagentDjangoMiddleware(self).process_exception)
|
|
37
|
+
#base_handler._request_middleware.insert(0, pythonagentDjangoMiddleware(self).process_request)
|
|
38
|
+
#base_handler._response_middleware.insert(0, pythonagentDjangoMiddleware(self).process_response)
|
|
39
|
+
#base_handler._template_response_middleware.insert(0, pythonagentDjangoMiddleware(self).process_template_response)
|
|
40
|
+
#base_handler._view_middleware.insert(0, pythonagentDjangoMiddleware(self).process_view)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _handle_uncaught_exception(self, handle_uncaught_exception, base_handler, request, resolver, exc_info):
|
|
45
|
+
add_exception(self, exc_info)
|
|
46
|
+
return handle_uncaught_exception(base_handler, request, resolver, exc_info)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DjangoExceptionInterceptor(BaseInterceptor):
|
|
50
|
+
def _handle_uncaught_exception(self, handle_uncaught_exception, request, resolver, exc_info):
|
|
51
|
+
add_exception(self, exc_info)
|
|
52
|
+
return handle_uncaught_exception(request, resolver, exc_info)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class pythonagentDjangoMiddleware(object):
|
|
58
|
+
def __init__(self, interceptor):
|
|
59
|
+
self.interceptor = interceptor
|
|
60
|
+
self.logger = logging.getLogger('pythonagent.agent')
|
|
61
|
+
|
|
62
|
+
def process_exception(self, request, exception):
|
|
63
|
+
add_exception(self.interceptor, sys.exc_info())
|
|
64
|
+
#print("Django application process_request called and request as= ", request)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def intercept_django_wsgi_handler(agent, mod):
|
|
68
|
+
agent.logger.warning("Instrument module: django.core.handlers.wsgi{}".format(", mod: {}".format(mod) if mod else ""))
|
|
69
|
+
WSGIInterceptor(agent, mod.WSGIHandler).attach('__call__')
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def intercept_django_asgi_handler(agent, mod):
|
|
73
|
+
agent.logger.warning("Instrument module: django.core.handlers.asgi{}".format(", mod: {}".format(mod) if mod else ""))
|
|
74
|
+
# Patch base class handle()
|
|
75
|
+
ASGIInterceptor(agent, mod.ASGIHandler).attach('handle')
|
|
76
|
+
|
|
77
|
+
# Patch a concrete subclass if it overrides handle or __call__ in its own __dict__.
|
|
78
|
+
# This is needed because Python MRO resolves the subclass method first, bypassing
|
|
79
|
+
# any patch we placed on the base class (e.g. Saleor's PatchedASGIHandler overrides
|
|
80
|
+
# handle() to fix a Django memory leak and its own handle shadows our base-class patch).
|
|
81
|
+
def _patch_subclass(cls):
|
|
82
|
+
if 'handle' in cls.__dict__:
|
|
83
|
+
ASGIInterceptor(agent, cls).attach('handle')
|
|
84
|
+
if '__call__' in cls.__dict__:
|
|
85
|
+
ASGIInterceptor(agent, cls).attach('__call__')
|
|
86
|
+
|
|
87
|
+
# Patch any ASGIHandler subclasses already defined when the hook fires
|
|
88
|
+
# (covers the late-bootstrap case where sys.modules already has the module).
|
|
89
|
+
queue = list(mod.ASGIHandler.__subclasses__())
|
|
90
|
+
while queue:
|
|
91
|
+
subclass = queue.pop(0)
|
|
92
|
+
_patch_subclass(subclass)
|
|
93
|
+
queue.extend(subclass.__subclasses__())
|
|
94
|
+
|
|
95
|
+
# Install __init_subclass__ hook so subclasses defined AFTER this hook fires
|
|
96
|
+
# (the common case: Saleor imports django.core.handlers.asgi first, then
|
|
97
|
+
# defines PatchedASGIHandler) are automatically patched at class-definition time.
|
|
98
|
+
_original_init_subclass = mod.ASGIHandler.__dict__.get('__init_subclass__', None)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def _auto_patch_subclass(cls, **kwargs):
|
|
102
|
+
if _original_init_subclass is not None:
|
|
103
|
+
_original_init_subclass.__func__(cls, **kwargs)
|
|
104
|
+
else:
|
|
105
|
+
super(mod.ASGIHandler, cls).__init_subclass__(**kwargs)
|
|
106
|
+
_patch_subclass(cls)
|
|
107
|
+
|
|
108
|
+
mod.ASGIHandler.__init_subclass__ = _auto_patch_subclass
|
|
109
|
+
|
|
110
|
+
# Prevent the generic uvicorn.config hook from double-wrapping Django ASGI apps.
|
|
111
|
+
# The hook checks _cav_asgi_patched on the class MRO before adding ASGIMiddleware.
|
|
112
|
+
mod.ASGIHandler._cav_asgi_patched = True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def intercept_django_base_handler(agent, mod):
|
|
116
|
+
agent.logger.warning("Instrument module: django.core.handlers.base{}".format(", mod: {}".format(mod) if mod else ""))
|
|
117
|
+
base_handler_methods = ['load_middleware']
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
import django.core.handlers.exception
|
|
121
|
+
if hasattr(django.core.handlers.exception, 'handle_uncaught_exception'):
|
|
122
|
+
DjangoExceptionInterceptor(agent, django.core.handlers.exception).attach('handle_uncaught_exception')
|
|
123
|
+
else:
|
|
124
|
+
base_handler_methods.append('handle_uncaught_exception')
|
|
125
|
+
except ImportError:
|
|
126
|
+
base_handler_methods.append('handle_uncaught_exception')
|
|
127
|
+
|
|
128
|
+
DjangoBaseHandlerInterceptor(agent, mod.BaseHandler).attach(base_handler_methods)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
6
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
7
|
+
|
|
8
|
+
class FalconExceptionAdder(BaseInterceptor):
|
|
9
|
+
def add_exception(self, func, *args, **kwargs):
|
|
10
|
+
with self.log_exceptions():
|
|
11
|
+
bt = self.bt
|
|
12
|
+
if bt:
|
|
13
|
+
bt.add_exception(*sys.exc_info())
|
|
14
|
+
return func(*args, **kwargs)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def intercept_falcon(agent, mod):
|
|
18
|
+
agent.logger.warning("Instrument module: falcon{}".format(", mod: {}".format(mod) if mod else ""))
|
|
19
|
+
WSGIInterceptor(agent, mod.App).attach('__call__')
|
|
20
|
+
FalconExceptionAdder(agent, mod.App).attach('_handle_exception', patched_method_name='add_exception')
|
|
21
|
+
#FalconExceptionAdder(agent, mod.Request).attach('set_response', patched_method_name='add_exception')
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
"""Interceptor for FastApi framework.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from pythonagent.agent.probes.frameworks.asgi import ASGIInterceptor
|
|
10
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FastapiInterceptor(BaseInterceptor):
|
|
14
|
+
def _run_endpoint_function(self, run_endpoint_function):
|
|
15
|
+
with self.log_exceptions():
|
|
16
|
+
bt = self.bt
|
|
17
|
+
# self.agent.logger.info("Modulename: FlaskInterceptor class || bt value is {0} :".format(bt))
|
|
18
|
+
if bt:
|
|
19
|
+
pass
|
|
20
|
+
return run_endpoint_function
|
|
21
|
+
|
|
22
|
+
def intercept_fastapi_asgi_handler(agent, mod):
|
|
23
|
+
agent.logger.warning("Instrument module: starlette.routing{}".format(", mod: {}".format(mod) if mod else ""))
|
|
24
|
+
ASGIInterceptor(agent, mod.Router).attach('__call__')
|
|
25
|
+
#ASGIInterceptor(agent, mod.Router).attach('app')
|
|
26
|
+
|
|
27
|
+
# Prevent the generic uvicorn.config hook from double-wrapping FastAPI/Starlette apps.
|
|
28
|
+
mod.Router._cav_asgi_patched = True
|
|
29
|
+
try:
|
|
30
|
+
import starlette.applications
|
|
31
|
+
starlette.applications.Starlette._cav_asgi_patched = True
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
"""Interceptor for Flask framework.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
10
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FlaskInterceptor(BaseInterceptor):
|
|
14
|
+
def _handle_user_exception(self, handle_user_exception, flask, e):
|
|
15
|
+
self.agent.logger.info('Modulename: FlaskInterceptor class')
|
|
16
|
+
with self.log_exceptions():
|
|
17
|
+
bt = self.bt
|
|
18
|
+
self.agent.logger.info("Modulename: FlaskInterceptor class || bt value is {0} :".format(bt))
|
|
19
|
+
if bt:
|
|
20
|
+
#bt.add_exception(*sys.exc_info())
|
|
21
|
+
self.agent.logger.debug("Exception occured as !!! {0}".format(sys.exc_info()))
|
|
22
|
+
#print('returning from _handle method !!')
|
|
23
|
+
return handle_user_exception(flask, e)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def intercept_flask(agent, mod):
|
|
27
|
+
#print('=============inside flask instrumentation==========')
|
|
28
|
+
agent.logger.warning("Instrument module: flask{}".format(", mod: {}".format(mod) if mod else ""))
|
|
29
|
+
WSGIInterceptor(agent, mod.Flask).attach('wsgi_app')
|
|
30
|
+
FlaskInterceptor(agent, mod.Flask).attach('handle_user_exception')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Interceptor for Pyramid.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from pythonagent.agent.probes.frameworks.wsgi import WSGIInterceptor
|
|
10
|
+
from pythonagent.agent.probes.base import BaseInterceptor
|
|
11
|
+
|
|
12
|
+
class PyramidRouterInterceptor(BaseInterceptor):
|
|
13
|
+
"""
|
|
14
|
+
Pyramid's interceptor class
|
|
15
|
+
"""
|
|
16
|
+
def _handle_request(self, handle_request, router_instance, req):
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
handle_request: original method pyramid.router.Router.handle_request
|
|
22
|
+
that is being overridden
|
|
23
|
+
router_instance: pyramid.router.Router instance
|
|
24
|
+
req: request object
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
Whatever pyramid.router.Router.handle_request returns
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
return handle_request(router_instance, req)
|
|
33
|
+
except Exception:
|
|
34
|
+
with self.log_exceptions():
|
|
35
|
+
bt = self.bt
|
|
36
|
+
if bt:
|
|
37
|
+
bt.add_exception(*sys.exc_info())
|
|
38
|
+
raise
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def intercept_pyramid(agent, mod):
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
|
|
47
|
+
mod: pyramid.router module
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
None
|
|
52
|
+
"""
|
|
53
|
+
#mod = '/home/cavisson/shop/my-shop/env/env/lib/python3.7/site-packages/pyramid/router.py'
|
|
54
|
+
agent.logger.warning("Instrument module: pyramid{}".format(", mod: {}".format(mod) if mod else ""))
|
|
55
|
+
WSGIInterceptor(agent, mod.Router).attach('__call__')
|
|
56
|
+
PyramidRouterInterceptor(agent, mod.Router).attach('handle_request')
|