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,49 @@
1
+ """Base class for HTTP connection interceptors.
2
+
3
+ """
4
+
5
+ from __future__ import unicode_literals
6
+
7
+ from ..base import ExitCallInterceptor
8
+ from pythonagent.lang import str, urlparse
9
+
10
+
11
+ EXIT_HTTP =0
12
+ EXIT_SUBTYPE_HTTP = 'HTTP'
13
+
14
+ class HTTPConnectionInterceptor(ExitCallInterceptor):
15
+ # If the library you are intercepting has an HTTPSConnection class which
16
+ # does not subclass httplib.HTTPSConnection, add it to this set.
17
+ https_connection_classes = set()
18
+ backend_name_format_string = '%s://{HOST}:{PORT}{URL}?{QUERY STRING}'
19
+
20
+ @classmethod
21
+ def _request_is_https(cls, connection):
22
+ #print("INSIDE IS REQUEST HTTPS")
23
+ if connection.port == 443:
24
+ return True
25
+ return isinstance(connection, tuple(cls.https_connection_classes))
26
+
27
+ def get_backend(self, host, port, scheme, url):
28
+ #self.agent.logger.info('Modulename: HTTPConnectionInterceptor class get_backend function')
29
+ parsed_url = urlparse(url)
30
+ backend_properties = {
31
+ 'HOST': host,
32
+ 'PORT': str(port),
33
+ 'URL': parsed_url.path,
34
+ 'QUERY STRING': parsed_url.query,
35
+ }
36
+ return self.agent.backend_registry.get_backend(EXIT_HTTP, EXIT_SUBTYPE_HTTP, backend_properties,
37
+ self.backend_name_format_string % scheme)
38
+
39
+
40
+ from .httplib import intercept_httplib
41
+ from .urllib3 import intercept_urllib3
42
+ from .requests import intercept_requests
43
+ from .tornado_httpclient import intercept_tornado_httpclient
44
+ from .boto import intercept_boto
45
+ from .aiohttp_client import intercept_aiohttp_client
46
+ from .httpx_client import intercept_httpx_client
47
+
48
+ __all__ = ['intercept_httplib', 'intercept_urllib3', 'intercept_requests', 'intercept_tornado_httpclient', 'intercept_boto', 'intercept_aiohttp_client', 'intercept_httpx_client']
49
+
@@ -0,0 +1,59 @@
1
+ from ..base import ExitCallInterceptor
2
+
3
+ from pythonagent.utils import get_current_timestamp_in_us, get_validated_duration, generate_callout_id
4
+ from urllib.parse import urlparse
5
+
6
+
7
+ class AsyncioHTTPClientInterceptor(ExitCallInterceptor):
8
+
9
+ async def wrap_request(self, _request, *args, **kwargs):
10
+ bt = None
11
+ exit_call = None
12
+ start_time = 0
13
+ try:
14
+ unique_id = generate_callout_id()
15
+ full_url = args[2]
16
+ if full_url:
17
+ parsed_url = urlparse(full_url)
18
+ host = parsed_url.hostname
19
+ port = parsed_url.port or ('443' if parsed_url.scheme == 'https' else '80')
20
+ url = parsed_url.path
21
+ host_port = "NA|" + str(host) + "|" + str(port) + "|" + url
22
+ else:
23
+ host_port = "NA|default_host|default_port|default_url"
24
+
25
+ mName = _request.__module__ + "." + _request.__qualname__
26
+ headers_value = self.agent.get_nd_header(self.bt, host_port, unique_id).decode()
27
+
28
+ client_session_object = args[0]
29
+
30
+ if client_session_object._default_headers:
31
+ client_session_object._default_headers["CavNDFPInstance"] = headers_value
32
+ else:
33
+ client_session_object._default_headers = {"CavNDFPInstance": headers_value}
34
+
35
+ bt = self.bt
36
+ start_time = get_current_timestamp_in_us()
37
+ exit_call = self.http_call_begin(bt, host_port, url, mName, start_time, host_port, unique_id)
38
+ except Exception as e:
39
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
40
+ if isinstance(e, NDHavocException):
41
+ raise e
42
+ self.agent.logger.exception("Exception in http call begin in aioHttp Client {}".format(e))
43
+
44
+ result = await _request(*args, **kwargs)
45
+
46
+ try:
47
+ end_time = get_current_timestamp_in_us()
48
+ duration = get_validated_duration(start_time, end_time, "AIOHTTP Client")
49
+ self.http_call_end(self.bt, exit_call, mName, result.status, duration, start_time, host_port, unique_id)
50
+
51
+ except Exception as e:
52
+ self.agent.logger.exception("Exception in http call end in aioHttp Client {}".format(e))
53
+ return result
54
+
55
+
56
+
57
+ def intercept_aiohttp_client(agent, mod):
58
+ agent.logger.warning("Instrument module: aiohttp.client{}".format(", mod: {}".format(mod) if mod else ""))
59
+ return AsyncioHTTPClientInterceptor(agent, mod.ClientSession).attach('_request', patched_method_name='wrap_request')
@@ -0,0 +1,12 @@
1
+
2
+ """Intercept boto to ensure that HTTPS is reported correctly.
3
+
4
+ """
5
+
6
+ from __future__ import unicode_literals
7
+
8
+ from . import HTTPConnectionInterceptor
9
+
10
+
11
+ def intercept_boto(agent, mod):
12
+ HTTPConnectionInterceptor.https_connection_classes.add(mod.CertValidatingHTTPSConnection)
@@ -0,0 +1,110 @@
1
+
2
+
3
+ """Interceptor for httplib/http.client.
4
+
5
+ """
6
+
7
+ from __future__ import unicode_literals
8
+ from . import HTTPConnectionInterceptor
9
+ from pythonagent.utils import get_current_timestamp_in_us, get_validated_duration, generate_callout_id
10
+
11
+
12
+ class HttplibConnectionInterceptor(HTTPConnectionInterceptor):
13
+ def _putrequest(self, putrequest, connection, method, url, *args, **kwargs):
14
+ exit_call = None
15
+ with self.log_exceptions():
16
+ ctx = self.agent.get_transaction_context()
17
+ if ctx:
18
+ ctx.http_request_type = method # GET, PUT, POST or DELETE
19
+ bt = self.bt
20
+ host_port = "NA|" + str(connection.host) + "|" + str(connection.port) + "|" + url
21
+ mName = str(putrequest.__module__ + "." + putrequest.__qualname__)
22
+ if ctx:
23
+ ctx.entry_point_fqm = mName
24
+ ctx.backend_header = host_port
25
+ ctx.callout_id = generate_callout_id()
26
+ current_time = get_current_timestamp_in_us()
27
+ if ctx:
28
+ ctx.tier_callout_start_time = current_time
29
+ exit_call = self.http_call_begin(bt, host_port, url, mName, current_time, host_port, ctx.callout_id if ctx else 0)
30
+ if bt:
31
+ scheme = 'https' if self._request_is_https(connection) else 'http'
32
+ connection._pythonagent_exit_call = exit_call
33
+ return putrequest(connection, method, url, pythonagent_exit_call=exit_call, *args, **kwargs)
34
+
35
+ def _endheaders(self, endheaders, connection, *args, **kwargs):
36
+ exit_call = None
37
+ with self.log_exceptions():
38
+ bt = self.bt
39
+ if self.agent.cav_env == "NATIVE":
40
+ prop_header = self.agent.sdk_get_propagation_header(bt)
41
+ else:
42
+ prop_header = None
43
+ if prop_header:
44
+ prop_header = prop_header.decode()
45
+ prop_header = str(prop_header)
46
+ prop_header = prop_header.split('\n')
47
+ for pr_header in prop_header:
48
+ key,value = pr_header.split(':')
49
+ connection.putheader(key,value)
50
+
51
+ if self.agent.cav_env == "NATIVE":
52
+ ctx = self.agent.get_transaction_context()
53
+ if ctx:
54
+ backend_name = ctx.backend_header
55
+ callout_id = ctx.callout_id
56
+ headers_value = self.agent.get_nd_header(self.bt, backend_name, callout_id)
57
+ if headers_value:
58
+ connection.putheader('CavNDFPInstance', headers_value.decode())
59
+
60
+ exit_call = getattr(connection, '_pythonagent_exit_call', None)
61
+ #self.agent.logger.info("Modulename: HttplibConnectionInterceptor class inside _endheaders exit_call is :{0}".format(exit_call))
62
+
63
+ header = self.make_correlation_header(exit_call)
64
+ if header is not None:
65
+ connection.putheader(*header)
66
+ #self.agent.logger.debug('Added correlation header to HTTP request: %s, %s' % header)
67
+ return endheaders(connection, pythonagent_exit_call=exit_call, *args, **kwargs)
68
+
69
+ def _getresponse(self, getresponse, connection, *args, **kwargs):
70
+ # CORE-40945 Catch TypeError as a special case for Python 2.6 and call getresponse with just the HTTPConnection instance.
71
+ exit_call = None
72
+ try:
73
+ exit_call = getattr(connection, '_pythonagent_exit_call', None)
74
+ bt = self.bt
75
+ except Exception as e:
76
+ self.agent.logger.exception("Error in _getresponse begin part: %s", e)
77
+
78
+ try:
79
+ with self.end_exit_call_and_reraise_on_exception(exit_call, ignored_exceptions=(TypeError,)):
80
+ response = getresponse(connection, *args, **kwargs)
81
+ except TypeError:
82
+ with self.end_exit_call_and_reraise_on_exception(exit_call):
83
+ response = getresponse(connection)
84
+
85
+ try:
86
+ current_time = get_current_timestamp_in_us()
87
+ ctx = self.agent.get_transaction_context()
88
+ start_time = ctx.tier_callout_start_time if ctx else 0
89
+ duration = get_validated_duration(start_time, current_time, "HTTP")
90
+ if ctx:
91
+ ctx.tier_callout_start_time = 0
92
+ method_name = None
93
+ self.http_call_end(self.bt, exit_call, method_name, response.status, duration, start_time, ctx.backend_header if ctx else None, ctx.callout_id if ctx else 0)
94
+ except Exception as e:
95
+ self.agent.logger.exception("Error in _getresponse end part: %s", e)
96
+
97
+ try:
98
+ del connection._pythonagent_exit_call
99
+ except AttributeError:
100
+ pass
101
+ return response
102
+
103
+
104
+ def intercept_httplib(agent, mod):
105
+ #print("intercept_httplib")
106
+ agent.logger.warning("Instrument module: httplib/http.client{}".format(", mod: {}".format(mod) if mod else ""))
107
+ HTTPConnectionInterceptor.https_connection_classes.add(mod.HTTPSConnection)
108
+ interceptor = HttplibConnectionInterceptor(agent, mod.HTTPConnection)
109
+ interceptor.attach(['putrequest', 'endheaders'])
110
+ interceptor.attach('getresponse', wrapper_func=None) # CORE-40945 Do not wrap getresponse in the default wrapper.
@@ -0,0 +1,116 @@
1
+ from ..base import ExitCallInterceptor
2
+
3
+ from pythonagent.utils import get_current_timestamp_in_us, get_validated_duration, generate_callout_id
4
+ from urllib.parse import urlparse
5
+
6
+
7
+ class HTTPXAsyncClientInterceptor(ExitCallInterceptor):
8
+
9
+ async def wrap_send(self, send, *args, **kwargs):
10
+ bt = None
11
+ exit_call = None
12
+ start_time = 0
13
+
14
+ try:
15
+ unique_id = generate_callout_id()
16
+ url = args[1].url
17
+ full_url = str(url)
18
+ if full_url:
19
+ parsed_url = urlparse(full_url)
20
+ host = parsed_url.hostname
21
+ port = parsed_url.port or ('443' if parsed_url.scheme == 'https' else '80')
22
+ url = parsed_url.path or "/"
23
+ if parsed_url.query:
24
+ url = url + "?" + parsed_url.query
25
+ host_port = "NA|" + host + "|" + str(port) + "|" + url
26
+ else:
27
+ host_port = "NA|default_host|default_port|default_url"
28
+
29
+ bt = self.bt
30
+ mName = send.__module__ + "." + send.__qualname__
31
+ headers_value = self.agent.get_nd_header(self.bt, host_port, unique_id).decode()
32
+
33
+ client_session_object = args[0]
34
+
35
+ if client_session_object._headers:
36
+ client_session_object._headers["CavNDFPInstance"] = headers_value
37
+ else:
38
+ client_session_object._headers = {"CavNDFPInstance": headers_value}
39
+
40
+
41
+ start_time = get_current_timestamp_in_us()
42
+ exit_call = self.http_call_begin(bt, host_port, url, mName, start_time, host_port, unique_id)
43
+ except Exception as e:
44
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
45
+ if isinstance(e, NDHavocException):
46
+ raise e
47
+ self.agent.logger.exception("Exception in http call begin in httpx_async Client {}".format(e))
48
+
49
+ result = await send(*args, **kwargs)
50
+
51
+ try:
52
+ current_time = get_current_timestamp_in_us()
53
+ duration = get_validated_duration(start_time, current_time, "httpx_async Client")
54
+ self.http_call_end(self.bt, exit_call, mName, result.status_code, duration, start_time, host_port, unique_id)
55
+
56
+ except Exception as e:
57
+ self.agent.logger.exception("Exception in http call end in httpx_async Client {}".format(e))
58
+
59
+ return result
60
+
61
+
62
+ class HTTPXClientInterceptor(ExitCallInterceptor):
63
+ def wrap_send(self, send, *args, **kwargs):
64
+ bt = None
65
+ exit_call = None
66
+ start_time = 0
67
+
68
+ try:
69
+ unique_id = generate_callout_id()
70
+ url = args[1].url
71
+ full_url = str(url)
72
+ if full_url:
73
+ parsed_url = urlparse(full_url)
74
+ host = parsed_url.hostname
75
+ port = parsed_url.port or ('443' if parsed_url.scheme == 'https' else '80')
76
+ url = parsed_url.path or "/"
77
+ if parsed_url.query:
78
+ url = url + "?" + parsed_url.query
79
+ host_port = "NA|" + host + "|" + str(port) + "|" + url
80
+ else:
81
+ host_port = "NA|default_host|default_port|default_url"
82
+ bt = self.bt
83
+ mName = send.__module__ + "." + send.__qualname__
84
+
85
+ headers_value = self.agent.get_nd_header(self.bt, host_port, unique_id).decode()
86
+ client_session_object = args[0]
87
+ if client_session_object._headers:
88
+ client_session_object._headers["CavNDFPInstance"] = headers_value
89
+ else:
90
+ client_session_object._headers = {"CavNDFPInstance": headers_value}
91
+
92
+ start_time = get_current_timestamp_in_us()
93
+ exit_call = self.http_call_begin(bt, host_port, url, mName, start_time, host_port, unique_id)
94
+ except Exception as e:
95
+ from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
96
+ if isinstance(e, NDHavocException):
97
+ raise e
98
+ self.agent.logger.exception("Exception in http call begin in httpx_sync Client {}".format(e))
99
+
100
+
101
+ result = send(*args, **kwargs)
102
+
103
+ try:
104
+ current_time = get_current_timestamp_in_us()
105
+ duration = get_validated_duration(start_time, current_time, "httpx_sync Client")
106
+ self.http_call_end(self.bt, exit_call, mName, result.status_code, duration, start_time, host_port, unique_id)
107
+ except Exception as e:
108
+ self.agent.logger.exception("Exception in http call begin in httpx_sync Client {}".format(e))
109
+
110
+ return result
111
+
112
+
113
+ def intercept_httpx_client(agent, mod):
114
+ agent.logger.warning("Instrument module: httpx{}".format(", mod: {}".format(mod) if mod else ""))
115
+ HTTPXClientInterceptor(agent, mod.Client).attach('send', patched_method_name='wrap_send')
116
+ HTTPXAsyncClientInterceptor(agent, mod.AsyncClient).attach('send', patched_method_name='wrap_send')
@@ -0,0 +1,15 @@
1
+
2
+ """Intercept requests to ensure that HTTPS is reported correctly.
3
+
4
+ """
5
+
6
+ from __future__ import unicode_literals
7
+
8
+ from .urllib3 import intercept_urllib3
9
+
10
+
11
+ def intercept_requests(agent, mod):
12
+ #print("intercept_requests")
13
+ agent.logger.warning("Instrument module: requests{}".format(", mod: {}".format(mod) if mod else ""))
14
+ # requests ships with its own version of urllib3, so we need to manually intercept it.
15
+ intercept_urllib3(agent, mod.packages.urllib3)
@@ -0,0 +1,85 @@
1
+ from __future__ import unicode_literals
2
+ import functools
3
+ from enum import unique
4
+ from logging import exception
5
+
6
+ from pythonagent.lang import get_args, urlparse
7
+
8
+ from . import HTTPConnectionInterceptor
9
+ from pythonagent.utils import get_current_timestamp_in_us, get_validated_duration, generate_callout_id
10
+
11
+
12
+ class TornadoAsyncHTTPClientInterceptor(HTTPConnectionInterceptor):
13
+
14
+ def _fetch(self, fetch, *args, **kwargs):
15
+ bt = self.bt
16
+ if not bt:
17
+ return fetch(*args, **kwargs)
18
+
19
+ exit_call = None
20
+ start_time = 0
21
+
22
+ try:
23
+ unique_id = generate_callout_id()
24
+ bt = self.bt
25
+ client = args[0]
26
+ request = args[1]
27
+ if isinstance(request, str):
28
+ url = request
29
+ else:
30
+ url = request.url
31
+
32
+ parsed_url = urlparse(url)
33
+ host = parsed_url.hostname
34
+ port = parsed_url.port or ('443' if parsed_url.scheme == 'https' else '80')
35
+ url = parsed_url.path
36
+ host_port = "NA|" + host + "|" + str(port) + "|" + url
37
+ mName = fetch.__module__ + "." + fetch.__qualname__
38
+ headers_value = self.agent.get_nd_header(self.bt, host_port, unique_id).decode()
39
+
40
+ headers = None
41
+
42
+ if isinstance(request, str):
43
+ try:
44
+ from tornado.httpclient import HTTPRequest
45
+ req = HTTPRequest(request, **kwargs)
46
+ headers = req._headers
47
+ except ImportError:
48
+ pass
49
+ else:
50
+ headers = request._headers
51
+
52
+ if isinstance(headers, dict):
53
+ headers["CavNDFPInstance"] = headers_value
54
+ else:
55
+ try:
56
+ from tornado.httputil import HTTPHeaders
57
+ if isinstance(headers, HTTPHeaders):
58
+ headers.add("CavNDFPInstance", headers_value)
59
+ except ImportError:
60
+ pass
61
+
62
+ start_time = get_current_timestamp_in_us()
63
+ exit_call = self.http_call_begin(bt, host_port, url, mName, start_time, host_port, unique_id)
64
+ except Exception as e:
65
+ self.agent.logger.exception("Exception in HTTP Begin for Tornado Client {}".format(e))
66
+
67
+ future = fetch(*args, **kwargs)
68
+ future.add_done_callback(functools.partial(self.end, exit_call, mName, host_port, start_time, unique_id))
69
+
70
+ return future
71
+
72
+ def end(self, exit_call, mName, host_port, start_time, unique_id, future):
73
+ try:
74
+ if future._result:
75
+ current_time = get_current_timestamp_in_us()
76
+ duration = get_validated_duration(start_time, current_time, "Tornado Async HTTP Client")
77
+ status = future._result.code
78
+ self.http_call_end(self.bt, exit_call, mName, status, duration, start_time, host_port, unique_id)
79
+ except Exception as e:
80
+ self.agent.logger.exception("Exception in HTTP End for Tornado Client {}".format(e))
81
+
82
+
83
+ def intercept_tornado_httpclient(agent, mod):
84
+ agent.logger.warning("Instrument module: tornado.httpclient{}".format(", mod: {}".format(mod) if mod else ""))
85
+ return TornadoAsyncHTTPClientInterceptor(agent, mod.AsyncHTTPClient).attach('fetch')
@@ -0,0 +1,16 @@
1
+
2
+ """Intercept urllib3 to ensure that HTTPS is reported correctly.
3
+
4
+ """
5
+
6
+ from __future__ import unicode_literals
7
+
8
+ from . import HTTPConnectionInterceptor
9
+
10
+
11
+ def intercept_urllib3(agent, mod):
12
+ agent.logger.warning("Instrument module: urllib3{}".format(", mod: {}".format(mod) if mod else ""))
13
+ # urllib3 1.8+ provides its own HTTPSConnection class.
14
+ if hasattr(mod, 'connection'):
15
+ #print("intercept_urllib3")
16
+ HTTPConnectionInterceptor.https_connection_classes.add(mod.connection.HTTPSConnection)
@@ -0,0 +1,21 @@
1
+ #from .langchain_core import intercept_langchain_core_language_models
2
+ #from .langchain_core import intercept_langchain_core_runnables
3
+ from .langchain_core import intercept_langchain_core_tools
4
+
5
+ from .langchain_community import intercept_langchain_community_vectorstores
6
+ from .langchain_community import intercept_langchain_community_embeddings
7
+
8
+ from .langchain_openai import intercept_langchain_openai
9
+
10
+ #from .langgraph import intercept_langgraph_pregel, intercept_langgraph_utils_runnable
11
+
12
+ __all__ = [#'intercept_langchain_core_language_models',
13
+ #'intercept_langchain_core_runnables',
14
+ 'intercept_langchain_core_tools',
15
+ 'intercept_langchain_community_vectorstores',
16
+ 'intercept_langchain_community_embeddings',
17
+ 'intercept_langchain_openai',
18
+ #'intercept_langgraph_pregel',
19
+ #'intercept_langgraph_utils_runnable'
20
+ ]
21
+
@@ -0,0 +1,95 @@
1
+ from ..base import ExitCallInterceptor
2
+ from ..span import ToolSpan
3
+ from pythonagent.utils import get_current_timestamp_in_us, get_validated_duration, split_fp_topo
4
+ import logging
5
+
6
+ """
7
+ A wrapper class to instrument LangChain Tool
8
+ executions triggered by LLM outputs.
9
+ Intercepts _invoke method to capture tool name
10
+ input and output of the prompt.
11
+ """
12
+
13
+ class LangChainBaseToolInterceptor(ExitCallInterceptor):
14
+ def _invoke(self, invoke, *args, **kwargs):
15
+
16
+ start_time, exit_call, bt, context, fqm, host_name_str = 0, 0, None, None, None, None
17
+ test_run, fpi, entry_fpi, nd_session_id, nv_session_id, page_id, full_fp = None, None, None, None, None, None, None
18
+
19
+ try:
20
+ tool_object, tool_parameters = args[0], args[1]
21
+ tool_name = tool_object.name
22
+
23
+ bt, callout_type, db = self.bt, "TOOLS", tool_name
24
+ host, port, username, query, query_params = "NA", "NA", "NA", "NA", None
25
+
26
+ host_name_str = f"NA|{host}|{port}|NA|{callout_type}|{db}|NA|NA|NA|{username}|NA"
27
+ fqm = f"{invoke.__module__}.{invoke.__qualname__}"
28
+
29
+ context = self.agent.get_transaction_context()
30
+ if context:
31
+ context.host_str = host_name_str
32
+ context.entry_point_fqm = fqm
33
+
34
+
35
+ start_time = get_current_timestamp_in_us()
36
+ if self.agent.logger.isEnabledFor(logging.INFO):
37
+ self.agent.logger.info("langchain base tool db call begin bt {} host {} query {} query_params {} start_time {}".format(bt, host, query, query_params, start_time))
38
+ exit_call = self.db_call_begin(bt, host, query, query_params, start_time)
39
+
40
+ except Exception as e:
41
+ self.agent.logger.exception("langchain base tool db call begin {}".format(e))
42
+
43
+ try:
44
+ result = invoke(*args, **kwargs)
45
+ except Exception as e:
46
+ self.agent.logger.exception("Error in application: {}".format(e))
47
+ try:
48
+ end_time = get_current_timestamp_in_us()
49
+ duration = get_validated_duration(start_time, end_time, "Langchain Tool")
50
+ if context:
51
+ context.status_code = 500
52
+ context.db_callout_time = 0
53
+ if self.agent.logger.isEnabledFor(logging.INFO):
54
+ self.agent.logger.info("langchain base tool db call end bt {} exit_call {} duration {} start_time {} fqm {}host_name_str {}".format(bt, exit_call, duration, start_time, fqm, host_name_str))
55
+ self.db_call_end(bt, exit_call, duration, start_time, fqm, host_name_str)
56
+ except Exception as e:
57
+ self.agent.logger.exception("Exception in Original Method DB Call End for BaseTool invoke {}".format(e))
58
+
59
+ raise
60
+
61
+ try:
62
+ end_time = get_current_timestamp_in_us()
63
+ duration = get_validated_duration(start_time, end_time, "Langchain Tool")
64
+ if context:
65
+ context.status_code = 200
66
+ context.db_callout_time = 0
67
+
68
+ if self.agent.logger.isEnabledFor(logging.INFO):
69
+ self.agent.logger.info("langchain base tool db call end bt {} exit_call {} duration {} start_time {} fqm {}host_name_str {}".format(bt, exit_call, duration, start_time, fqm, host_name_str))
70
+ self.db_call_end(bt, exit_call, duration, start_time, fqm, host_name_str)
71
+
72
+ if self.agent.nf_enabled:
73
+ # Capturing input prompt and output response, formatting it, and sending to NF server
74
+ meta = { "span_kind": "tool",
75
+ "input": str(args),
76
+ "output": str(result)}
77
+
78
+ if context:
79
+ fp_topo = context.fp_topo
80
+ test_run, fpi, entry_fpi, nd_session_id, nv_session_id, page_id, full_fp = split_fp_topo(fp_topo)
81
+
82
+ tool_span = ToolSpan(fqm, start_time, duration, "ok", meta, test_run, fpi, entry_fpi, nd_session_id, nv_session_id, page_id, full_fp)
83
+ tool_span_json = tool_span.create_span_json()
84
+
85
+ if self.agent.logger.isEnabledFor(logging.INFO):
86
+ self.agent.logger.info("langchain base tool sending tool span {}".format(tool_span_json))
87
+
88
+ # SDK API call to send logs to NF
89
+ self.agent.dump_log_message(bt, tool_span_json)
90
+
91
+ except Exception as e:
92
+ self.agent.logger.exception("langchain base tool db call begin {}".format(e))
93
+
94
+ return result
95
+