sf-veritas 0.10.3__cp310-cp310-manylinux_2_28_x86_64.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.
Potentially problematic release.
This version of sf-veritas might be problematic. Click here for more details.
- sf_veritas/__init__.py +20 -0
- sf_veritas/_sffastlog.c +889 -0
- sf_veritas/_sffastlog.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnet.c +924 -0
- sf_veritas/_sffastnet.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnetworkrequest.c +730 -0
- sf_veritas/_sffastnetworkrequest.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan.c +2155 -0
- sf_veritas/_sffuncspan.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan_config.c +617 -0
- sf_veritas/_sffuncspan_config.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfheadercheck.c +341 -0
- sf_veritas/_sfheadercheck.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfnetworkhop.c +1451 -0
- sf_veritas/_sfnetworkhop.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfservice.c +1175 -0
- sf_veritas/_sfservice.cpython-310-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfteepreload.c +5167 -0
- sf_veritas/app_config.py +49 -0
- sf_veritas/cli.py +336 -0
- sf_veritas/constants.py +10 -0
- sf_veritas/custom_excepthook.py +304 -0
- sf_veritas/custom_log_handler.py +129 -0
- sf_veritas/custom_output_wrapper.py +144 -0
- sf_veritas/custom_print.py +146 -0
- sf_veritas/django_app.py +5 -0
- sf_veritas/env_vars.py +186 -0
- sf_veritas/exception_handling_middleware.py +18 -0
- sf_veritas/exception_metaclass.py +69 -0
- sf_veritas/fast_frame_info.py +116 -0
- sf_veritas/fast_network_hop.py +293 -0
- sf_veritas/frame_tools.py +112 -0
- sf_veritas/funcspan_config_loader.py +556 -0
- sf_veritas/function_span_profiler.py +1174 -0
- sf_veritas/import_hook.py +62 -0
- sf_veritas/infra_details/__init__.py +3 -0
- sf_veritas/infra_details/get_infra_details.py +24 -0
- sf_veritas/infra_details/kubernetes/__init__.py +3 -0
- sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
- sf_veritas/infra_details/kubernetes/get_details.py +7 -0
- sf_veritas/infra_details/running_on/__init__.py +17 -0
- sf_veritas/infra_details/running_on/kubernetes.py +11 -0
- sf_veritas/interceptors.py +497 -0
- sf_veritas/libsfnettee.so +0 -0
- sf_veritas/local_env_detect.py +118 -0
- sf_veritas/package_metadata.py +6 -0
- sf_veritas/patches/__init__.py +0 -0
- sf_veritas/patches/concurrent_futures.py +19 -0
- sf_veritas/patches/constants.py +1 -0
- sf_veritas/patches/exceptions.py +82 -0
- sf_veritas/patches/multiprocessing.py +32 -0
- sf_veritas/patches/network_libraries/__init__.py +76 -0
- sf_veritas/patches/network_libraries/aiohttp.py +281 -0
- sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
- sf_veritas/patches/network_libraries/http_client.py +419 -0
- sf_veritas/patches/network_libraries/httpcore.py +515 -0
- sf_veritas/patches/network_libraries/httplib2.py +204 -0
- sf_veritas/patches/network_libraries/httpx.py +515 -0
- sf_veritas/patches/network_libraries/niquests.py +211 -0
- sf_veritas/patches/network_libraries/pycurl.py +385 -0
- sf_veritas/patches/network_libraries/requests.py +633 -0
- sf_veritas/patches/network_libraries/tornado.py +341 -0
- sf_veritas/patches/network_libraries/treq.py +270 -0
- sf_veritas/patches/network_libraries/urllib_request.py +468 -0
- sf_veritas/patches/network_libraries/utils.py +398 -0
- sf_veritas/patches/os.py +17 -0
- sf_veritas/patches/threading.py +218 -0
- sf_veritas/patches/web_frameworks/__init__.py +54 -0
- sf_veritas/patches/web_frameworks/aiohttp.py +793 -0
- sf_veritas/patches/web_frameworks/async_websocket_consumer.py +317 -0
- sf_veritas/patches/web_frameworks/blacksheep.py +527 -0
- sf_veritas/patches/web_frameworks/bottle.py +502 -0
- sf_veritas/patches/web_frameworks/cherrypy.py +678 -0
- sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
- sf_veritas/patches/web_frameworks/django.py +944 -0
- sf_veritas/patches/web_frameworks/eve.py +395 -0
- sf_veritas/patches/web_frameworks/falcon.py +926 -0
- sf_veritas/patches/web_frameworks/fastapi.py +724 -0
- sf_veritas/patches/web_frameworks/flask.py +520 -0
- sf_veritas/patches/web_frameworks/klein.py +501 -0
- sf_veritas/patches/web_frameworks/litestar.py +551 -0
- sf_veritas/patches/web_frameworks/pyramid.py +428 -0
- sf_veritas/patches/web_frameworks/quart.py +824 -0
- sf_veritas/patches/web_frameworks/robyn.py +697 -0
- sf_veritas/patches/web_frameworks/sanic.py +857 -0
- sf_veritas/patches/web_frameworks/starlette.py +723 -0
- sf_veritas/patches/web_frameworks/strawberry.py +813 -0
- sf_veritas/patches/web_frameworks/tornado.py +481 -0
- sf_veritas/patches/web_frameworks/utils.py +91 -0
- sf_veritas/print_override.py +13 -0
- sf_veritas/regular_data_transmitter.py +409 -0
- sf_veritas/request_interceptor.py +401 -0
- sf_veritas/request_utils.py +550 -0
- sf_veritas/server_status.py +1 -0
- sf_veritas/shutdown_flag.py +11 -0
- sf_veritas/subprocess_startup.py +3 -0
- sf_veritas/test_cli.py +145 -0
- sf_veritas/thread_local.py +970 -0
- sf_veritas/timeutil.py +114 -0
- sf_veritas/transmit_exception_to_sailfish.py +28 -0
- sf_veritas/transmitter.py +132 -0
- sf_veritas/types.py +47 -0
- sf_veritas/unified_interceptor.py +1580 -0
- sf_veritas/utils.py +39 -0
- sf_veritas-0.10.3.dist-info/METADATA +97 -0
- sf_veritas-0.10.3.dist-info/RECORD +132 -0
- sf_veritas-0.10.3.dist-info/WHEEL +5 -0
- sf_veritas-0.10.3.dist-info/entry_points.txt +2 -0
- sf_veritas-0.10.3.dist-info/top_level.txt +1 -0
- sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
- sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
- sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
- sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
- sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
- sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
- sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
- sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
- sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
- sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
- sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
- sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
- sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
- sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
- sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
- sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
- sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
- sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
- sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
- sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
- sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
- sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
- sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Monkey-patch the `requests` stack (requests → urllib3 → http.client):
|
|
3
|
+
|
|
4
|
+
• For every outbound request, propagate the SAILFISH_TRACING_HEADER + FUNCSPAN_OVERRIDE_HEADER
|
|
5
|
+
unless the destination host is in `domains_to_not_propagate_headers_to`.
|
|
6
|
+
• Fire NetworkRequestTransmitter via utils.record_network_request
|
|
7
|
+
so we always capture (url, status, timings, success, error).
|
|
8
|
+
• When LD_PRELOAD is active, ONLY inject headers (skip capture - socket layer handles it).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import http.client
|
|
14
|
+
import os
|
|
15
|
+
import time
|
|
16
|
+
from typing import Dict, List, Optional, Tuple
|
|
17
|
+
|
|
18
|
+
import requests
|
|
19
|
+
import urllib3
|
|
20
|
+
from requests.sessions import Session
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import wrapt
|
|
24
|
+
|
|
25
|
+
HAS_WRAPT = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
HAS_WRAPT = False
|
|
28
|
+
|
|
29
|
+
# JSON serialization - try fast orjson first, fallback to stdlib json
|
|
30
|
+
try:
|
|
31
|
+
import orjson
|
|
32
|
+
|
|
33
|
+
HAS_ORJSON = True
|
|
34
|
+
except ImportError:
|
|
35
|
+
import json
|
|
36
|
+
|
|
37
|
+
HAS_ORJSON = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
from ...constants import FUNCSPAN_OVERRIDE_HEADER, SAILFISH_TRACING_HEADER
|
|
41
|
+
from ...thread_local import (
|
|
42
|
+
activate_reentrancy_guards_exception,
|
|
43
|
+
activate_reentrancy_guards_logging,
|
|
44
|
+
activate_reentrancy_guards_print,
|
|
45
|
+
get_funcspan_override,
|
|
46
|
+
)
|
|
47
|
+
from .utils import (
|
|
48
|
+
get_trace_and_should_propagate,
|
|
49
|
+
get_trace_and_should_propagate_fast,
|
|
50
|
+
init_fast_header_check,
|
|
51
|
+
inject_headers_ultrafast,
|
|
52
|
+
record_network_request,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
###############################################################################
|
|
56
|
+
# Internal helpers
|
|
57
|
+
###############################################################################
|
|
58
|
+
|
|
59
|
+
# header names used for re-entrancy guards
|
|
60
|
+
REENTRANCY_GUARD_LOGGING_PREACTIVE = "reentrancy_guard_logging_preactive"
|
|
61
|
+
REENTRANCY_GUARD_PRINT_PREACTIVE = "reentrancy_guard_print_preactive"
|
|
62
|
+
REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE = "reentrancy_guard_exception_preactive"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _tee_preload_active() -> bool:
|
|
66
|
+
"""Detect if LD_PRELOAD tee is active (same logic as http_client.py)."""
|
|
67
|
+
if os.getenv("SF_TEE_PRELOAD_ONLY", "0") == "1":
|
|
68
|
+
return True
|
|
69
|
+
ld = os.getenv("LD_PRELOAD", "")
|
|
70
|
+
return "libsfnettee.so" in ld or "_sfteepreload" in ld
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# PERFORMANCE: Reentrancy guards disabled - they add ~10-20μs per request
|
|
74
|
+
# The socket layer (LD_PRELOAD) handles everything we need
|
|
75
|
+
# def _activate_rg(headers: Dict[str, str]) -> None:
|
|
76
|
+
# """Turn the three 'preactive' guard flags ON for downstream hops."""
|
|
77
|
+
# headers[REENTRANCY_GUARD_LOGGING_PREACTIVE] = "true"
|
|
78
|
+
# headers[REENTRANCY_GUARD_PRINT_PREACTIVE] = "true"
|
|
79
|
+
# headers[REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE] = "true"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# def _check_rg(headers: Dict[str, str]) -> None:
|
|
83
|
+
# """If any pre-active guard present, switch the corresponding guard on."""
|
|
84
|
+
# if headers.get(REENTRANCY_GUARD_LOGGING_PREACTIVE, "false").lower() == "true":
|
|
85
|
+
# activate_reentrancy_guards_logging()
|
|
86
|
+
# if headers.get(REENTRANCY_GUARD_PRINT_PREACTIVE, "false").lower() == "true":
|
|
87
|
+
# activate_reentrancy_guards_print()
|
|
88
|
+
# if headers.get(REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE, "false").lower() == "true":
|
|
89
|
+
# activate_reentrancy_guards_exception()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _prepare(
|
|
93
|
+
url: str,
|
|
94
|
+
domains_to_skip: List[str],
|
|
95
|
+
headers: Optional[Dict[str, str]],
|
|
96
|
+
) -> Tuple[str, Dict[str, str], int]:
|
|
97
|
+
"""
|
|
98
|
+
Inject trace header + funcspan override header (unless excluded) and return:
|
|
99
|
+
trace_id, merged_headers, timestamp_ms
|
|
100
|
+
|
|
101
|
+
ULTRA-FAST: <20ns overhead for header injection.
|
|
102
|
+
|
|
103
|
+
PERFORMANCE: Reentrancy guards removed - saves ~10-20μs per request.
|
|
104
|
+
"""
|
|
105
|
+
trace_id, propagate = get_trace_and_should_propagate(url, domains_to_skip)
|
|
106
|
+
hdrs: Dict[str, str] = dict(headers or {})
|
|
107
|
+
# _check_rg(hdrs) # DISABLED for performance
|
|
108
|
+
if propagate:
|
|
109
|
+
hdrs[SAILFISH_TRACING_HEADER] = trace_id
|
|
110
|
+
|
|
111
|
+
# Inject funcspan override header if present (ContextVar lookup ~8ns)
|
|
112
|
+
try:
|
|
113
|
+
funcspan_override = get_funcspan_override()
|
|
114
|
+
if funcspan_override is not None:
|
|
115
|
+
hdrs[FUNCSPAN_OVERRIDE_HEADER] = funcspan_override
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
# _activate_rg(hdrs) # DISABLED for performance
|
|
120
|
+
return trace_id, hdrs, int(time.time() * 1_000)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _capture_and_record_requests(
|
|
124
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
125
|
+
):
|
|
126
|
+
"""Capture response data in background thread AFTER response is returned to user."""
|
|
127
|
+
resp_data: bytes = b""
|
|
128
|
+
req_headers: bytes = b""
|
|
129
|
+
resp_headers: bytes = b""
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
# Capture headers efficiently
|
|
133
|
+
if HAS_ORJSON:
|
|
134
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
135
|
+
if resp:
|
|
136
|
+
resp_headers = orjson.dumps({str(k): str(v) for k, v in resp.headers.items()})
|
|
137
|
+
else:
|
|
138
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
139
|
+
if resp:
|
|
140
|
+
resp_headers = json.dumps({str(k): str(v) for k, v in resp.headers.items()}).encode("utf-8")
|
|
141
|
+
|
|
142
|
+
# For response body: check if already cached in _content
|
|
143
|
+
if resp:
|
|
144
|
+
try:
|
|
145
|
+
if hasattr(resp, "_content") and resp._content is not None:
|
|
146
|
+
resp_data = resp._content
|
|
147
|
+
except Exception: # noqa: BLE001
|
|
148
|
+
pass
|
|
149
|
+
except Exception: # noqa: BLE001
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
# Send to C extension in background
|
|
153
|
+
record_network_request(
|
|
154
|
+
trace_id,
|
|
155
|
+
url,
|
|
156
|
+
method,
|
|
157
|
+
status,
|
|
158
|
+
success,
|
|
159
|
+
err,
|
|
160
|
+
timestamp_start=t0,
|
|
161
|
+
timestamp_end=t1,
|
|
162
|
+
request_data=req_data,
|
|
163
|
+
response_data=resp_data,
|
|
164
|
+
request_headers=req_headers,
|
|
165
|
+
response_headers=resp_headers,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _capture_and_record_urllib3(
|
|
170
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
171
|
+
):
|
|
172
|
+
"""Capture urllib3 response data in background thread AFTER response is returned to user."""
|
|
173
|
+
resp_data: bytes = b""
|
|
174
|
+
req_headers: bytes = b""
|
|
175
|
+
resp_headers: bytes = b""
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
# Capture headers efficiently
|
|
179
|
+
if HAS_ORJSON:
|
|
180
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
181
|
+
if resp:
|
|
182
|
+
resp_headers = orjson.dumps({str(k): str(v) for k, v in resp.headers.items()})
|
|
183
|
+
else:
|
|
184
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
185
|
+
if resp:
|
|
186
|
+
resp_headers = json.dumps({str(k): str(v) for k, v in resp.headers.items()}).encode("utf-8")
|
|
187
|
+
|
|
188
|
+
# For response body: check if already available in data attribute
|
|
189
|
+
if resp:
|
|
190
|
+
try:
|
|
191
|
+
resp_data = getattr(resp, "data", b"")
|
|
192
|
+
except Exception: # noqa: BLE001
|
|
193
|
+
pass
|
|
194
|
+
except Exception: # noqa: BLE001
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
# Send to C extension in background
|
|
198
|
+
record_network_request(
|
|
199
|
+
trace_id,
|
|
200
|
+
url,
|
|
201
|
+
method,
|
|
202
|
+
status,
|
|
203
|
+
success,
|
|
204
|
+
err,
|
|
205
|
+
timestamp_start=t0,
|
|
206
|
+
timestamp_end=t1,
|
|
207
|
+
request_data=req_data,
|
|
208
|
+
response_data=resp_data,
|
|
209
|
+
request_headers=req_headers,
|
|
210
|
+
response_headers=resp_headers,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _capture_and_record_http_client(
|
|
215
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
216
|
+
):
|
|
217
|
+
"""Capture http.client response data in background thread AFTER response is returned to user."""
|
|
218
|
+
resp_data: bytes = b""
|
|
219
|
+
req_headers: bytes = b""
|
|
220
|
+
resp_headers: bytes = b""
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
# Capture headers efficiently
|
|
224
|
+
if HAS_ORJSON:
|
|
225
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
226
|
+
else:
|
|
227
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
228
|
+
|
|
229
|
+
# http.client doesn't easily expose response data, skip body capture
|
|
230
|
+
except Exception: # noqa: BLE001
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
# Send to C extension in background
|
|
234
|
+
record_network_request(
|
|
235
|
+
trace_id,
|
|
236
|
+
url,
|
|
237
|
+
method,
|
|
238
|
+
status,
|
|
239
|
+
success,
|
|
240
|
+
err,
|
|
241
|
+
timestamp_start=t0,
|
|
242
|
+
timestamp_end=t1,
|
|
243
|
+
request_data=req_data,
|
|
244
|
+
response_data=resp_data,
|
|
245
|
+
request_headers=req_headers,
|
|
246
|
+
response_headers=resp_headers,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
###############################################################################
|
|
251
|
+
# Top-level patch function
|
|
252
|
+
###############################################################################
|
|
253
|
+
def patch_requests(domains_to_not_propagate_headers_to: Optional[List[str]] = None):
|
|
254
|
+
"""
|
|
255
|
+
Apply all monkey-patches. Safe to call multiple times.
|
|
256
|
+
|
|
257
|
+
When LD_PRELOAD is active:
|
|
258
|
+
- ALWAYS inject headers (trace_id + funcspan_override)
|
|
259
|
+
- SKIP capture/emission (LD_PRELOAD handles at socket layer)
|
|
260
|
+
"""
|
|
261
|
+
exclude = domains_to_not_propagate_headers_to or []
|
|
262
|
+
preload_active = _tee_preload_active()
|
|
263
|
+
|
|
264
|
+
# PERFORMANCE DEBUG: Log preload detection
|
|
265
|
+
SF_DEBUG = os.getenv("SF_DEBUG", "false").lower() == "true"
|
|
266
|
+
if SF_DEBUG:
|
|
267
|
+
ld_preload = os.getenv("LD_PRELOAD", "")
|
|
268
|
+
print(f"[[patch_requests]] LD_PRELOAD={ld_preload}", flush=True)
|
|
269
|
+
print(f"[[patch_requests]] preload_active={preload_active}", flush=True)
|
|
270
|
+
print(
|
|
271
|
+
f"[[patch_requests]] Using {'ULTRA-FAST' if preload_active else 'FULL CAPTURE'} path",
|
|
272
|
+
flush=True,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Initialize C extension for ultra-fast header checking (if available)
|
|
276
|
+
if preload_active:
|
|
277
|
+
init_fast_header_check(exclude)
|
|
278
|
+
|
|
279
|
+
# --------------------------------------------------------------------- #
|
|
280
|
+
# 1. Patch `requests.Session.request`
|
|
281
|
+
# --------------------------------------------------------------------- #
|
|
282
|
+
# Save original function BEFORE any patching
|
|
283
|
+
original_request = Session.request
|
|
284
|
+
|
|
285
|
+
if preload_active:
|
|
286
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
287
|
+
if HAS_WRAPT:
|
|
288
|
+
# FASTEST: Use wrapt directly (OTEL-style for minimal overhead)
|
|
289
|
+
def instrumented_request(wrapped, instance, args, kwargs):
|
|
290
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
291
|
+
# args = (method, url, ...), kwargs = {...}
|
|
292
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
293
|
+
|
|
294
|
+
# OPTIMIZED: Avoid kwargs.pop() - use get() with or-pattern (10x faster!)
|
|
295
|
+
headers = kwargs.get("headers") or {}
|
|
296
|
+
|
|
297
|
+
# CRITICAL: Skip if already injected (prevents double injection in requests→urllib3→http.client chain)
|
|
298
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
299
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
300
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
301
|
+
|
|
302
|
+
kwargs["headers"] = headers
|
|
303
|
+
|
|
304
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
305
|
+
return wrapped(*args, **kwargs)
|
|
306
|
+
|
|
307
|
+
wrapt.wrap_function_wrapper(Session, "request", instrumented_request)
|
|
308
|
+
else:
|
|
309
|
+
# Fallback: Direct patching if wrapt not available
|
|
310
|
+
def patched_request(self: Session, method, url, **kwargs): # type: ignore[override]
|
|
311
|
+
# OPTIMIZED: Avoid kwargs.pop() - use get() with or-pattern (10x faster!)
|
|
312
|
+
headers = kwargs.get("headers") or {}
|
|
313
|
+
|
|
314
|
+
# CRITICAL: Skip if already injected (prevents double injection in requests→urllib3→http.client chain)
|
|
315
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
316
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
317
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
318
|
+
|
|
319
|
+
kwargs["headers"] = headers
|
|
320
|
+
|
|
321
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
322
|
+
return original_request(self, method, url, **kwargs)
|
|
323
|
+
|
|
324
|
+
Session.request = patched_request
|
|
325
|
+
requests.Session.request = (
|
|
326
|
+
patched_request # cover direct `requests.Session(...)`
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
else:
|
|
330
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
331
|
+
# PERFORMANCE: Removed thread spawning - saves ~50-200μs per request
|
|
332
|
+
# Direct C extension call instead of background threads
|
|
333
|
+
def patched_request(self: Session, method, url, **kwargs): # type: ignore[override]
|
|
334
|
+
# --- header handling / injection --------------------------------- #
|
|
335
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, kwargs.pop("headers", {}))
|
|
336
|
+
kwargs["headers"] = hdrs
|
|
337
|
+
|
|
338
|
+
status: int = 0
|
|
339
|
+
success: bool = False
|
|
340
|
+
err: str | None = None
|
|
341
|
+
req_data: bytes = b""
|
|
342
|
+
|
|
343
|
+
# PERFORMANCE: Skip request body capture - saves ~10-50μs
|
|
344
|
+
# C extension captures everything at socket layer
|
|
345
|
+
# try:
|
|
346
|
+
# if "json" in kwargs:
|
|
347
|
+
# try:
|
|
348
|
+
# import orjson
|
|
349
|
+
# req_data = orjson.dumps(kwargs["json"])
|
|
350
|
+
# except ImportError:
|
|
351
|
+
# import json
|
|
352
|
+
# req_data = json.dumps(kwargs["json"]).encode('utf-8')
|
|
353
|
+
# elif "data" in kwargs:
|
|
354
|
+
# data = kwargs["data"]
|
|
355
|
+
# if isinstance(data, bytes):
|
|
356
|
+
# req_data = data
|
|
357
|
+
# elif isinstance(data, str):
|
|
358
|
+
# req_data = data.encode('utf-8')
|
|
359
|
+
# except Exception: # noqa: BLE001
|
|
360
|
+
# pass
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
resp = original_request(self, method, url, **kwargs)
|
|
364
|
+
status = resp.status_code
|
|
365
|
+
success = resp.ok
|
|
366
|
+
t1 = int(time.time() * 1_000)
|
|
367
|
+
|
|
368
|
+
# PERFORMANCE: Direct C call (NO thread spawning!) - saves ~50-200μs
|
|
369
|
+
record_network_request(
|
|
370
|
+
trace_id,
|
|
371
|
+
url,
|
|
372
|
+
str(method).upper(),
|
|
373
|
+
status,
|
|
374
|
+
success,
|
|
375
|
+
None,
|
|
376
|
+
timestamp_start=t0,
|
|
377
|
+
timestamp_end=t1,
|
|
378
|
+
request_data=req_data,
|
|
379
|
+
response_data=b"",
|
|
380
|
+
request_headers=b"",
|
|
381
|
+
response_headers=b"",
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
return resp
|
|
385
|
+
except Exception as exc: # noqa: BLE001
|
|
386
|
+
err = str(exc)[:255]
|
|
387
|
+
t1 = int(time.time() * 1_000)
|
|
388
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
389
|
+
record_network_request(
|
|
390
|
+
trace_id,
|
|
391
|
+
url,
|
|
392
|
+
str(method).upper(),
|
|
393
|
+
status,
|
|
394
|
+
success,
|
|
395
|
+
err,
|
|
396
|
+
timestamp_start=t0,
|
|
397
|
+
timestamp_end=t1,
|
|
398
|
+
request_data=req_data,
|
|
399
|
+
response_data=b"",
|
|
400
|
+
request_headers=b"",
|
|
401
|
+
response_headers=b"",
|
|
402
|
+
)
|
|
403
|
+
raise
|
|
404
|
+
|
|
405
|
+
if not HAS_WRAPT:
|
|
406
|
+
Session.request = patched_request
|
|
407
|
+
requests.Session.request = (
|
|
408
|
+
patched_request # cover direct `requests.Session(...)`
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# --------------------------------------------------------------------- #
|
|
412
|
+
# 2. Patch urllib3's low-level ConnectionPool.urlopen (used by requests)
|
|
413
|
+
# --------------------------------------------------------------------- #
|
|
414
|
+
# Save original function BEFORE any patching
|
|
415
|
+
original_urlopen = urllib3.connectionpool.HTTPConnectionPool.urlopen
|
|
416
|
+
|
|
417
|
+
if preload_active:
|
|
418
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
419
|
+
if HAS_WRAPT:
|
|
420
|
+
# FASTEST: Use wrapt directly (OTEL-style for minimal overhead)
|
|
421
|
+
def instrumented_urlopen(wrapped, instance, args, kwargs):
|
|
422
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
423
|
+
# args = (method, url, ...), kwargs = {body, headers, ...}
|
|
424
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
425
|
+
|
|
426
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
427
|
+
headers = kwargs.get("headers") or {}
|
|
428
|
+
|
|
429
|
+
# CRITICAL: Skip if already injected by requests.Session (prevents double injection)
|
|
430
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
431
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
432
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
433
|
+
|
|
434
|
+
kwargs["headers"] = headers
|
|
435
|
+
|
|
436
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
437
|
+
return wrapped(*args, **kwargs)
|
|
438
|
+
|
|
439
|
+
wrapt.wrap_function_wrapper(
|
|
440
|
+
urllib3.connectionpool.HTTPConnectionPool,
|
|
441
|
+
"urlopen",
|
|
442
|
+
instrumented_urlopen,
|
|
443
|
+
)
|
|
444
|
+
else:
|
|
445
|
+
# Fallback: Direct patching if wrapt not available
|
|
446
|
+
def patched_urlopen(self, method, url, body=None, headers=None, **kw): # type: ignore[override]
|
|
447
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
448
|
+
headers = headers or {}
|
|
449
|
+
|
|
450
|
+
# CRITICAL: Skip if already injected by requests.Session (prevents double injection)
|
|
451
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
452
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
453
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
454
|
+
|
|
455
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
456
|
+
return original_urlopen(
|
|
457
|
+
self, method, url, body=body, headers=headers, **kw
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
urllib3.connectionpool.HTTPConnectionPool.urlopen = patched_urlopen
|
|
461
|
+
|
|
462
|
+
else:
|
|
463
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
464
|
+
# PERFORMANCE: Removed thread spawning and body capture
|
|
465
|
+
def patched_urlopen(self, method, url, body=None, headers=None, **kw): # type: ignore[override]
|
|
466
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, headers)
|
|
467
|
+
status: int = 0
|
|
468
|
+
success: bool = False
|
|
469
|
+
err: str | None = None
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
resp = original_urlopen(
|
|
473
|
+
self, method, url, body=body, headers=hdrs, **kw
|
|
474
|
+
)
|
|
475
|
+
status = getattr(resp, "status", 0)
|
|
476
|
+
success = status < 400
|
|
477
|
+
t1 = int(time.time() * 1_000)
|
|
478
|
+
|
|
479
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
480
|
+
record_network_request(
|
|
481
|
+
trace_id,
|
|
482
|
+
url,
|
|
483
|
+
str(method).upper(),
|
|
484
|
+
status,
|
|
485
|
+
success,
|
|
486
|
+
None,
|
|
487
|
+
timestamp_start=t0,
|
|
488
|
+
timestamp_end=t1,
|
|
489
|
+
request_data=b"",
|
|
490
|
+
response_data=b"",
|
|
491
|
+
request_headers=b"",
|
|
492
|
+
response_headers=b"",
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
return resp
|
|
496
|
+
except Exception as exc: # noqa: BLE001
|
|
497
|
+
err = str(exc)[:255]
|
|
498
|
+
t1 = int(time.time() * 1_000)
|
|
499
|
+
record_network_request(
|
|
500
|
+
trace_id,
|
|
501
|
+
url,
|
|
502
|
+
str(method).upper(),
|
|
503
|
+
status,
|
|
504
|
+
success,
|
|
505
|
+
err,
|
|
506
|
+
timestamp_start=t0,
|
|
507
|
+
timestamp_end=t1,
|
|
508
|
+
request_data=b"",
|
|
509
|
+
response_data=b"",
|
|
510
|
+
request_headers=b"",
|
|
511
|
+
response_headers=b"",
|
|
512
|
+
)
|
|
513
|
+
raise
|
|
514
|
+
|
|
515
|
+
if not HAS_WRAPT:
|
|
516
|
+
urllib3.connectionpool.HTTPConnectionPool.urlopen = patched_urlopen
|
|
517
|
+
|
|
518
|
+
# --------------------------------------------------------------------- #
|
|
519
|
+
# 3. Patch http.client for "raw" stdlib usage (rare but easy to support)
|
|
520
|
+
# --------------------------------------------------------------------- #
|
|
521
|
+
# Save original function BEFORE any patching
|
|
522
|
+
original_http_client_request = http.client.HTTPConnection.request
|
|
523
|
+
|
|
524
|
+
if preload_active:
|
|
525
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
526
|
+
if HAS_WRAPT:
|
|
527
|
+
# FASTEST: Use wrapt directly (OTEL-style for minimal overhead)
|
|
528
|
+
def instrumented_http_request(wrapped, instance, args, kwargs):
|
|
529
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
530
|
+
# args = (method, url, ...), kwargs = {body, headers, encode_chunked, ...}
|
|
531
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
532
|
+
|
|
533
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
534
|
+
headers = kwargs.get("headers") or {}
|
|
535
|
+
|
|
536
|
+
# CRITICAL: Skip if already injected by requests/urllib3 (prevents triple injection)
|
|
537
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
538
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
539
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
540
|
+
|
|
541
|
+
kwargs["headers"] = headers
|
|
542
|
+
|
|
543
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
544
|
+
return wrapped(*args, **kwargs)
|
|
545
|
+
|
|
546
|
+
wrapt.wrap_function_wrapper(
|
|
547
|
+
http.client.HTTPConnection, "request", instrumented_http_request
|
|
548
|
+
)
|
|
549
|
+
else:
|
|
550
|
+
# Fallback: Direct patching if wrapt not available
|
|
551
|
+
def patched_http_request(self, method, url, body=None, headers=None, *, encode_chunked=False): # type: ignore[override]
|
|
552
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
553
|
+
headers = headers or {}
|
|
554
|
+
|
|
555
|
+
# CRITICAL: Skip if already injected by requests/urllib3 (prevents triple injection)
|
|
556
|
+
if SAILFISH_TRACING_HEADER not in headers:
|
|
557
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
558
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
559
|
+
|
|
560
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
561
|
+
return original_http_client_request(
|
|
562
|
+
self,
|
|
563
|
+
method,
|
|
564
|
+
url,
|
|
565
|
+
body=body,
|
|
566
|
+
headers=headers,
|
|
567
|
+
encode_chunked=encode_chunked,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
http.client.HTTPConnection.request = patched_http_request
|
|
571
|
+
|
|
572
|
+
else:
|
|
573
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
574
|
+
# PERFORMANCE: Removed thread spawning and body capture
|
|
575
|
+
def patched_http_request(self, method, url, body=None, headers=None, *, encode_chunked=False): # type: ignore[override]
|
|
576
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, headers)
|
|
577
|
+
status: int = 0
|
|
578
|
+
success: bool = False
|
|
579
|
+
err: str | None = None
|
|
580
|
+
|
|
581
|
+
try:
|
|
582
|
+
resp = original_http_client_request(
|
|
583
|
+
self,
|
|
584
|
+
method,
|
|
585
|
+
url,
|
|
586
|
+
body=body,
|
|
587
|
+
headers=hdrs,
|
|
588
|
+
encode_chunked=encode_chunked,
|
|
589
|
+
)
|
|
590
|
+
status = getattr(
|
|
591
|
+
self, "response", getattr(resp, "status", 0)
|
|
592
|
+
) # best-effort
|
|
593
|
+
success = bool(status) and status < 400
|
|
594
|
+
t1 = int(time.time() * 1_000)
|
|
595
|
+
|
|
596
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
597
|
+
record_network_request(
|
|
598
|
+
trace_id,
|
|
599
|
+
url,
|
|
600
|
+
str(method).upper(),
|
|
601
|
+
status,
|
|
602
|
+
success,
|
|
603
|
+
None,
|
|
604
|
+
timestamp_start=t0,
|
|
605
|
+
timestamp_end=t1,
|
|
606
|
+
request_data=b"",
|
|
607
|
+
response_data=b"",
|
|
608
|
+
request_headers=b"",
|
|
609
|
+
response_headers=b"",
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
return resp
|
|
613
|
+
except Exception as exc: # noqa: BLE001
|
|
614
|
+
err = str(exc)[:255]
|
|
615
|
+
t1 = int(time.time() * 1_000)
|
|
616
|
+
record_network_request(
|
|
617
|
+
trace_id,
|
|
618
|
+
url,
|
|
619
|
+
str(method).upper(),
|
|
620
|
+
status,
|
|
621
|
+
success,
|
|
622
|
+
err,
|
|
623
|
+
timestamp_start=t0,
|
|
624
|
+
timestamp_end=t1,
|
|
625
|
+
request_data=b"",
|
|
626
|
+
response_data=b"",
|
|
627
|
+
request_headers=b"",
|
|
628
|
+
response_headers=b"",
|
|
629
|
+
)
|
|
630
|
+
raise
|
|
631
|
+
|
|
632
|
+
if not HAS_WRAPT:
|
|
633
|
+
http.client.HTTPConnection.request = patched_http_request
|