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,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
|
+
|