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.
Files changed (180) hide show
  1. cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
  2. cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
  3. cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
  4. cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
  5. cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
  6. pythonagent/__init__.py +22 -0
  7. pythonagent/agent/__init__.py +219 -0
  8. pythonagent/agent/internal/__init__.py +1 -0
  9. pythonagent/agent/internal/agent.py +1651 -0
  10. pythonagent/agent/internal/framesinfo.py +152 -0
  11. pythonagent/agent/internal/heap_dump.py +39 -0
  12. pythonagent/agent/internal/intercept_module.py +70 -0
  13. pythonagent/agent/internal/logs.py +122 -0
  14. pythonagent/agent/internal/metadata/__init__.py +0 -0
  15. pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
  16. pythonagent/agent/internal/proc_compat.py +75 -0
  17. pythonagent/agent/internal/profile.py +47 -0
  18. pythonagent/agent/internal/provider.py +83 -0
  19. pythonagent/agent/internal/thread_dump.py +85 -0
  20. pythonagent/agent/internal/udp.py +245 -0
  21. pythonagent/agent/internal/udp_message.py +800 -0
  22. pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
  23. pythonagent/agent/probes/Instrumentation/find.py +243 -0
  24. pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
  25. pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
  26. pythonagent/agent/probes/Instrumentation/parser.py +129 -0
  27. pythonagent/agent/probes/__init__.py +219 -0
  28. pythonagent/agent/probes/base.py +303 -0
  29. pythonagent/agent/probes/cache/__init__.py +51 -0
  30. pythonagent/agent/probes/cache/redis.py +119 -0
  31. pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
  32. pythonagent/agent/probes/coroutines/__init__.py +1 -0
  33. pythonagent/agent/probes/coroutines/asyncio.py +63 -0
  34. pythonagent/agent/probes/elasticdb/__init__.py +7 -0
  35. pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
  36. pythonagent/agent/probes/frameworks/__init__.py +27 -0
  37. pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
  38. pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
  39. pythonagent/agent/probes/frameworks/aisess.py +151 -0
  40. pythonagent/agent/probes/frameworks/asgi.py +340 -0
  41. pythonagent/agent/probes/frameworks/bottle.py +27 -0
  42. pythonagent/agent/probes/frameworks/cherry.py +25 -0
  43. pythonagent/agent/probes/frameworks/django.py +128 -0
  44. pythonagent/agent/probes/frameworks/falcon.py +21 -0
  45. pythonagent/agent/probes/frameworks/fastapi.py +35 -0
  46. pythonagent/agent/probes/frameworks/flask.py +30 -0
  47. pythonagent/agent/probes/frameworks/pyramid.py +56 -0
  48. pythonagent/agent/probes/frameworks/test.py +108 -0
  49. pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
  50. pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
  51. pythonagent/agent/probes/frameworks/wsgi.py +353 -0
  52. pythonagent/agent/probes/grpc/__init__.py +76 -0
  53. pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
  54. pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
  55. pythonagent/agent/probes/havoc/__init__.py +0 -0
  56. pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
  57. pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
  58. pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
  59. pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
  60. pythonagent/agent/probes/http/__init__.py +49 -0
  61. pythonagent/agent/probes/http/aiohttp_client.py +59 -0
  62. pythonagent/agent/probes/http/boto.py +12 -0
  63. pythonagent/agent/probes/http/httplib.py +110 -0
  64. pythonagent/agent/probes/http/httpx_client.py +116 -0
  65. pythonagent/agent/probes/http/requests.py +15 -0
  66. pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
  67. pythonagent/agent/probes/http/urllib3.py +16 -0
  68. pythonagent/agent/probes/langchain/__init__.py +21 -0
  69. pythonagent/agent/probes/langchain/base_tool.py +95 -0
  70. pythonagent/agent/probes/langchain/langchain_community.py +136 -0
  71. pythonagent/agent/probes/langchain/langchain_core.py +32 -0
  72. pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
  73. pythonagent/agent/probes/logging/__init__.py +106 -0
  74. pythonagent/agent/probes/message_brokers/__init__.py +4 -0
  75. pythonagent/agent/probes/message_brokers/pika.py +126 -0
  76. pythonagent/agent/probes/mongodb/__init__.py +6 -0
  77. pythonagent/agent/probes/mongodb/pymongo.py +286 -0
  78. pythonagent/agent/probes/openai/__init__.py +3 -0
  79. pythonagent/agent/probes/openai/openai.py +797 -0
  80. pythonagent/agent/probes/span.py +101 -0
  81. pythonagent/agent/probes/sql/__init__.py +13 -0
  82. pythonagent/agent/probes/sql/botocores3.py +51 -0
  83. pythonagent/agent/probes/sql/dbapi.py +285 -0
  84. pythonagent/agent/probes/sql/dynamodb.py +90 -0
  85. pythonagent/agent/probes/sql/mysql_connector.py +24 -0
  86. pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
  87. pythonagent/agent/probes/sql/mysqldb.py +43 -0
  88. pythonagent/agent/probes/sql/psycopg2.py +174 -0
  89. pythonagent/agent/probes/sql/pymysql.py +25 -0
  90. pythonagent/bootstrap/__init__.py +0 -0
  91. pythonagent/bootstrap/cav_gunicorn.py +26 -0
  92. pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
  93. pythonagent/bootstrap/run.py +47 -0
  94. pythonagent/bootstrap/sitecustomize.py +287 -0
  95. pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
  96. pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
  97. pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
  98. pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
  99. pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
  100. pythonagent/config.py +279 -0
  101. pythonagent/find.py +72 -0
  102. pythonagent/find_mod_cls_name.py +54 -0
  103. pythonagent/lang.py +131 -0
  104. pythonagent/lib.py +91 -0
  105. pythonagent/main/__init__.py +0 -0
  106. pythonagent/main/pytrace/__init__.py +79 -0
  107. pythonagent/main/pytrace/commands/__init__.py +0 -0
  108. pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
  109. pythonagent/main/pytrace/commands/run.py +401 -0
  110. pythonagent/main/pytrace/pytrace.py +133 -0
  111. pythonagent/main/wsgi.py +6 -0
  112. pythonagent/main.py +27 -0
  113. pythonagent/run.py +46 -0
  114. pythonagent/sqins.py +8 -0
  115. pythonagent/test.py +73 -0
  116. pythonagent/utils.py +168 -0
  117. pythonagent/vendor/__init__.py +0 -0
  118. pythonagent/vendor/pympler/__init__.py +1 -0
  119. pythonagent/vendor/pympler/asizeof.py +2810 -0
  120. pythonagent/vendor/pympler/charts.py +62 -0
  121. pythonagent/vendor/pympler/classtracker.py +590 -0
  122. pythonagent/vendor/pympler/classtracker_stats.py +780 -0
  123. pythonagent/vendor/pympler/garbagegraph.py +80 -0
  124. pythonagent/vendor/pympler/mprofile.py +97 -0
  125. pythonagent/vendor/pympler/muppy.py +275 -0
  126. pythonagent/vendor/pympler/panels.py +115 -0
  127. pythonagent/vendor/pympler/process.py +238 -0
  128. pythonagent/vendor/pympler/py.typed +0 -0
  129. pythonagent/vendor/pympler/refbrowser.py +451 -0
  130. pythonagent/vendor/pympler/refgraph.py +350 -0
  131. pythonagent/vendor/pympler/summary.py +321 -0
  132. pythonagent/vendor/pympler/tracker.py +267 -0
  133. pythonagent/vendor/pympler/util/__init__.py +0 -0
  134. pythonagent/vendor/pympler/util/bottle.py +3809 -0
  135. pythonagent/vendor/pympler/util/compat.py +23 -0
  136. pythonagent/vendor/pympler/util/stringutils.py +77 -0
  137. pythonagent/vendor/pympler/web.py +346 -0
  138. pythonagent/vendor/werkzeug/__init__.py +20 -0
  139. pythonagent/vendor/werkzeug/_compat.py +228 -0
  140. pythonagent/vendor/werkzeug/_internal.py +473 -0
  141. pythonagent/vendor/werkzeug/_reloader.py +341 -0
  142. pythonagent/vendor/werkzeug/datastructures.py +3120 -0
  143. pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
  144. pythonagent/vendor/werkzeug/debug/console.py +218 -0
  145. pythonagent/vendor/werkzeug/debug/repr.py +297 -0
  146. pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
  147. pythonagent/vendor/werkzeug/exceptions.py +829 -0
  148. pythonagent/vendor/werkzeug/filesystem.py +64 -0
  149. pythonagent/vendor/werkzeug/formparser.py +584 -0
  150. pythonagent/vendor/werkzeug/http.py +1307 -0
  151. pythonagent/vendor/werkzeug/local.py +420 -0
  152. pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
  153. pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
  154. pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
  155. pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
  156. pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
  157. pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
  158. pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
  159. pythonagent/vendor/werkzeug/posixemulation.py +117 -0
  160. pythonagent/vendor/werkzeug/routing.py +2210 -0
  161. pythonagent/vendor/werkzeug/security.py +249 -0
  162. pythonagent/vendor/werkzeug/serving.py +1117 -0
  163. pythonagent/vendor/werkzeug/test.py +1123 -0
  164. pythonagent/vendor/werkzeug/testapp.py +241 -0
  165. pythonagent/vendor/werkzeug/urls.py +1138 -0
  166. pythonagent/vendor/werkzeug/useragents.py +202 -0
  167. pythonagent/vendor/werkzeug/utils.py +778 -0
  168. pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
  169. pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
  170. pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
  171. pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
  172. pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
  173. pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
  174. pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
  175. pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
  176. pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
  177. pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
  178. pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
  179. pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
  180. pythonagent/vendor/werkzeug/wsgi.py +1000 -0
@@ -0,0 +1,303 @@
1
+
2
+
3
+ """Definition of base entry and exit point interceptors.
4
+
5
+ """
6
+
7
+
8
+ import sys
9
+ import types
10
+ import inspect
11
+ from functools import wraps
12
+ from contextlib import contextmanager
13
+ import time
14
+ from pythonagent.utils import get_current_timestamp_in_us
15
+
16
+
17
+ class BaseInterceptor(object):
18
+ def __init__(self, agent, cls):
19
+ self.agent = agent
20
+ self.cls = cls
21
+
22
+ @property
23
+ def bt(self):
24
+ # add a docstring here about what current bt means now.
25
+ return self.agent.get_current_bt()
26
+
27
+ def __setitem__(self, key, value):
28
+ bt = self.bt
29
+ if bt:
30
+ bt._properties[key] = value
31
+
32
+ def __getitem__(self, key):
33
+ bt = self.bt
34
+ if bt:
35
+ return bt._properties.get(key)
36
+
37
+ def __delitem__(self, key):
38
+ bt = self.bt
39
+ if bt:
40
+ bt._properties.pop(key, None)
41
+
42
+
43
+ @staticmethod
44
+ def _fix_dunder_method_name(method, class_name):
45
+ # If `method` starts with '__', then it will have been renamed by the lexer to '_SomeClass__some_method'
46
+ # (unless the method name ends with '__').
47
+ if method.startswith('__') and not method.endswith('__'):
48
+ method = '_' + class_name.lstrip('_') + method
49
+ return method
50
+
51
+ def _attach(self, method, wrapper_func, patched_method_name):
52
+ patched_method_name = patched_method_name or '_' + method
53
+
54
+ # Deal with reserved identifiers.
55
+ # https://docs.python.org/2/reference/lexical_analysis.html#reserved-classes-of-identifiers
56
+ # method = self._fix_dunder_method_name(method, self.cls.__name__)
57
+ # patched_method_name = self._fix_dunder_method_name(patched_method_name, self.__class__.__name__)
58
+
59
+ # Skip classmethods and staticmethods: replacing the descriptor with a
60
+ # plain function via setattr breaks subclass cls-binding for classmethods
61
+ # (the captured bound-to-base version is called instead of bound-to-subclass)
62
+ # and breaks staticmethods (they receive 'self' unexpectedly).
63
+ # Modules have no __mro__; skip the walk for module-level functions
64
+ # (module attrs can never be classmethod/staticmethod descriptors).
65
+ mro = getattr(self.cls, '__mro__', None)
66
+ method = self._fix_dunder_method_name(method, self.cls.__name__)
67
+ if mro is not None:
68
+ for klass in mro:
69
+ raw = vars(klass).get(method)
70
+ if raw is not None:
71
+ if isinstance(raw, (classmethod, staticmethod)):
72
+ return
73
+ break # found as a plain function/descriptor, safe to wrap
74
+
75
+ patched_method_name = self._fix_dunder_method_name(patched_method_name, self.__class__.__name__)
76
+
77
+ # Wrap the original method if required.
78
+ original_method = getattr(self.cls, method)
79
+
80
+ if not isinstance(original_method, types.FunctionType) and not isinstance(original_method, types.MethodType):
81
+ return
82
+
83
+ # Do not intercept the same method more than once.
84
+ if hasattr(original_method, '_pythonagent_intercepted'):
85
+ return
86
+
87
+ if wrapper_func:
88
+ @wraps(original_method)
89
+ def wrapped_method(*args, **kwargs):
90
+ return wrapper_func(original_method, *args, **kwargs)
91
+ real_method = wrapped_method
92
+ else:
93
+ real_method = original_method
94
+
95
+ # Replace `self.cls.method` with a call to the patched method.
96
+ patched_method = getattr(self, patched_method_name)
97
+
98
+ if inspect.iscoroutinefunction(patched_method):
99
+ @wraps(original_method)
100
+ async def call_patched_method(*args, **kwargs):
101
+ return await patched_method(real_method, *args, **kwargs)
102
+ else:
103
+ @wraps(original_method)
104
+ def call_patched_method(*args, **kwargs):
105
+ return patched_method(real_method, *args, **kwargs)
106
+
107
+ call_patched_method._pythonagent_intercepted = True
108
+
109
+ setattr(self.cls, method, call_patched_method)
110
+
111
+ def attach(self, method_or_methods, wrapper_func=None, patched_method_name=None):
112
+ if not isinstance(method_or_methods, list):
113
+ method_or_methods = [method_or_methods]
114
+ for method in method_or_methods:
115
+ self._attach(method, wrapper_func, patched_method_name)
116
+
117
+ def log_exception(self, level=1):
118
+ self.agent.logger.exception('Exception in {klass}.{function}.'.format(klass=self.__class__.__name__, function=level))
119
+
120
+ @contextmanager
121
+ def log_exceptions(self):
122
+ try:
123
+ yield
124
+ except Exception as e:
125
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
126
+ if isinstance(e, NDHavocException):
127
+ raise e
128
+ self.log_exception(level=3)
129
+
130
+
131
+ NO_WRAPPER = object()
132
+
133
+
134
+ class ExitCallInterceptor(BaseInterceptor):
135
+ def attach(self, method_or_methods, wrapper_func=NO_WRAPPER, patched_method_name=None):
136
+ if wrapper_func is NO_WRAPPER:
137
+ wrapper_func = self.run
138
+ super(ExitCallInterceptor, self).attach(method_or_methods, wrapper_func=wrapper_func,
139
+ patched_method_name=patched_method_name)
140
+
141
+ def make_correlation_header(self, exit_call):
142
+ header = None
143
+ if exit_call and header is not None:
144
+ exit_call.optional_properties['CorrelationHeader'] = header[1]
145
+ return header
146
+
147
+ def http_call_begin(self, bt, http_host, url, method_name, start_time, backend_name=None, callout_id=0):
148
+ """Start an exit call."""
149
+
150
+ exit_call = None
151
+ backend_name = backend_name or http_host
152
+ with self.log_exceptions():
153
+ self.agent.logger.debug("http call begin bt {} http_host {} url {} method_name {} current_time {}".format(bt, http_host, url, method_name, start_time))
154
+ exit_call= self.agent.http_call_begin(bt, http_host, url, method_name, start_time) # Native
155
+ try:
156
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
157
+ backend_name_value = self.agent.setting_backend_name_otel(backend_name, "HTTP")
158
+ if backend_name_value:
159
+ backend_name_value = backend_name_value.decode() if isinstance(backend_name_value, bytes) else backend_name_value
160
+ ctx = self.agent.get_transaction_context()
161
+ btname = ctx.btname if ctx else None
162
+ req_url = ctx.url_path if ctx else None
163
+ self.agent.havoc_monitor.apply_outbound_service_failure(backend_name, backend_name_value, req_url, btname)
164
+
165
+ except Exception as e:
166
+ if isinstance(e, NDHavocException):
167
+ self.agent.logger.info("Havoc Exception {}".format(e))
168
+ status = 503
169
+ self.agent.set_current_status_code(503)
170
+ self.agent.get_nd_header(bt, backend_name, callout_id)
171
+ self.agent.http_call_end(bt, exit_call, method_name, backend_name, status, 0, start_time, callout_id)
172
+ raise e
173
+ else:
174
+ self.agent.logger.info("Non-Havoc Exception {}".format(e))
175
+ return exit_call
176
+
177
+
178
+ def run(self, func, *args, **kwargs):
179
+ """Run the function. If it raises an exception, end the exit call started from func
180
+ and raise the exception.
181
+
182
+ The exit call that needs to be managed should be passed as key word argument pythonagent_exit_call.
183
+
184
+ """
185
+ exit_call = kwargs.pop('pythonagent_exit_call', None)
186
+ with self.end_exit_call_and_reraise_on_exception(exit_call):
187
+ return func(*args, **kwargs)
188
+
189
+ def http_call_end(self, bt, exit_call, method_name, status=200, duration=0, start_time=0, backend_name=None, callout_id=0):
190
+ """End the exit call.
191
+ """
192
+ ctx = self.agent.get_transaction_context()
193
+ if not method_name:
194
+ method_name = ctx.entry_point_fqm if ctx else None
195
+
196
+ if not backend_name:
197
+ backend_name = ctx.backend_header if ctx else None
198
+
199
+ if start_time == 0:
200
+ start_time = ctx.tier_callout_start_time if ctx else 0
201
+
202
+
203
+ with self.log_exceptions():
204
+ if not exit_call:
205
+ self.agent.logger.debug("http call end bt {} exit_call {} method_name {} backend_name {} status {} duration {} start_time {}".format(bt, exit_call, method_name, backend_name, status, duration, start_time))
206
+ self.agent.http_call_end(bt, exit_call, method_name, backend_name, status, duration, start_time, callout_id)
207
+
208
+ def db_call_begin(self, bt, db_host, db_query, query_parameters=None, db_start_time=0, method_name=None, host_str=None):
209
+ """Start an exit call.
210
+ """
211
+ ip_handle = None
212
+ ctx = self.agent.get_transaction_context()
213
+ if not host_str:
214
+ host_str = ctx.host_str if ctx else None
215
+ if not host_str:
216
+ host_str = db_host
217
+
218
+ with self.log_exceptions():
219
+ if not method_name:
220
+ method_name = ctx.entry_point_fqm if ctx else None
221
+ ip_handle = self.agent.db_call_begin(method_name, bt, db_host, db_query, query_parameters, db_start_time)
222
+ try:
223
+
224
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
225
+ backend_name = None
226
+ backend_name_value = self.agent.setting_backend_name_otel(host_str, "DB")
227
+ if backend_name_value:
228
+ backend_name = backend_name_value.decode() if isinstance(backend_name_value, bytes) else backend_name_value
229
+ btname = ctx.btname if ctx else None
230
+ req_url = ctx.url_path if ctx else None
231
+ self.agent.havoc_monitor.apply_outbound_service_failure(host_str, backend_name, req_url, btname)
232
+
233
+
234
+ return ip_handle
235
+
236
+ except Exception as e:
237
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
238
+ if isinstance(e, NDHavocException):
239
+ self.agent.logger.info("Havoc Exception {}".format(e))
240
+ status_code = 503
241
+ self.agent.set_current_status_code(status_code)
242
+ if ctx:
243
+ ctx.status_code = status_code
244
+ self.agent.db_call_end(bt, ip_handle, 0, db_start_time, method_name, host_str, status_code)
245
+ raise e
246
+ else:
247
+ self.agent.logger.info("Non-Havoc Exception {}".format(e))
248
+ return ip_handle
249
+
250
+ def db_call_end(self, bt, ip_handle, duration=None, db_start_time=None, entry_point_fqm=None, host_str=None, status_code=None):
251
+ """End the exit call.
252
+ """
253
+ ctx = self.agent.get_transaction_context()
254
+ if not entry_point_fqm:
255
+ entry_point_fqm = ctx.entry_point_fqm if ctx else None
256
+
257
+ if not host_str:
258
+ host_str = ctx.host_str if ctx else None
259
+
260
+ if not status_code:
261
+ status_code = ctx.status_code if ctx else None
262
+
263
+ with self.log_exceptions():
264
+ if bt:
265
+ self.agent.db_call_end(bt, ip_handle, duration, db_start_time, entry_point_fqm, host_str, status_code)
266
+
267
+ @contextmanager
268
+ def end_exit_call_and_reraise_on_exception(self, exit_call, ignored_exceptions=()):
269
+ try:
270
+ yield
271
+ except ignored_exceptions:
272
+ raise
273
+ except:
274
+ self.agent.logger.exception("Exception raised in end exit call !!! {0}".format(sys.exc_info()))
275
+ raise
276
+
277
+
278
+ class EntryPointInterceptor(BaseInterceptor):
279
+ HTTP_ERROR_DISPLAY_NAME = 'HTTP {code}'
280
+
281
+ def start_business_transaction(self, bt_name, correlation_header, nd_cookie=None, nv_cookie=None, bt_header_value=None, fp_instance=None):
282
+ with self.log_exceptions():
283
+ return self.agent.start_business_transaction(bt_name, correlation_header, nd_cookie, nv_cookie,bt_header_value, fp_instance)
284
+
285
+ def end_business_transaction(self, bt, cpu_time):
286
+ with self.log_exceptions():
287
+ self.agent.end_business_transaction(bt, cpu_time)
288
+
289
+ def handle_http_status_code(self, bt, status_code, msg):
290
+ """Add the status code to the BT and deal with error codes.
291
+
292
+ If the status code is in the error config and enabled, or the status
293
+ code is >= 400, create an ErrorInfo object and add it to the BT.
294
+
295
+ """
296
+ self.agent.set_current_status_code(status_code)
297
+ if status_code >= 400:
298
+ self.agent.logger.info('Message is '.format(msg))
299
+ else:
300
+ return
301
+
302
+
303
+
@@ -0,0 +1,51 @@
1
+ """Base interceptor for distributed caches (typically, key-value stores).
2
+
3
+ """
4
+
5
+ from __future__ import unicode_literals
6
+
7
+ #from appdynamics.agent.models.exitcalls import EXIT_CACHE, EXIT_SUBTYPE_CACHE
8
+ from ..base import ExitCallInterceptor
9
+
10
+ EXIT_CACHE= 1
11
+ EXIT_SUBTYPE_CACHE ='CACHE'
12
+ class CacheInterceptor(ExitCallInterceptor):
13
+ """Base class for cache interceptors.
14
+
15
+ Extra Parameters
16
+ -----------------
17
+ vendor : string
18
+ The vendor name of this cache backend e.g. MEMCACHED.
19
+
20
+ """
21
+
22
+ backend_name_format_string = '{SERVER POOL} - {VENDOR}'
23
+
24
+ def __init__(self, agent, cls, vendor):
25
+ self.vendor = vendor
26
+ super(CacheInterceptor, self).__init__(agent, cls)
27
+
28
+ def get_backend(self, server_pool):
29
+ """
30
+
31
+ Parameters
32
+ ----------
33
+ server_pool : list of str
34
+
35
+ """
36
+ self.agent.logger.info('Modulenam CacheInterceptor class inside get_backend')
37
+ backend_properties = {
38
+ 'VENDOR': self.vendor,
39
+ 'SERVER POOL': '\n'.join(server_pool),
40
+ }
41
+ self.agent.logger.info("Modulenam CacheInterceptor class inside get_backend backend_properties is {0}".format(backend_properties))
42
+ return self.agent.backend_registry.get_backend(EXIT_CACHE, EXIT_SUBTYPE_CACHE, backend_properties,self.backend_name_format_string)
43
+ # return None
44
+
45
+
46
+ from .redis import intercept_redis
47
+ from .redis_asyncio import intercept_redis_asyncio
48
+
49
+
50
+ __all__ = ['intercept_redis', 'intercept_redis_asyncio']
51
+
@@ -0,0 +1,119 @@
1
+ """Interceptor for Redis.
2
+
3
+ """
4
+
5
+ from __future__ import unicode_literals
6
+
7
+ from pythonagent.agent.probes.cache import CacheInterceptor
8
+ from pythonagent.utils import generate_flow_path_id, get_utf8_bytes, get_current_timestamp_in_us, get_validated_duration
9
+
10
+
11
+ class RedisConnectionInterceptor(CacheInterceptor):
12
+
13
+ def __init__(self, agent, cls):
14
+ super(RedisConnectionInterceptor, self).__init__(agent, cls, 'REDIS')
15
+
16
+ def _send_packed_command(self, send_packed_command, connection, command, check_health=True):
17
+ exit_call = None
18
+ bt = None
19
+ db_start_time = 0
20
+ try:
21
+ # Extract command string early so we can guard on PING before touching context
22
+ command_str = ""
23
+ command_len = len(command)
24
+ if command_len != 0:
25
+ if command_len == 1:
26
+ parts = command[0].decode('utf-8', errors='replace').split('\n')
27
+ command_str = parts[2][:-1] if len(parts) > 2 else ""
28
+ else:
29
+ for i in range(command_len):
30
+ try:
31
+ parts = command[i].decode('utf-8', errors='replace').split('\n')
32
+ if len(parts) > 2:
33
+ sep = ',' if i < command_len - 1 else ''
34
+ command_str += parts[2][:-1] + sep
35
+ except AttributeError:
36
+ continue
37
+
38
+ # Skip internal health-check PINGs to prevent infinite recursion:
39
+ # Redis's check_health() sends a PING through the patched send_packed_command,
40
+ # which would re-enter this interceptor and loop indefinitely.
41
+ if command_str == "PING":
42
+ return send_packed_command(connection, command,
43
+ check_health=check_health,
44
+ pythonagent_exit_call=None)
45
+
46
+ # format :- "PROTOCOL|HOST|PORT|URL|DBPRODNAME|DBNAME|DBPRODVER|DRIVERNAME|DRIVERVER|USERNAME|SERVICENAME"
47
+ host_name_str = f"NA|{connection.host}|{connection.port}|NA|REDIS|{connection.db}|NA|NA|NA|{connection.username}|NA"
48
+ ctx = self.agent.get_transaction_context()
49
+ if ctx is not None:
50
+ ctx.host_str = host_name_str
51
+ ctx.entry_point_fqm = connection.__module__ + "." + send_packed_command.__qualname__
52
+
53
+ bt = self.bt or self.agent.get_current_bt()
54
+ self.agent.logger.info("Modulename: RedisConnectionInterceptor class || bt value is {0}".format(bt))
55
+
56
+ if bt:
57
+ try:
58
+ server_pool = ['%s:%s' % (connection.host, connection.port)]
59
+ except AttributeError:
60
+ # For UnixDomainSocketConnection objects.
61
+ server_pool = [connection.path]
62
+ #backend = self.get_backend(server_pool)
63
+ backend = True
64
+ if backend:
65
+ db_start_time = get_current_timestamp_in_us()
66
+ if ctx is not None:
67
+ ctx.db_callout_start_time = db_start_time
68
+ self.agent.logger.debug("db_call_begin for redis bt {} host {} query {} query_params {} db_start_time {}".format(bt, connection.host, command_str, None, db_start_time))
69
+ exit_call = self.db_call_begin(bt, connection.host, command_str, None, db_start_time, host_str=host_name_str)
70
+
71
+ except Exception as e:
72
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
73
+ if isinstance(e, NDHavocException):
74
+ raise e
75
+ self.agent.logger.exception("Exception in DB begin for redis {}".format(e))
76
+
77
+ try:
78
+ # Forward check_health so the original receives check_health=False when the
79
+ # health-check retry path calls us, preventing another check_health() cycle.
80
+ result = send_packed_command(connection, command,
81
+ check_health=check_health,
82
+ pythonagent_exit_call=exit_call)
83
+ except Exception as e:
84
+ self.agent.logger.exception("Error in application: {}".format(e))
85
+ try:
86
+ db_end_time = get_current_timestamp_in_us()
87
+ ctx = self.agent.get_transaction_context()
88
+ db_start_time = ctx.db_callout_start_time if ctx else 0
89
+ duration = get_validated_duration(db_start_time, db_end_time, "Redis")
90
+ if ctx:
91
+ ctx.status_code = 500
92
+ ctx.db_callout_start_time = 0
93
+ self.agent.logger.debug("db_call_end for redis with exception bt {} ip_handle {} duration {} db_start_time {}".format(bt, exit_call, duration, db_start_time))
94
+ self.db_call_end(bt, exit_call, duration, db_start_time)
95
+ except Exception as e:
96
+ self.agent.logger.exception("Exception in Original Method DB Call End for redis {}".format(e))
97
+ raise
98
+
99
+ try:
100
+ db_end_time = get_current_timestamp_in_us()
101
+ ctx = self.agent.get_transaction_context()
102
+ db_start_time = ctx.db_callout_start_time if ctx else 0
103
+ duration = get_validated_duration(db_start_time, db_end_time, "Redis")
104
+ if ctx:
105
+ ctx.status_code = 200
106
+ ctx.db_callout_start_time = 0
107
+ self.agent.logger.debug("db_call_end for redis bt {} ip_handle {} duration {} db_start_time {}".format(bt, exit_call, duration, db_start_time))
108
+ self.db_call_end(bt, exit_call, duration, db_start_time)
109
+
110
+ except Exception as e:
111
+ self.agent.logger.debug("Exception in DB End for redis {}".format(e))
112
+
113
+ return result
114
+
115
+
116
+ def intercept_redis(agent, mod):
117
+ agent.logger.warning("Instrument module: redis.connection{}".format(", mod: {}".format(mod) if mod else ""))
118
+ RedisConnectionInterceptor(agent, mod.Connection).attach('send_packed_command')
119
+
@@ -0,0 +1,83 @@
1
+ """Interceptor for Async Redis.
2
+
3
+ """
4
+
5
+ from __future__ import unicode_literals
6
+
7
+ from pythonagent.agent.probes.cache import CacheInterceptor
8
+ from pythonagent.utils import generate_flow_path_id, get_utf8_bytes, get_current_timestamp_in_us, get_validated_duration
9
+
10
+
11
+ class RedisAsyncIOConnectionInterceptor(CacheInterceptor):
12
+
13
+ def __init__(self, agent, cls):
14
+ super(RedisAsyncIOConnectionInterceptor, self).__init__(agent, cls, 'RedisAsyncIO')
15
+
16
+ async def _send_packed_command(self, send_packed_command, *args, **kwargs):
17
+
18
+ bt = None
19
+ exit_call = None
20
+ db_start_time = 0
21
+ entry_point_fqm = ""
22
+ host_str = ""
23
+ status_code = 0
24
+
25
+ try:
26
+ connection = args[0]
27
+ command = args[1]
28
+
29
+ command_str = ""
30
+ command_len = len(command)
31
+ if command_len != 0:
32
+ if command_len == 1:
33
+ command_str = command[0].decode().split('\n')[2][:-1]
34
+ else:
35
+ for i in range(command_len):
36
+ if i < command_len - 1:
37
+ command_str += command[i].decode().split('\n')[2][:-1] + ','
38
+ else:
39
+ command_str += command[i].decode().split('\n')[2][:-1]
40
+
41
+ if command_str == "CLIENT":
42
+ return await send_packed_command(*args, **kwargs)
43
+
44
+
45
+ # format :- "PROTOCOL|HOST|PORT|URL|DBPRODNAME|DBNAME|DBPRODVER|DRIVERNAME|DRIVERVER|USERNAME|SERVICENAME"
46
+ host_str = f"NA|{connection.host}|{connection.port}|NA|REDIS|{connection.db}|NA|NA|NA|{connection.username}|NA"
47
+ entry_point_fqm = connection.__module__ + "." + send_packed_command.__qualname__
48
+
49
+ bt = self.bt
50
+ query_parameters = None
51
+ db_start_time = get_current_timestamp_in_us()
52
+ exit_call = self.db_call_begin(bt, connection.host, command_str, query_parameters, db_start_time, entry_point_fqm, host_str)
53
+ except Exception as e:
54
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
55
+ if isinstance(e, NDHavocException):
56
+ raise e
57
+ self.agent.logger.exception("Error in DB Begin part of redis_asyncio: {}".format(e))
58
+
59
+ try:
60
+ result = await send_packed_command(*args, **kwargs)
61
+ except Exception as e:
62
+ db_end_time = get_current_timestamp_in_us()
63
+ duration = get_validated_duration(db_start_time, db_end_time, "redis_asyncio")
64
+ status_code = 500
65
+ self.db_call_end(bt, exit_call, duration, db_start_time, entry_point_fqm, host_str, status_code)
66
+ raise
67
+
68
+ try:
69
+ db_end_time = get_current_timestamp_in_us()
70
+ duration = get_validated_duration(db_start_time, db_end_time, "Redis")
71
+ status_code = 200
72
+ self.agent.logger.debug("db_call_end for redis bt {} ip_handle {} duration {} db_start_time {}".format(bt, exit_call, duration, db_start_time))
73
+ self.db_call_end(bt, exit_call, duration, db_start_time, entry_point_fqm, host_str, status_code)
74
+
75
+ except Exception as e:
76
+ self.agent.logger.exception("Error in DB End part of redis_asyncio: {}".format(e))
77
+
78
+ return result
79
+
80
+
81
+ def intercept_redis_asyncio(agent, mod):
82
+ agent.logger.warning("Instrument module: redis.asyncio.connection{}".format(", mod: {}".format(mod) if mod else ""))
83
+ RedisAsyncIOConnectionInterceptor(agent, mod.Connection).attach('send_packed_command')
@@ -0,0 +1 @@
1
+ from .asyncio import intercept_asyncio
@@ -0,0 +1,63 @@
1
+ """Interceptor for Asyncio
2
+
3
+ """
4
+
5
+ from __future__ import unicode_literals
6
+ from ..base import ExitCallInterceptor
7
+
8
+
9
+ class AsyncioInterceptor(ExitCallInterceptor):
10
+
11
+ def _dd_create_task(self, create_task, *args, **kwargs):
12
+
13
+ coro = self.get_argument_value(args, kwargs, 1, "coro")
14
+
15
+ context = self.agent.get_transaction_context()
16
+
17
+ async def traced_coro(*args_c, **kwargs_c):
18
+
19
+ if context and context != self.agent.get_transaction_context():
20
+ self.agent.set_transaction_context(context)
21
+
22
+ return await coro
23
+
24
+ args, kwargs = self.set_argument_value(args, kwargs, 1, "coro", traced_coro())
25
+
26
+ call = create_task(*args, **kwargs)
27
+
28
+ return call
29
+
30
+ def _set_event_loop(self, set_event_loop, *args, **kwargs):
31
+ ss = set_event_loop(*args, **kwargs)
32
+ return ss
33
+
34
+ def _get_event_loop(self, get_event_loop, *args, **kwargs):
35
+ get = get_event_loop()
36
+
37
+ def get_argument_value(self, args, kwargs, pos, kw, optional=False):
38
+ try:
39
+ return kwargs[kw]
40
+ except KeyError:
41
+ try:
42
+ return args[pos]
43
+ except IndexError:
44
+ if optional:
45
+ return None
46
+ raise Exception("%s (at position %d)" % (kw, pos))
47
+
48
+ def set_argument_value(self, args, kwargs, pos, kw, value, override_unset=False):
49
+ if len(args) > pos:
50
+ args = args[:pos] + (value,) + args[pos + 1:]
51
+ elif kw in kwargs or override_unset:
52
+ kwargs[kw] = value
53
+ else:
54
+ raise Exception("%s (at position %d) is invalid" % (kw, pos))
55
+
56
+ return args, kwargs
57
+
58
+
59
+ def intercept_asyncio(agent, mod):
60
+ agent.logger.warning("Instrument module: asyncio{}".format(", mod: {}".format(mod) if mod else ""))
61
+ AsyncioInterceptor(agent, mod.base_events.BaseEventLoop).attach('create_task',
62
+ patched_method_name="_dd_create_task")
63
+ #AsyncioInterceptor(agent, mod.events.BaseDefaultEventLoopPolicy).attach("set_event_loop")
@@ -0,0 +1,7 @@
1
+
2
+ from __future__ import unicode_literals
3
+
4
+ from .aelastic import intercept_elastic
5
+
6
+ __all__ = ['intercept_elastic']
7
+
@@ -0,0 +1,54 @@
1
+ from __future__ import unicode_literals
2
+ from ..base import ExitCallInterceptor
3
+ from pythonagent.lang import str
4
+
5
+
6
+ def intercept_elastic(agent, mod):
7
+ class ExitCallListener(mod.monitoring.CommandListener):
8
+ backend_name_format_string = '{HOST}:{PORT} - {DATABASE}'
9
+
10
+ def __init__(self):
11
+ self.interceptor = ExitCallInterceptor(agent, None)
12
+ self.exit_call_map = {}
13
+
14
+
15
+ def get_backend(self, connection_id):
16
+ #agent.logger.debug('Modulenameintercept_pymongo class inside get_backend function')
17
+ print("connection iddddddddddddddddddddddddddddddddd::::::::::::::::::", connection_id)
18
+ host, port = connection_id
19
+ backend_properties = {
20
+ 'HOST': host,
21
+ 'PORT': str(port)
22
+ }
23
+ return backend_properties
24
+
25
+ def started(self, event):
26
+ agent.logger.info('Modulenameintercept_elastic class inside started function is ')
27
+ with self.interceptor.log_exceptions():
28
+ agent.logger.info('Modulenameintercept_elastic class inside started function is ')
29
+ with self.interceptor.log_exceptions():
30
+ agent.logger.info("inside _execute funciton")
31
+ agent.logger.info("event:{0} ".format(event))
32
+ bt = self.interceptor.bt
33
+
34
+ agent.logger.info("bt statment....{0}".format(bt))
35
+ if not bt:
36
+ bt = agent.get_current_bt()
37
+ agent.logger.info("new bt value{0}".format(bt))
38
+ if bt:
39
+ backend = self.get_backend(event.connection_id, event.database_name)
40
+ agent.logger.info("backend..........{0}".format(backend))
41
+ agent.logger.info("Modulenameintercept_pymongo class || backend is{0}".format(backend))
42
+ if backend:
43
+ agent.logger.info("host.....".format(backend["HOST"]))
44
+
45
+ if backend and not hasattr(event, '_pythonagent_exit_call'):
46
+ exit_call = self.interceptor.db_call_begin(bt, backend["HOST"], str(event.command))
47
+ self.exit_call_map[event.operation_id] = exit_call
48
+
49
+ def succeeded(self, event):
50
+ self.interceptor.db_call_end(self.interceptor.bt,self.exit_call_map.pop(event.operation_id,None))
51
+ def failed(self, event):
52
+ self.interceptor.db_call_end(self.interceptor.bt,self.exit_call_map.pop(event.operation_id,None))
53
+
54
+ mod.monitoring.register(ExitCallListener())