sf-veritas 0.9.7__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.
Potentially problematic release.
This version of sf-veritas might be problematic. Click here for more details.
- sf_veritas/.gitignore +2 -0
- sf_veritas/__init__.py +4 -0
- sf_veritas/app_config.py +49 -0
- sf_veritas/cli.py +336 -0
- sf_veritas/constants.py +3 -0
- sf_veritas/custom_excepthook.py +285 -0
- sf_veritas/custom_log_handler.py +53 -0
- sf_veritas/custom_output_wrapper.py +107 -0
- sf_veritas/custom_print.py +34 -0
- sf_veritas/django_app.py +5 -0
- sf_veritas/env_vars.py +83 -0
- sf_veritas/exception_handling_middleware.py +18 -0
- sf_veritas/exception_metaclass.py +69 -0
- sf_veritas/frame_tools.py +112 -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 +252 -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 +51 -0
- sf_veritas/patches/network_libraries/aiohttp.py +100 -0
- sf_veritas/patches/network_libraries/curl_cffi.py +93 -0
- sf_veritas/patches/network_libraries/http_client.py +64 -0
- sf_veritas/patches/network_libraries/httpcore.py +152 -0
- sf_veritas/patches/network_libraries/httplib2.py +76 -0
- sf_veritas/patches/network_libraries/httpx.py +123 -0
- sf_veritas/patches/network_libraries/niquests.py +192 -0
- sf_veritas/patches/network_libraries/pycurl.py +71 -0
- sf_veritas/patches/network_libraries/requests.py +187 -0
- sf_veritas/patches/network_libraries/tornado.py +139 -0
- sf_veritas/patches/network_libraries/treq.py +122 -0
- sf_veritas/patches/network_libraries/urllib_request.py +129 -0
- sf_veritas/patches/network_libraries/utils.py +101 -0
- sf_veritas/patches/os.py +17 -0
- sf_veritas/patches/threading.py +32 -0
- sf_veritas/patches/web_frameworks/__init__.py +45 -0
- sf_veritas/patches/web_frameworks/aiohttp.py +133 -0
- sf_veritas/patches/web_frameworks/async_websocket_consumer.py +132 -0
- sf_veritas/patches/web_frameworks/blacksheep.py +107 -0
- sf_veritas/patches/web_frameworks/bottle.py +142 -0
- sf_veritas/patches/web_frameworks/cherrypy.py +246 -0
- sf_veritas/patches/web_frameworks/django.py +307 -0
- sf_veritas/patches/web_frameworks/eve.py +138 -0
- sf_veritas/patches/web_frameworks/falcon.py +229 -0
- sf_veritas/patches/web_frameworks/fastapi.py +145 -0
- sf_veritas/patches/web_frameworks/flask.py +186 -0
- sf_veritas/patches/web_frameworks/klein.py +40 -0
- sf_veritas/patches/web_frameworks/litestar.py +217 -0
- sf_veritas/patches/web_frameworks/pyramid.py +89 -0
- sf_veritas/patches/web_frameworks/quart.py +155 -0
- sf_veritas/patches/web_frameworks/robyn.py +114 -0
- sf_veritas/patches/web_frameworks/sanic.py +120 -0
- sf_veritas/patches/web_frameworks/starlette.py +144 -0
- sf_veritas/patches/web_frameworks/strawberry.py +269 -0
- sf_veritas/patches/web_frameworks/tornado.py +129 -0
- sf_veritas/patches/web_frameworks/utils.py +55 -0
- sf_veritas/print_override.py +13 -0
- sf_veritas/regular_data_transmitter.py +358 -0
- sf_veritas/request_interceptor.py +399 -0
- sf_veritas/request_utils.py +104 -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 +436 -0
- sf_veritas/timeutil.py +114 -0
- sf_veritas/transmit_exception_to_sailfish.py +28 -0
- sf_veritas/transmitter.py +58 -0
- sf_veritas/types.py +44 -0
- sf_veritas/unified_interceptor.py +323 -0
- sf_veritas/utils.py +39 -0
- sf_veritas-0.9.7.dist-info/METADATA +83 -0
- sf_veritas-0.9.7.dist-info/RECORD +86 -0
- sf_veritas-0.9.7.dist-info/WHEEL +4 -0
- sf_veritas-0.9.7.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import builtins
|
|
3
|
+
import functools
|
|
4
|
+
import inspect
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
import threading
|
|
10
|
+
from types import ModuleType
|
|
11
|
+
from typing import Dict, List, Optional, Union
|
|
12
|
+
|
|
13
|
+
from pydantic import validate_call
|
|
14
|
+
|
|
15
|
+
from . import app_config
|
|
16
|
+
from .custom_excepthook import (
|
|
17
|
+
custom_excepthook,
|
|
18
|
+
custom_thread_excepthook,
|
|
19
|
+
start_profiling,
|
|
20
|
+
)
|
|
21
|
+
from .custom_log_handler import CustomLogHandler
|
|
22
|
+
from .env_vars import LOG_LEVEL, PRINT_CONFIGURATION_STATUSES, SF_DEBUG
|
|
23
|
+
from .exception_metaclass import PatchedException
|
|
24
|
+
from .interceptors import PrintInterceptor
|
|
25
|
+
from .patches.network_libraries import patch_all_http_clients
|
|
26
|
+
from .patches.web_frameworks import patch_web_frameworks
|
|
27
|
+
from .shutdown_flag import set_shutdown_flag
|
|
28
|
+
from .thread_local import (
|
|
29
|
+
_thread_locals,
|
|
30
|
+
activate_reentrancy_guards_sys_stdout,
|
|
31
|
+
get_or_set_sf_trace_id,
|
|
32
|
+
get_reentrancy_guard_sys_stdout_active,
|
|
33
|
+
)
|
|
34
|
+
from .local_env_detect import set_sf_is_local_flag
|
|
35
|
+
from .timeutil import TimeSync
|
|
36
|
+
|
|
37
|
+
# from .patches.django import find_and_modify_output_wrapper
|
|
38
|
+
|
|
39
|
+
STRINGS_NOT_FOUND_IN_CALLER_LOCATIONS = {
|
|
40
|
+
"site-packages",
|
|
41
|
+
"dist-packages",
|
|
42
|
+
"venv",
|
|
43
|
+
"/lib/python",
|
|
44
|
+
"\\lib\\python",
|
|
45
|
+
"sf-veritas",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class UnifiedInterceptor:
|
|
50
|
+
def __init__(self):
|
|
51
|
+
self.log_interceptor = CustomLogHandler()
|
|
52
|
+
self.print_interceptor = PrintInterceptor()
|
|
53
|
+
self._original_stdout = sys.stdout # Store the original sys.stdout
|
|
54
|
+
self._original_stderr = sys.stderr # Store the original sys.stderr
|
|
55
|
+
self._lock = threading.Lock()
|
|
56
|
+
|
|
57
|
+
def write(self, message):
|
|
58
|
+
"""
|
|
59
|
+
Custom write method for intercepting sys.stdout.write calls.
|
|
60
|
+
"""
|
|
61
|
+
if get_reentrancy_guard_sys_stdout_active() or (
|
|
62
|
+
hasattr(_thread_locals, "reentrancy_guard_logging_active")
|
|
63
|
+
and _thread_locals.reentrancy_guard_logging_active
|
|
64
|
+
):
|
|
65
|
+
self._original_stdout.write(message)
|
|
66
|
+
self._original_stdout.flush()
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
_, trace_id = get_or_set_sf_trace_id()
|
|
70
|
+
with self._lock:
|
|
71
|
+
# Always write to the original stdout
|
|
72
|
+
self._original_stdout.write(message)
|
|
73
|
+
self._original_stdout.flush()
|
|
74
|
+
|
|
75
|
+
if message.strip():
|
|
76
|
+
if SF_DEBUG:
|
|
77
|
+
# Bypass our custom print/interceptor entirely:
|
|
78
|
+
self._original_stdout.write(
|
|
79
|
+
f"UnifiedInterceptor.write...SENDING DATA...args={(message, trace_id)}\n"
|
|
80
|
+
)
|
|
81
|
+
self._original_stdout.flush()
|
|
82
|
+
self.print_interceptor.do_send((message, trace_id), trace_id)
|
|
83
|
+
|
|
84
|
+
def flush(self):
|
|
85
|
+
"""
|
|
86
|
+
Custom flush method for sys.stdout.
|
|
87
|
+
"""
|
|
88
|
+
self._original_stdout.flush()
|
|
89
|
+
|
|
90
|
+
def create_custom_print(self):
|
|
91
|
+
"""
|
|
92
|
+
Create a custom print function that includes `log` as a keyword argument.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def custom_print(*args, log=True, **kwargs):
|
|
96
|
+
"""
|
|
97
|
+
Custom print function to intercept print statements.
|
|
98
|
+
"""
|
|
99
|
+
# Prepare the message to print
|
|
100
|
+
print_args = (
|
|
101
|
+
(f"[[CUSTOM-PRINT; log={log}]]", *args, "[[/CUSTOM-PRINT]]")
|
|
102
|
+
if SF_DEBUG
|
|
103
|
+
else args
|
|
104
|
+
)
|
|
105
|
+
modified_message = " ".join(map(str, print_args))
|
|
106
|
+
|
|
107
|
+
# Output the message to the original stdout
|
|
108
|
+
activate_reentrancy_guards_sys_stdout()
|
|
109
|
+
self._original_stdout.write(modified_message + "\n")
|
|
110
|
+
self._original_stdout.flush()
|
|
111
|
+
|
|
112
|
+
message = " ".join(map(str, args))
|
|
113
|
+
# If log is True, send the message to the logging system
|
|
114
|
+
if log and message.strip():
|
|
115
|
+
_, trace_id = get_or_set_sf_trace_id()
|
|
116
|
+
if SF_DEBUG:
|
|
117
|
+
# Bypass the interceptor here as well
|
|
118
|
+
self._original_stdout.write(
|
|
119
|
+
f"UnifiedInterceptor.custom_print...SENDING DATA...args={(message, trace_id)}\n"
|
|
120
|
+
)
|
|
121
|
+
self._original_stdout.flush()
|
|
122
|
+
self.print_interceptor.do_send((message, trace_id), trace_id)
|
|
123
|
+
# threading.Thread(
|
|
124
|
+
# target=self.print_interceptor.send, args=(message, trace_id)
|
|
125
|
+
# ).start()
|
|
126
|
+
|
|
127
|
+
return custom_print
|
|
128
|
+
|
|
129
|
+
def __getattr__(self, attr):
|
|
130
|
+
"""
|
|
131
|
+
Delegate attribute access to the original stdout or stderr.
|
|
132
|
+
"""
|
|
133
|
+
if hasattr(self._original_stdout, attr):
|
|
134
|
+
return getattr(self._original_stdout, attr)
|
|
135
|
+
# TODO - Sibyl post-launch - handle stderr interception
|
|
136
|
+
# elif hasattr(self._original_stderr, attr):
|
|
137
|
+
# return getattr(self._original_stderr, attr)
|
|
138
|
+
else:
|
|
139
|
+
raise AttributeError(
|
|
140
|
+
f"'{self.__class__.__name__}' object has no attribute '{attr}'"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def intercept_stdout(self):
|
|
144
|
+
"""
|
|
145
|
+
Replace sys.stdout and builtins.print to intercept all output.
|
|
146
|
+
"""
|
|
147
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
148
|
+
self._original_stdout.write("Intercepting stdout and print...\n")
|
|
149
|
+
self._original_stdout.flush()
|
|
150
|
+
|
|
151
|
+
# Replace the stdout
|
|
152
|
+
sys.stdout = self
|
|
153
|
+
# TODO - Sibyl post-launch - handle stderr interception
|
|
154
|
+
# sys.stderr = self
|
|
155
|
+
|
|
156
|
+
# Create a custom print function with modified signature
|
|
157
|
+
custom_print_function = self.create_custom_print()
|
|
158
|
+
|
|
159
|
+
builtins.print = functools.partial(custom_print_function)
|
|
160
|
+
|
|
161
|
+
# Check if __builtins__ is a module or a dictionary and override accordingly
|
|
162
|
+
if isinstance(__builtins__, dict):
|
|
163
|
+
__builtins__["print"] = custom_print_function
|
|
164
|
+
elif isinstance(__builtins__, ModuleType):
|
|
165
|
+
setattr(__builtins__, "print", custom_print_function)
|
|
166
|
+
|
|
167
|
+
# Explicitly override the print function in the `__main__` module if present
|
|
168
|
+
if "__main__" in sys.modules:
|
|
169
|
+
sys.modules["__main__"].__dict__["print"] = custom_print_function
|
|
170
|
+
|
|
171
|
+
# Ensure `print` is overridden in the `builtins` module reference
|
|
172
|
+
sys.modules["builtins"].print = custom_print_function
|
|
173
|
+
|
|
174
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
175
|
+
self._original_stdout.write("Intercepting stdout and print...DONE\n")
|
|
176
|
+
self._original_stdout.flush()
|
|
177
|
+
|
|
178
|
+
def intercept_exceptions(self):
|
|
179
|
+
"""
|
|
180
|
+
Intercept all uncaught exceptions globally and in threads.
|
|
181
|
+
"""
|
|
182
|
+
start_profiling()
|
|
183
|
+
|
|
184
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
185
|
+
self._original_stdout.write("Intercepting uncaught exceptions...\n")
|
|
186
|
+
self._original_stdout.flush()
|
|
187
|
+
|
|
188
|
+
# Intercept uncaught exceptions globally
|
|
189
|
+
sys.excepthook = custom_excepthook
|
|
190
|
+
|
|
191
|
+
# If available, intercept uncaught exceptions in threads (Python 3.8+)
|
|
192
|
+
if hasattr(threading, "excepthook"):
|
|
193
|
+
threading.excepthook = custom_thread_excepthook
|
|
194
|
+
|
|
195
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
196
|
+
self._original_stdout.write("Intercepting uncaught exceptions...DONE\n")
|
|
197
|
+
self._original_stdout.flush()
|
|
198
|
+
|
|
199
|
+
# TODO - Figure out how to make this work universally
|
|
200
|
+
def patch_exception_class(self):
|
|
201
|
+
"""
|
|
202
|
+
Safely patch `builtins.Exception` with `PatchedException` while providing a fallback.
|
|
203
|
+
"""
|
|
204
|
+
import builtins
|
|
205
|
+
|
|
206
|
+
# Check if Exception has already been patched
|
|
207
|
+
if hasattr(builtins.Exception, "transmit_to_sailfish"):
|
|
208
|
+
return # Already patched, no action needed
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
|
|
212
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
213
|
+
self._original_stdout.write("Monkey-patching Exceptions class...\n")
|
|
214
|
+
self._original_stdout.flush()
|
|
215
|
+
# Backup original Exception
|
|
216
|
+
_ = builtins.Exception
|
|
217
|
+
# Patch built-in Exception
|
|
218
|
+
builtins.Exception = PatchedException
|
|
219
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
220
|
+
self._original_stdout.write("Monkey-patching Exceptions class...DONE\n")
|
|
221
|
+
self._original_stdout.flush()
|
|
222
|
+
except Exception as e:
|
|
223
|
+
# Log or handle failure to patch
|
|
224
|
+
print(f"[Warning] Failed to patch `builtins.Exception`: {e}")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@validate_call
|
|
228
|
+
def setup_interceptors(
|
|
229
|
+
api_key: str,
|
|
230
|
+
graphql_endpoint: str = None,
|
|
231
|
+
service_identifier: Optional[str] = None,
|
|
232
|
+
service_version: Optional[str] = None,
|
|
233
|
+
git_sha: Optional[str] = None,
|
|
234
|
+
service_additional_metadata: Optional[
|
|
235
|
+
Dict[str, Union[str, int, float, None]]
|
|
236
|
+
] = None,
|
|
237
|
+
profiling_mode_enabled: bool = False, # Profiling mode argument to enable or disable profiling
|
|
238
|
+
profiling_max_depth: int = 5,
|
|
239
|
+
domains_to_not_propagate_headers_to: Optional[List[str]] = None,
|
|
240
|
+
site_and_dist_packages_to_collect_local_variables_on: Optional[List[str]] = None,
|
|
241
|
+
setup_global_time_at_app_spinup: bool = True,
|
|
242
|
+
):
|
|
243
|
+
if service_identifier is None:
|
|
244
|
+
service_identifier = os.getenv("SERVICE_VERSION", os.getenv("GIT_SHA"))
|
|
245
|
+
if git_sha is None:
|
|
246
|
+
git_sha = os.getenv("GIT_SHA")
|
|
247
|
+
app_config._service_identifier = service_identifier
|
|
248
|
+
app_config._service_version = service_version
|
|
249
|
+
app_config._git_sha = git_sha
|
|
250
|
+
app_config._service_additional_metadata = service_additional_metadata
|
|
251
|
+
app_config._profiling_mode_enabled = profiling_mode_enabled
|
|
252
|
+
app_config._profiling_max_depth = profiling_max_depth
|
|
253
|
+
app_config._set_site_and_dist_packages_to_collect_local_variables_on = (
|
|
254
|
+
site_and_dist_packages_to_collect_local_variables_on
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Caller location - navigate up the stack to see if the interceptor was set in code -vs- CLI usage
|
|
258
|
+
for frame in inspect.stack():
|
|
259
|
+
if any(
|
|
260
|
+
[
|
|
261
|
+
string_item in frame.filename
|
|
262
|
+
for string_item in STRINGS_NOT_FOUND_IN_CALLER_LOCATIONS
|
|
263
|
+
]
|
|
264
|
+
):
|
|
265
|
+
continue
|
|
266
|
+
app_config._setup_interceptors_call_filename = frame.filename
|
|
267
|
+
app_config._setup_interceptors_call_lineno = frame.lineno
|
|
268
|
+
break
|
|
269
|
+
|
|
270
|
+
# Use provided or default API Key and GraphQL endpoint
|
|
271
|
+
app_config._sailfish_api_key = api_key
|
|
272
|
+
app_config._sailfish_graphql_endpoint = (
|
|
273
|
+
graphql_endpoint or app_config._sailfish_graphql_endpoint
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Check if the interceptors have already been initialized
|
|
277
|
+
if app_config._interceptors_initialized:
|
|
278
|
+
if SF_DEBUG:
|
|
279
|
+
print("[[DEBUG]] Interceptors already set up. Skipping setup.", log=False)
|
|
280
|
+
return # Exit early to prevent duplicate setup
|
|
281
|
+
|
|
282
|
+
if not app_config._sailfish_api_key:
|
|
283
|
+
raise RuntimeError(
|
|
284
|
+
"The 'api_key' parameter is missing. Please provide a valid value."
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
288
|
+
print("Setting up interceptors")
|
|
289
|
+
|
|
290
|
+
# Setup shutdown flag to prevent issues
|
|
291
|
+
atexit.register(set_shutdown_flag)
|
|
292
|
+
|
|
293
|
+
# Setup Global Time Sync
|
|
294
|
+
if setup_global_time_at_app_spinup:
|
|
295
|
+
TimeSync.get_instance()
|
|
296
|
+
|
|
297
|
+
# Determine if instance is local or not
|
|
298
|
+
set_sf_is_local_flag()
|
|
299
|
+
# Setup UnifiedInterceptor
|
|
300
|
+
unified_interceptor = UnifiedInterceptor()
|
|
301
|
+
|
|
302
|
+
# Set up custom exception hook
|
|
303
|
+
unified_interceptor.intercept_exceptions()
|
|
304
|
+
|
|
305
|
+
# Set up logging
|
|
306
|
+
logging.basicConfig(level=LOG_LEVEL)
|
|
307
|
+
logger = logging.getLogger()
|
|
308
|
+
logger.addHandler(CustomLogHandler())
|
|
309
|
+
|
|
310
|
+
# Intercept stdout and print
|
|
311
|
+
unified_interceptor.intercept_stdout()
|
|
312
|
+
|
|
313
|
+
# Set up custom output wrappers
|
|
314
|
+
patch_web_frameworks()
|
|
315
|
+
|
|
316
|
+
# Patch requests and other core HTTP libraries
|
|
317
|
+
patch_all_http_clients(domains_to_not_propagate_headers_to)
|
|
318
|
+
|
|
319
|
+
# Mark interceptors as initialized to prevent re-running
|
|
320
|
+
app_config._interceptors_initialized = True
|
|
321
|
+
|
|
322
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
323
|
+
print("Interceptors setup completed.", log=False)
|
sf_veritas/utils.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def strtobool(val) -> bool:
|
|
6
|
+
"""Convert a string representation of truth to true (1) or false (0).
|
|
7
|
+
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
|
|
8
|
+
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
|
|
9
|
+
'val' is anything else.
|
|
10
|
+
"""
|
|
11
|
+
val = val.lower()
|
|
12
|
+
if val in ("y", "yes", "t", "true", "on", "1"):
|
|
13
|
+
return True
|
|
14
|
+
elif val in ("n", "no", "f", "false", "off", "0"):
|
|
15
|
+
return False
|
|
16
|
+
else:
|
|
17
|
+
raise ValueError("invalid truth value %r" % (val,))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_json_serializable(value):
|
|
21
|
+
try:
|
|
22
|
+
json.dumps(value)
|
|
23
|
+
return True
|
|
24
|
+
except (TypeError, OverflowError):
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def serialize_json_with_exclusions(input_dict) -> Tuple[str, List[str]]:
|
|
29
|
+
serializable_data = {}
|
|
30
|
+
excluded_fields = []
|
|
31
|
+
|
|
32
|
+
for k, v in input_dict.items():
|
|
33
|
+
if is_json_serializable(v):
|
|
34
|
+
serializable_data[k] = v
|
|
35
|
+
else:
|
|
36
|
+
excluded_fields.append(k)
|
|
37
|
+
|
|
38
|
+
json_str = json.dumps(serializable_data)
|
|
39
|
+
return json_str, excluded_fields
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sf-veritas
|
|
3
|
+
Version: 0.9.7
|
|
4
|
+
Summary: Middleware for Django, Flask, and FastAPI to intercept requests, logs, and exceptions.
|
|
5
|
+
Keywords: publishToPublic
|
|
6
|
+
Author: Eric Meadows
|
|
7
|
+
Author-email: em@sailfishqa.com
|
|
8
|
+
Requires-Python: >=3.8,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Provides-Extra: django
|
|
16
|
+
Provides-Extra: fastapi
|
|
17
|
+
Provides-Extra: flask
|
|
18
|
+
Requires-Dist: deepmerge (>=1.1.1,<2.0.0)
|
|
19
|
+
Requires-Dist: fire (>=0.7.0,<0.8.0)
|
|
20
|
+
Requires-Dist: ntplib (>=0.4.0,<0.5.0)
|
|
21
|
+
Requires-Dist: pydantic (>=2.9.2,<3.0.0)
|
|
22
|
+
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
23
|
+
Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
24
|
+
Requires-Dist: tldextract (>=5.1.2,<6.0.0)
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# SF3 Middleware - Currently transmissions are diabled
|
|
28
|
+
|
|
29
|
+
<!-- TODO - turn on thread transmissions -->
|
|
30
|
+
|
|
31
|
+
Middleware for Python (Django, Flask, and FastAPI) to intercept requests, print statements, logs, and exceptions while persisting tracing.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Use Poetry to install:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
poetry add sf-veritas
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Django, etc
|
|
44
|
+
|
|
45
|
+
**Switch the following:**
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
python manage.py runserver 0.0.0.0:8000
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**To:**
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
sf-veritas API-KEY python manage.py runserver 0.0.0.0:8000
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## TODO - Rename all Sailfish artifacts to Sailfish.ai
|
|
58
|
+
|
|
59
|
+
## Network Hop Calculation Time
|
|
60
|
+
|
|
61
|
+
To evaluate the performance impact of this package, we benchmarked 1000 HTTP requests with and without the package enabled.
|
|
62
|
+
|
|
63
|
+
| Configuration | Mean (ms) | Median (ms) | Std Dev (ms) |
|
|
64
|
+
|-------------------|-----------|-------------|--------------|
|
|
65
|
+
| ✅ With Package | 79.12 | 54.00 | 111.18 |
|
|
66
|
+
| ❌ Without Package | 69.70 | 52.00 | 73.78 |
|
|
67
|
+
|
|
68
|
+
> ⚠️ Note: The package introduces a slight increase in mean response time and variance. This trade-off may be acceptable depending on the value the package provides (e.g., additional logging, monitoring, or security features).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Optimized Entrypoint Capture (Post-Refactor)
|
|
73
|
+
|
|
74
|
+
After optimizing how the user-code entrypoint is captured (via faster stack inspection), we observed improved stability and performance across 1015 analyzed requests:
|
|
75
|
+
|
|
76
|
+
| Configuration | Mean (ms) | Median (ms) | Std Dev (ms) |
|
|
77
|
+
|-------------------|-----------|-------------|--------------|
|
|
78
|
+
| ✅ With Package | 142.45 | 138.50 | 80.78 |
|
|
79
|
+
| ❌ Without Package | 131.07 | 127.00 | 35.75 |
|
|
80
|
+
|
|
81
|
+
> ⚠️ The optimized implementation added a slight increase in mean latency (~8.7%), but this tradeoff is offset by improved accuracy of entrypoint capture.
|
|
82
|
+
---
|
|
83
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
sf_veritas/.gitignore,sha256=pNa3wX1hjNC9UH8qdnUypN0lonYjbzw7BKd18FJm2SI,38
|
|
2
|
+
sf_veritas/__init__.py,sha256=fTJxyhfxhYQcUvtZb1Fz1MTaCByUNeo5qZySdJNG570,195
|
|
3
|
+
sf_veritas/app_config.py,sha256=uQzUcZrt-QwlFTLnETeWEt8vq4wBFiVM2TKjB_uijGY,1671
|
|
4
|
+
sf_veritas/cli.py,sha256=b2Dowg5LLyHU-TWzypk0DctNjwqhzFDn-LqHZ3EGkyk,12376
|
|
5
|
+
sf_veritas/constants.py,sha256=eP7X2dr8fOVDkLOYAUgy-LgQwdT-WHRaKQecFhMdZ4s,162
|
|
6
|
+
sf_veritas/custom_excepthook.py,sha256=CDDBLw4w8vLFeOdVcgdoSBfNvlGchyRJW605-KVdqgI,8876
|
|
7
|
+
sf_veritas/custom_log_handler.py,sha256=hyQiI8kytLnfPqcadfVcWnjqwojTAgNe4IrQEKz_BS4,2012
|
|
8
|
+
sf_veritas/custom_output_wrapper.py,sha256=M_i5OQ4e9Fkn0TcsFHuwWHbMgdICCU8JpOGnXimUyXU,3706
|
|
9
|
+
sf_veritas/custom_print.py,sha256=uNFGakL1S57PjhuxLyh5LsKadmJGgp1bGEZgnD_i6tQ,1098
|
|
10
|
+
sf_veritas/django_app.py,sha256=iQR7GB19P2mTa4tSO9-YcMt_86-GcGtdKT6rg2ck-DU,94
|
|
11
|
+
sf_veritas/env_vars.py,sha256=PAuK4xIxZBH7_q921AQegFTWcGnWcVodra25Mnbr36E,2994
|
|
12
|
+
sf_veritas/exception_handling_middleware.py,sha256=o3tNvNoof59JnLwnKv-FnJN_9H8sbQkSJ1wa-BCIS1c,504
|
|
13
|
+
sf_veritas/exception_metaclass.py,sha256=jvem80P8WHjIGoJAP_DO71ADHZhjX42XgRCfafno20g,2450
|
|
14
|
+
sf_veritas/frame_tools.py,sha256=xVpyq-mrQEnwIZqZXpDtYEm6TX5hJkODrg-86IgACqs,3581
|
|
15
|
+
sf_veritas/import_hook.py,sha256=iY7ag7NwjwXNEbsdSmUvDDKn7f2HOVxfCqMhi0cXCIE,2008
|
|
16
|
+
sf_veritas/infra_details/__init__.py,sha256=fm2C1_km1TcbdNXuq8C6Lr8KhHPyUQ3bYYZoZgVYLos,82
|
|
17
|
+
sf_veritas/infra_details/get_infra_details.py,sha256=zhO-lT5xYbZ3ABqcj8bfPuH1Yf234s1c8Kwudc6NtHU,610
|
|
18
|
+
sf_veritas/infra_details/kubernetes/__init__.py,sha256=4g1cFJ7_pbVLzoaaXWOwt6W4Kt8_EBSjR5-sD2L1L4Q,64
|
|
19
|
+
sf_veritas/infra_details/kubernetes/get_cluster_name.py,sha256=q2toNwTmNZmxD7moy8V5gxVQZtaGjNolm4KUHVqiGEY,4680
|
|
20
|
+
sf_veritas/infra_details/kubernetes/get_details.py,sha256=PEzmte-9EC0kCRo-A1R_S0E60RjRKvpwrCmOVG1zSfA,163
|
|
21
|
+
sf_veritas/infra_details/running_on/__init__.py,sha256=zAEbvDQIrafUCIeDQbKaU2orcY1wvKc8z9OL_eyDdeg,271
|
|
22
|
+
sf_veritas/infra_details/running_on/kubernetes.py,sha256=BaIGbtO7aWosmWFn1Uyv_I1DaXICPdOv8AVr1hJodc8,312
|
|
23
|
+
sf_veritas/interceptors.py,sha256=sZvPuv7icqlODDGR1ISJACxB8mh2HxJg-73b18AHHYs,9710
|
|
24
|
+
sf_veritas/local_env_detect.py,sha256=t6LT1JM0rN5njYBLHQ0oSH9FLgL9ldsyj6XpRTbpcGY,3932
|
|
25
|
+
sf_veritas/package_metadata.py,sha256=U06HSL0cDQcvSXwOXFU9HiBCPuAHm8NDbvxlNrmdutc,143
|
|
26
|
+
sf_veritas/patches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
sf_veritas/patches/concurrent_futures.py,sha256=ZqUbg3R5daWpSx2sFgwilAF1D8HDkd95tfPyaASWcKU,562
|
|
28
|
+
sf_veritas/patches/constants.py,sha256=ALgzeZFfzF54_w_q49Ycst7OGocMDv8eHHZkhsJz0vA,87
|
|
29
|
+
sf_veritas/patches/exceptions.py,sha256=m4Zy9MLNxqvfCmexdSSxFXW45A8a_EOthLYwm9yEgkU,2531
|
|
30
|
+
sf_veritas/patches/multiprocessing.py,sha256=g-POVQKQrr7fBmDJmN5jpHy0LCRhmNw_v4ypZfQm_2Y,863
|
|
31
|
+
sf_veritas/patches/network_libraries/__init__.py,sha256=9TxCn84dmfC3PBEd253O3OclYbs9iPUfhfYra_6Y4_Y,2546
|
|
32
|
+
sf_veritas/patches/network_libraries/aiohttp.py,sha256=IaxWgKu3x0k6tkDTlN953DBCi6swPfhPRZJLv-O9tVI,3159
|
|
33
|
+
sf_veritas/patches/network_libraries/curl_cffi.py,sha256=L4ruL5SYfekttpN8kt3ZD8QejY7OodK9vhu7L5FNtXU,3325
|
|
34
|
+
sf_veritas/patches/network_libraries/http_client.py,sha256=Z4lERGyPFiHemSXt_FO1LIvuRtPjJX3hr8h2r-wWUws,1932
|
|
35
|
+
sf_veritas/patches/network_libraries/httpcore.py,sha256=XQa4x44NGdlik4H-yhUQc3HgQ8EhiXfiVQemg85jGlc,5337
|
|
36
|
+
sf_veritas/patches/network_libraries/httplib2.py,sha256=fDZBz70TXXb2cMBDek9AyryJJrATnEMDCVLL-h2cVQ8,2515
|
|
37
|
+
sf_veritas/patches/network_libraries/httpx.py,sha256=4DzB_28SAoWM8GoigKvgz8BO-qaTSf8Qigbxb_0F3C4,4847
|
|
38
|
+
sf_veritas/patches/network_libraries/niquests.py,sha256=sLWemQVmWHs-LJOaAZz7LvsoIY7uLwCWMkO2xSsXMlo,7096
|
|
39
|
+
sf_veritas/patches/network_libraries/pycurl.py,sha256=YqApbIewO33dW5Cn92K4lKWE_afZTCB-AG9iE9Z8vfs,2519
|
|
40
|
+
sf_veritas/patches/network_libraries/requests.py,sha256=gNvuswBshBajHbaGR82wKpJdAl3zdd8OzT9NxDdwtV4,7083
|
|
41
|
+
sf_veritas/patches/network_libraries/tornado.py,sha256=a5m0DhggCg0sw4CUF15rJLmgrP34uRwltyJGFT4fXPE,5002
|
|
42
|
+
sf_veritas/patches/network_libraries/treq.py,sha256=9o7nQNfDi0JMzc6GGtNjO5aT5IQW9m1fO04_qRPFyM0,3951
|
|
43
|
+
sf_veritas/patches/network_libraries/urllib_request.py,sha256=YieQzM9KGxtXSQVGsDwOcDX4nL5J9Ed9obK_VtGF-yg,4397
|
|
44
|
+
sf_veritas/patches/network_libraries/utils.py,sha256=aydgjaGmgSrGMe-NXoBU1UImw-HVtSIRtWZoqepm4pc,3076
|
|
45
|
+
sf_veritas/patches/os.py,sha256=68w74eQ-BugO2oLR3HKSrtQwc2OrR7lWXzPUGqaI8YA,305
|
|
46
|
+
sf_veritas/patches/threading.py,sha256=52xBdu-tM-VAl6qSlgPoq1XUDo-uyQtmlydpAknNeQM,833
|
|
47
|
+
sf_veritas/patches/web_frameworks/__init__.py,sha256=6woJnHwEqhlEZTznjgLoZVq95DzmAtRIkI98JsUgrDg,1237
|
|
48
|
+
sf_veritas/patches/web_frameworks/aiohttp.py,sha256=wXB2w6wn3dz-DU8Z7rjoAEdkM9Qr72u3bNFd3BjxSV0,5538
|
|
49
|
+
sf_veritas/patches/web_frameworks/async_websocket_consumer.py,sha256=eJhYYW7zBWU5HqS-s6M8ZuKneOqXx0kxD9SbUkikqPs,5488
|
|
50
|
+
sf_veritas/patches/web_frameworks/blacksheep.py,sha256=xen18jBvLnc3t2169M3qhWIkr3_FfDhN3zXLMCTcYAE,3871
|
|
51
|
+
sf_veritas/patches/web_frameworks/bottle.py,sha256=_P7IsXa8Gm0EUCnXk04SDliReydIX-a3Lr6g5julb24,5305
|
|
52
|
+
sf_veritas/patches/web_frameworks/cherrypy.py,sha256=fjn_BcK7zBZqJyG-4fYjRivq0oblMTx_4fSdGhm4kwU,10879
|
|
53
|
+
sf_veritas/patches/web_frameworks/django.py,sha256=CknqP0nYvC39spBARqLVlbcx65gaM6rzqmrdddkcRhk,12045
|
|
54
|
+
sf_veritas/patches/web_frameworks/eve.py,sha256=86xWCLE_ynTQzgzvBxcczI2AoSqQKqwA80Ac-lpoWUw,5182
|
|
55
|
+
sf_veritas/patches/web_frameworks/falcon.py,sha256=1YQaJkRBI_hkGoy3BqUAMSuwXgcvGVULMl9La6Xcizc,7619
|
|
56
|
+
sf_veritas/patches/web_frameworks/fastapi.py,sha256=H349IBrUndwCRG7Q_Lrz6V903dQONsG4okhf4LUAczE,5714
|
|
57
|
+
sf_veritas/patches/web_frameworks/flask.py,sha256=wQJGC-P8BwoVJE3ALQLxuckRSOm_dg8oFyiJ34K1GCA,7233
|
|
58
|
+
sf_veritas/patches/web_frameworks/klein.py,sha256=itfQA-g18gHsi02Valti1E-xm3YYIyEBouLICGUhSpQ,1288
|
|
59
|
+
sf_veritas/patches/web_frameworks/litestar.py,sha256=siqjoBE1PuuRIn9LDF-hmOhKFMsA3g-a72QBIFshpyU,8352
|
|
60
|
+
sf_veritas/patches/web_frameworks/pyramid.py,sha256=sQBLUGfY5wNNEvWW6KW1C8vg0PWALAGSR9sY-Nmi1Ds,3553
|
|
61
|
+
sf_veritas/patches/web_frameworks/quart.py,sha256=k98W-yKmRAdSYp4A4tWqAvsN3Sl5zf_JPUvaPrvZZTo,6848
|
|
62
|
+
sf_veritas/patches/web_frameworks/robyn.py,sha256=-v7UpPVyNsVi_XtkOoKnafV8OUtfnSrVaky1rsZ2fS0,5330
|
|
63
|
+
sf_veritas/patches/web_frameworks/sanic.py,sha256=ePR99kGPUhHxH7YpqK1z4VObTegW7_8JYqnAKdLPYkM,4942
|
|
64
|
+
sf_veritas/patches/web_frameworks/starlette.py,sha256=5TW6kxhUtMWCxxIaY6XsaE5LtmV7bW-HCmLoz-j5TDM,5659
|
|
65
|
+
sf_veritas/patches/web_frameworks/strawberry.py,sha256=xDejE6s3EvwMVQU-QcF9hcOtH_yqq9hPmP8fR2c7V8U,9819
|
|
66
|
+
sf_veritas/patches/web_frameworks/tornado.py,sha256=LWHXd5M1rcBNkn9zwTh3Ccl6sBJd7IuZiFitubRyeUQ,5030
|
|
67
|
+
sf_veritas/patches/web_frameworks/utils.py,sha256=Uen_Se3HyF_LFtJClWMaui8hTDneIKvGVJHhxRIfk8E,1412
|
|
68
|
+
sf_veritas/print_override.py,sha256=zE0dynL7xEe1O8XGesU9nh18AW5afAS3wTTD2Ff206I,313
|
|
69
|
+
sf_veritas/regular_data_transmitter.py,sha256=x8q9sgq5b1Tbk3Q4fgGCf_Ankwz_xb-P2VMUVDI1kNM,12444
|
|
70
|
+
sf_veritas/request_interceptor.py,sha256=b221vp-pi5vsBEfGWU9g9dukPEzIqQyAYMnBCLt3ZWU,14428
|
|
71
|
+
sf_veritas/request_utils.py,sha256=pgmPB3cdqf7sEhrrQP-8b68H8cEEa_HZLKQS0ov--tE,3045
|
|
72
|
+
sf_veritas/server_status.py,sha256=VZQJpDrjrLKB61A9OH2fjCdJTraI29iZ6x2oVE8lwnA,23
|
|
73
|
+
sf_veritas/shutdown_flag.py,sha256=ivDhin6dqbvXyhafbslZnTPhWlGTZ15rwyTaxA7HcBM,160
|
|
74
|
+
sf_veritas/subprocess_startup.py,sha256=OoYxLGvXlR2eoyg6s4D1-Gv2i7FvoGazkM0yPQulhog,103
|
|
75
|
+
sf_veritas/test_cli.py,sha256=2xjWTDKiavL4W49n7KsW5-2Wu0xRorTyavlNIJoZh_o,4944
|
|
76
|
+
sf_veritas/thread_local.py,sha256=I_vJDEUu4rOcq7oqLGzULvCqsFlfahtqPsMQrwxvKng,13757
|
|
77
|
+
sf_veritas/timeutil.py,sha256=cUFfmLV300YfbPcokf7dmTEida7uOKrRP5w7iMExoBc,4364
|
|
78
|
+
sf_veritas/transmit_exception_to_sailfish.py,sha256=hKC30o9fEpYuCDwNI3EJ3Qc6hA-Clh2VnsHDmEVQmj4,1024
|
|
79
|
+
sf_veritas/transmitter.py,sha256=_U7b1tUw5OacdxkW6-8uXG1OVMiGLA4vz5WBXulGlHY,2055
|
|
80
|
+
sf_veritas/types.py,sha256=_rfWwt-LOCDQ7K_U64ga6IG_XeWDkoOEdf_LLn_cmjQ,1200
|
|
81
|
+
sf_veritas/unified_interceptor.py,sha256=-RPY2PA5jWS_m27o21mS5rGxLuXmtw_p3UzAWFNIOtg,11737
|
|
82
|
+
sf_veritas/utils.py,sha256=4DWVRCbjnE4nsG_t7A6Qmw35mg0AE5B3oRg_R-WP9PM,1089
|
|
83
|
+
sf_veritas-0.9.7.dist-info/METADATA,sha256=5D_wf2frGgCDB_4oiJAHxj60GPNcy_zDhFxoSZmYz24,2810
|
|
84
|
+
sf_veritas-0.9.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
85
|
+
sf_veritas-0.9.7.dist-info/entry_points.txt,sha256=vzQkbDZ9AUiUKu9jT437eSAdj6V8VCkwaU_s5nRA1KY,55
|
|
86
|
+
sf_veritas-0.9.7.dist-info/RECORD,,
|