sf-veritas 0.11.10__cp314-cp314-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.
Files changed (141) hide show
  1. sf_veritas/__init__.py +46 -0
  2. sf_veritas/_auto_preload.py +73 -0
  3. sf_veritas/_sfconfig.c +162 -0
  4. sf_veritas/_sfconfig.cpython-314-x86_64-linux-gnu.so +0 -0
  5. sf_veritas/_sfcrashhandler.c +267 -0
  6. sf_veritas/_sfcrashhandler.cpython-314-x86_64-linux-gnu.so +0 -0
  7. sf_veritas/_sffastlog.c +953 -0
  8. sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
  9. sf_veritas/_sffastnet.c +994 -0
  10. sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
  11. sf_veritas/_sffastnetworkrequest.c +727 -0
  12. sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
  13. sf_veritas/_sffuncspan.c +2791 -0
  14. sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
  15. sf_veritas/_sffuncspan_config.c +730 -0
  16. sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
  17. sf_veritas/_sfheadercheck.c +341 -0
  18. sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
  19. sf_veritas/_sfnetworkhop.c +1454 -0
  20. sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
  21. sf_veritas/_sfservice.c +1223 -0
  22. sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
  23. sf_veritas/_sfteepreload.c +6227 -0
  24. sf_veritas/app_config.py +57 -0
  25. sf_veritas/cli.py +336 -0
  26. sf_veritas/constants.py +10 -0
  27. sf_veritas/custom_excepthook.py +304 -0
  28. sf_veritas/custom_log_handler.py +146 -0
  29. sf_veritas/custom_output_wrapper.py +153 -0
  30. sf_veritas/custom_print.py +153 -0
  31. sf_veritas/django_app.py +5 -0
  32. sf_veritas/env_vars.py +186 -0
  33. sf_veritas/exception_handling_middleware.py +18 -0
  34. sf_veritas/exception_metaclass.py +69 -0
  35. sf_veritas/fast_frame_info.py +116 -0
  36. sf_veritas/fast_network_hop.py +293 -0
  37. sf_veritas/frame_tools.py +112 -0
  38. sf_veritas/funcspan_config_loader.py +693 -0
  39. sf_veritas/function_span_profiler.py +1313 -0
  40. sf_veritas/get_preload_path.py +34 -0
  41. sf_veritas/import_hook.py +62 -0
  42. sf_veritas/infra_details/__init__.py +3 -0
  43. sf_veritas/infra_details/get_infra_details.py +24 -0
  44. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  45. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  46. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  47. sf_veritas/infra_details/running_on/__init__.py +17 -0
  48. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  49. sf_veritas/interceptors.py +543 -0
  50. sf_veritas/libsfnettee.so +0 -0
  51. sf_veritas/local_env_detect.py +118 -0
  52. sf_veritas/package_metadata.py +6 -0
  53. sf_veritas/patches/__init__.py +0 -0
  54. sf_veritas/patches/_patch_tracker.py +74 -0
  55. sf_veritas/patches/concurrent_futures.py +19 -0
  56. sf_veritas/patches/constants.py +1 -0
  57. sf_veritas/patches/exceptions.py +82 -0
  58. sf_veritas/patches/multiprocessing.py +32 -0
  59. sf_veritas/patches/network_libraries/__init__.py +99 -0
  60. sf_veritas/patches/network_libraries/aiohttp.py +294 -0
  61. sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
  62. sf_veritas/patches/network_libraries/http_client.py +670 -0
  63. sf_veritas/patches/network_libraries/httpcore.py +580 -0
  64. sf_veritas/patches/network_libraries/httplib2.py +315 -0
  65. sf_veritas/patches/network_libraries/httpx.py +557 -0
  66. sf_veritas/patches/network_libraries/niquests.py +218 -0
  67. sf_veritas/patches/network_libraries/pycurl.py +399 -0
  68. sf_veritas/patches/network_libraries/requests.py +595 -0
  69. sf_veritas/patches/network_libraries/ssl_socket.py +822 -0
  70. sf_veritas/patches/network_libraries/tornado.py +360 -0
  71. sf_veritas/patches/network_libraries/treq.py +270 -0
  72. sf_veritas/patches/network_libraries/urllib_request.py +483 -0
  73. sf_veritas/patches/network_libraries/utils.py +598 -0
  74. sf_veritas/patches/os.py +17 -0
  75. sf_veritas/patches/threading.py +231 -0
  76. sf_veritas/patches/web_frameworks/__init__.py +54 -0
  77. sf_veritas/patches/web_frameworks/aiohttp.py +798 -0
  78. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +337 -0
  79. sf_veritas/patches/web_frameworks/blacksheep.py +532 -0
  80. sf_veritas/patches/web_frameworks/bottle.py +513 -0
  81. sf_veritas/patches/web_frameworks/cherrypy.py +683 -0
  82. sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
  83. sf_veritas/patches/web_frameworks/django.py +963 -0
  84. sf_veritas/patches/web_frameworks/eve.py +401 -0
  85. sf_veritas/patches/web_frameworks/falcon.py +931 -0
  86. sf_veritas/patches/web_frameworks/fastapi.py +738 -0
  87. sf_veritas/patches/web_frameworks/flask.py +526 -0
  88. sf_veritas/patches/web_frameworks/klein.py +501 -0
  89. sf_veritas/patches/web_frameworks/litestar.py +616 -0
  90. sf_veritas/patches/web_frameworks/pyramid.py +440 -0
  91. sf_veritas/patches/web_frameworks/quart.py +841 -0
  92. sf_veritas/patches/web_frameworks/robyn.py +708 -0
  93. sf_veritas/patches/web_frameworks/sanic.py +874 -0
  94. sf_veritas/patches/web_frameworks/starlette.py +742 -0
  95. sf_veritas/patches/web_frameworks/strawberry.py +1446 -0
  96. sf_veritas/patches/web_frameworks/tornado.py +485 -0
  97. sf_veritas/patches/web_frameworks/utils.py +170 -0
  98. sf_veritas/print_override.py +13 -0
  99. sf_veritas/regular_data_transmitter.py +444 -0
  100. sf_veritas/request_interceptor.py +401 -0
  101. sf_veritas/request_utils.py +550 -0
  102. sf_veritas/segfault_handler.py +116 -0
  103. sf_veritas/server_status.py +1 -0
  104. sf_veritas/shutdown_flag.py +11 -0
  105. sf_veritas/subprocess_startup.py +3 -0
  106. sf_veritas/test_cli.py +145 -0
  107. sf_veritas/thread_local.py +1319 -0
  108. sf_veritas/timeutil.py +114 -0
  109. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  110. sf_veritas/transmitter.py +132 -0
  111. sf_veritas/types.py +47 -0
  112. sf_veritas/unified_interceptor.py +1678 -0
  113. sf_veritas/utils.py +39 -0
  114. sf_veritas-0.11.10.dist-info/METADATA +97 -0
  115. sf_veritas-0.11.10.dist-info/RECORD +141 -0
  116. sf_veritas-0.11.10.dist-info/WHEEL +5 -0
  117. sf_veritas-0.11.10.dist-info/entry_points.txt +2 -0
  118. sf_veritas-0.11.10.dist-info/top_level.txt +1 -0
  119. sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  120. sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  121. sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
  122. sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  123. sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  124. sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
  125. sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  126. sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  127. sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  128. sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  129. sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
  130. sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  131. sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  132. sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  133. sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  134. sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  135. sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  136. sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  137. sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
  138. sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
  139. sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
  140. sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  141. sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
@@ -0,0 +1,153 @@
1
+ # sf_veritas/custom_print.py
2
+ import builtins
3
+ import logging
4
+ import os
5
+ import sys
6
+
7
+ from . import app_config, transmit_exception_to_sailfish
8
+ from .env_vars import SF_DEBUG
9
+ from .thread_local import activate_reentrancy_guards_sys_stdout, get_or_set_sf_trace_id
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # --- Optional native fast path (C extension) ---
14
+ try:
15
+ from . import _sffastlog # compiled extension
16
+
17
+ _FAST_OK = True
18
+ except Exception: # pragma: no cover
19
+ _sffastlog = None
20
+ _FAST_OK = False
21
+
22
+ # Keep an internal one-time init guard for the print fast path
23
+ _FAST_PRINT_READY = False
24
+
25
+ # GraphQL mutation for print statements (must match your server exactly)
26
+ # Mirrors interceptors.PrintInterceptor._QUERY variables/shape.
27
+ _COLLECT_PRINT_MUTATION = """
28
+ mutation CollectPrintStatements(
29
+ $apiKey: String!,
30
+ $serviceUuid: String!,
31
+ $sessionId: String!,
32
+ $contents: String!,
33
+ $reentrancyGuardPreactive: Boolean!,
34
+ $library: String!,
35
+ $timestampMs: String!,
36
+ $version: String!
37
+ ) {
38
+ collectPrintStatements(
39
+ apiKey: $apiKey,
40
+ serviceUuid: $serviceUuid,
41
+ sessionId: $sessionId,
42
+ contents: $contents,
43
+ reentrancyGuardPreactive: $reentrancyGuardPreactive,
44
+ library: $library,
45
+ timestampMs: $timestampMs,
46
+ version: $version
47
+ )
48
+ }
49
+ """.strip()
50
+
51
+
52
+ def _ensure_fast_print_initialized() -> bool:
53
+ """
54
+ Lazily initialize the native print path. Safe to call every print; it becomes a no-op after the first success.
55
+ """
56
+ global _FAST_PRINT_READY
57
+
58
+ if not _FAST_OK or _FAST_PRINT_READY:
59
+ return _FAST_PRINT_READY
60
+
61
+ # We require the same config used elsewhere in the package
62
+ endpoint = getattr(app_config, "_sailfish_graphql_endpoint", None)
63
+ api_key = getattr(app_config, "_sailfish_api_key", None)
64
+ service_uuid = getattr(app_config, "_service_uuid", None)
65
+ library = getattr(app_config, "library", "sailfish-python")
66
+ version = getattr(app_config, "version", "0.0.0")
67
+ http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
68
+
69
+ if not (endpoint and api_key and service_uuid):
70
+ # Not configured yet; stay in Python fallback for now
71
+ return False
72
+
73
+ try:
74
+ ok = _sffastlog.init_print(
75
+ url=endpoint,
76
+ query=_COLLECT_PRINT_MUTATION,
77
+ api_key=str(api_key),
78
+ service_uuid=str(service_uuid),
79
+ library=str(library),
80
+ version=str(version),
81
+ http2=http2,
82
+ )
83
+ _FAST_PRINT_READY = bool(ok)
84
+ except Exception:
85
+ _FAST_PRINT_READY = False
86
+
87
+ return _FAST_PRINT_READY
88
+
89
+
90
+ def custom_print(*args, log=True, **kwargs):
91
+ """
92
+ Custom print function to intercept print statements.
93
+ - Writes to real stdout (sys.__stdout__) to avoid recursion.
94
+ - If log=True, ships the message via native _sffastlog.print_ when available,
95
+ otherwise falls back to the Python interceptor path: sys.stdout.print_interceptor.do_send(...)
96
+ """
97
+ # Tag in debug without creating extra strings when not needed
98
+ activate_reentrancy_guards_sys_stdout()
99
+
100
+ # Build the message ONCE; use it for both console and network
101
+ if args:
102
+ # Fastest reasonable join; avoid f-strings on the hot path
103
+ msg = " ".join(map(str, args))
104
+ else:
105
+ msg = ""
106
+
107
+ # Write to the actual stdout (not our wrapper)
108
+ if SF_DEBUG:
109
+ # Keep your debug envelope; avoid doing any extra work when not in debug
110
+ builtins._original_print( # pylint: disable=protected-access
111
+ "[[CUSTOM-PRINT]]", msg, "[[/CUSTOM-PRINT]]", file=sys.__stdout__, **kwargs
112
+ )
113
+ else:
114
+ builtins._original_print( # pylint: disable=protected-access
115
+ msg, file=sys.__stdout__, **kwargs
116
+ )
117
+
118
+ # Optionally ship to backend
119
+ if not log:
120
+ return
121
+ if not msg.strip():
122
+ return
123
+
124
+ # Get (or set) trace id exactly the way you do elsewhere
125
+ _, trace_id = get_or_set_sf_trace_id()
126
+
127
+ # Try native fast path; otherwise Python fallback via the PrintInterceptor attached to sys.stdout
128
+ if _ensure_fast_print_initialized():
129
+ try:
130
+ # Your GraphQL schema includes "reentrancyGuardPreactive" for prints;
131
+ # keep it false here to mirror previous behavior.
132
+ _sffastlog.print_(contents=msg, session_id=str(trace_id), preactive=0)
133
+ return
134
+ except Exception as e:
135
+ logger.exception(e)
136
+ transmit_exception_to_sailfish(e)
137
+ # fall back below
138
+ pass
139
+
140
+ # Python fallback (existing path)
141
+ # NOTE: sys.stdout is replaced by UnifiedInterceptor, which carries `print_interceptor`.
142
+ # If you’re not using UnifiedInterceptor in a given environment, ensure sys.stdout provides it.
143
+ try:
144
+ sys.stdout.print_interceptor.do_send((msg, trace_id), trace_id)
145
+ except Exception:
146
+ logger.exception(e)
147
+ transmit_exception_to_sailfish(e)
148
+ # As a last resort, swallow to keep print() non-fatal
149
+ if SF_DEBUG:
150
+ builtins._original_print(
151
+ "[[CUSTOM-PRINT-FALLBACK-ERROR]] failed to send print payload [[/CUSTOM-PRINT-FALLBACK-ERROR]]",
152
+ file=sys.__stdout__,
153
+ )
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class SFVeritasConfig(AppConfig):
5
+ name = "sf_veritas"
sf_veritas/env_vars.py ADDED
@@ -0,0 +1,186 @@
1
+ import logging
2
+ import os
3
+
4
+ from .utils import strtobool
5
+
6
+ LITE_DEBUGGING = strtobool(os.getenv("LITE_DEBUGGING", "false"))
7
+ SF_DEBUG = strtobool(os.getenv("SF_DEBUG", "false"))
8
+ SF_DEBUG_TRACES = strtobool(os.getenv("SF_DEBUG_TRACES", "false"))
9
+ SF_INTERNAL = strtobool(os.getenv("SF_INTERNAL", "false"))
10
+ IS_SAILFISH_COLLECTOR = strtobool(os.getenv("IS_SAILFISH_COLLECTOR", "false"))
11
+ STRAWBERRY_DEBUG = strtobool(os.getenv("STRAWBERRY_DEBUG", "false"))
12
+ CAPTURE_STRAWBERRY_ERRORS_WITH_DATA = strtobool(
13
+ os.getenv("CAPTURE_STRAWBERRY_ERRORS_WITH_DATA", "false")
14
+ )
15
+
16
+ # Log filtering - regex pattern to suppress logs from being sent to Sailfish
17
+ # Default: suppress successful (2xx) requests to /healthz and /graphql/ endpoints
18
+ SF_LOG_IGNORE_REGEX = os.getenv(
19
+ "SF_LOG_IGNORE_REGEX", r"HTTP\s(POST|GET)\s(\/healthz|\/graphql\/)\s2\d{2}\s.*"
20
+ )
21
+ PRINT_CONFIGURATION_STATUSES = strtobool(
22
+ os.getenv("PRINT_CONFIGURATION_STATUSES", "false")
23
+ )
24
+
25
+ # Parent death signal control (Linux only)
26
+ # Disables automatic registration for SIGTERM on parent process death
27
+ # Useful if customers have custom signal handling that conflicts
28
+ SF_DISABLE_PARENT_DEATH_SIGNAL = strtobool(
29
+ os.getenv("SF_DISABLE_PARENT_DEATH_SIGNAL", "false")
30
+ )
31
+
32
+ # Parent process monitoring (cross-platform)
33
+ # Interval in milliseconds to check if parent process has died (orphaned)
34
+ # Lower = faster detection, higher = less CPU overhead
35
+ # Set to 0 to disable orphan detection
36
+ SF_PARENT_MONITOR_INTERVAL_MS = int(
37
+ os.getenv("SF_PARENT_MONITOR_INTERVAL_MS", "100") # Default: check every 100ms
38
+ )
39
+
40
+ # Exception Tools
41
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SELF = strtobool(
42
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_SELF", "true")
43
+ )
44
+ SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER = strtobool(
45
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER", "true")
46
+ )
47
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER = strtobool(
48
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER", "true")
49
+ )
50
+ SAILFISH_EXCEPTION_STACK_DEPTH_LOCALS = int(
51
+ os.getenv("SAILFISH_EXCEPTION_STACK_DEPTH_LOCALS", "5")
52
+ )
53
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_DEPTH = os.getenv(
54
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_DEPTH", "full"
55
+ )
56
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE = os.getenv(
57
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE", "line"
58
+ )
59
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE_AT_OFFENSIVE_CALL = os.getenv(
60
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE_AT_OFFENSIVE_CALL", "line"
61
+ )
62
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_VALUES_AT_FULL_DEPTH_EXCEPTION = strtobool(
63
+ os.getenv(
64
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_VALUES_AT_FULL_DEPTH_EXCEPTION", "false"
65
+ )
66
+ )
67
+ SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE = os.getenv(
68
+ "SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE",
69
+ "strawberry.types.info.Info,graphql.type.definition.GraphQLResolveInfo,strawberry.field.StrawberryField",
70
+ )
71
+ SAILFISH_EXCEPTION_FETCH_BEYOND_OFFENDER_DEPTH = int(
72
+ os.getenv("SAILFISH_EXCEPTION_FETCH_BEYOND_OFFENDER_DEPTH", "3")
73
+ )
74
+ SAILFISH_EXCEPTION_FETCH_LOCALS_BEYOND_OFFENDER_DEPTH = int(
75
+ os.getenv(
76
+ "SAILFISH_EXCEPTION_FETCH_LOCALS_BEYOND_OFFENDER_DEPTH", "5"
77
+ ) # Sibyl launch - lower this
78
+ )
79
+ SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_DEPTH = int(
80
+ os.getenv(
81
+ "SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_DEPTH", "3"
82
+ ) # Sibyl launch - lower this
83
+ )
84
+ SAILFISH_EXCEPTION_FETCH_LOCALS_ABOVE_OFFENDER_DEPTH = int(
85
+ os.getenv(
86
+ "SAILFISH_EXCEPTION_FETCH_LOCALS_ABOVE_OFFENDER_DEPTH", "-1"
87
+ ) # Sibyl launch - lower this
88
+ )
89
+ SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_INCLUDE_INSTALLED_PACKAGES = strtobool(
90
+ os.getenv(
91
+ "SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_INCLUDE_INSTALLED_PACKAGES", "false"
92
+ )
93
+ )
94
+
95
+
96
+ def get_log_level():
97
+ if LOG_LEVEL_ENV_VAR == "DEBUG":
98
+ return logging.DEBUG
99
+ if LOG_LEVEL_ENV_VAR == "INFO":
100
+ return logging.INFO
101
+ if LOG_LEVEL_ENV_VAR == "WARN":
102
+ return logging.WARN
103
+ if LOG_LEVEL_ENV_VAR == "ERROR":
104
+ return logging.ERROR
105
+ return logging.CRITICAL
106
+
107
+
108
+ LOG_LEVEL_ENV_VAR = os.getenv("LOG_LEVEL", "INFO")
109
+ LOG_LEVEL = get_log_level()
110
+
111
+ # Function Span Serialization
112
+ SF_FUNCSPAN_PARSE_JSON_STRINGS = strtobool(
113
+ os.getenv("SF_FUNCSPAN_PARSE_JSON_STRINGS", "true")
114
+ )
115
+
116
+ # Function Span Capture Control - Granular configuration
117
+ SF_FUNCSPAN_CAPTURE_ARGUMENTS = strtobool(
118
+ os.getenv("SF_FUNCSPAN_CAPTURE_ARGUMENTS", "true")
119
+ )
120
+ SF_FUNCSPAN_CAPTURE_RETURN_VALUE = strtobool(
121
+ os.getenv("SF_FUNCSPAN_CAPTURE_RETURN_VALUE", "true")
122
+ )
123
+
124
+ # Separate size limits for arguments vs return values
125
+ SF_FUNCSPAN_ARG_LIMIT_MB = int(os.getenv("SF_FUNCSPAN_ARG_LIMIT_MB", "1"))
126
+ SF_FUNCSPAN_RETURN_LIMIT_MB = int(os.getenv("SF_FUNCSPAN_RETURN_LIMIT_MB", "1"))
127
+
128
+ # Auto-capture child functions (default: true)
129
+ # When false, only capture top-level functions, not their children
130
+ SF_FUNCSPAN_AUTOCAPTURE_ALL_CHILD_FUNCTIONS = strtobool(
131
+ os.getenv("SF_FUNCSPAN_AUTOCAPTURE_ALL_CHILD_FUNCTIONS", "true")
132
+ )
133
+
134
+ # Two-tier profiler control:
135
+ # SF_ENABLE_PROFILER: Gates installation of profiler hooks (default: false)
136
+ # SF_ENABLE_FUNCTION_SPANS: Master kill switch for capture/transmission (default: true)
137
+ SF_ENABLE_PROFILER = strtobool(os.getenv("SF_ENABLE_PROFILER", "false"))
138
+
139
+ # Include Django view functions in tracing (default: false)
140
+ # Django view functions are typically framework entry points and already traced by web framework instrumentation
141
+ # Set to true if you want detailed function-level tracing inside Django views
142
+ SF_FUNCSPAN_INCLUDE_DJANGO_VIEW_FUNCTIONS = strtobool(
143
+ os.getenv("SF_FUNCSPAN_INCLUDE_DJANGO_VIEW_FUNCTIONS", "false")
144
+ )
145
+
146
+ # Capture spans from installed packages (site-packages, stdlib, etc.) (default: false)
147
+ # When false, only captures user code; when true, also captures installed libraries
148
+ SF_FUNCSPAN_CAPTURE_INSTALLED_PACKAGES = strtobool(
149
+ os.getenv("SF_FUNCSPAN_CAPTURE_INSTALLED_PACKAGES", "false")
150
+ )
151
+
152
+ # Capture spans from sf_veritas telemetry code itself (default: false)
153
+ # When false, skips sf_veritas code; when true, captures our own telemetry code
154
+ SF_FUNCSPAN_CAPTURE_SF_VERITAS = strtobool(
155
+ os.getenv("SF_FUNCSPAN_CAPTURE_SF_VERITAS", "false")
156
+ )
157
+
158
+ # Network Hop I/O Capture - OTEL-style defaults (bodies OFF for performance)
159
+ SF_NETWORKHOP_CAPTURE_ENABLED = strtobool(
160
+ os.getenv("SF_NETWORKHOP_CAPTURE_ENABLED", "true") # OTEL doesn't capture bodies
161
+ )
162
+ SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS = strtobool(
163
+ os.getenv("SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS", "false")
164
+ )
165
+ SF_NETWORKHOP_CAPTURE_REQUEST_BODY = strtobool(
166
+ os.getenv(
167
+ "SF_NETWORKHOP_CAPTURE_REQUEST_BODY", "false"
168
+ ) # OTEL doesn't capture bodies
169
+ )
170
+ SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS = strtobool(
171
+ os.getenv("SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS", "false")
172
+ )
173
+ SF_NETWORKHOP_CAPTURE_RESPONSE_BODY = strtobool(
174
+ os.getenv(
175
+ "SF_NETWORKHOP_CAPTURE_RESPONSE_BODY", "false"
176
+ ) # OTEL doesn't capture bodies
177
+ )
178
+ SF_NETWORKHOP_REQUEST_LIMIT_MB = int(os.getenv("SF_NETWORKHOP_REQUEST_LIMIT_MB", "1"))
179
+ SF_NETWORKHOP_RESPONSE_LIMIT_MB = int(os.getenv("SF_NETWORKHOP_RESPONSE_LIMIT_MB", "1"))
180
+
181
+ # Route-based suppression for inbound network tracing (comma-separated route patterns with wildcard support)
182
+ # Example: "/healthz, /metrics, /admin/*, /api/v1/status*"
183
+ # Supports wildcards: * (matches any sequence) and ? (matches single character)
184
+ SF_DISABLE_INBOUND_NETWORK_TRACING_ON_ROUTES = os.getenv(
185
+ "SF_DISABLE_INBOUND_NETWORK_TRACING_ON_ROUTES", ""
186
+ )
@@ -0,0 +1,18 @@
1
+ import sys
2
+
3
+ from .custom_excepthook import custom_excepthook
4
+
5
+
6
+ class CustomExceptionMiddleware:
7
+ def __init__(self, app):
8
+ self.app = app
9
+
10
+ async def __call__(self, scope, receive, send):
11
+ try:
12
+ await self.app(scope, receive, send)
13
+ except Exception as exc:
14
+ exc_type, exc_value, exc_traceback = sys.exc_info()
15
+ custom_excepthook(exc_type, exc_value, exc_traceback)
16
+
17
+ def __getattr__(self, attr):
18
+ return getattr(self.original, attr)
@@ -0,0 +1,69 @@
1
+ from abc import ABCMeta
2
+
3
+ from .custom_excepthook import transmit_exception
4
+
5
+
6
+ class ExceptionMeta(ABCMeta):
7
+ """
8
+ Metaclass to add `capture_even_if_caught` functionality to exceptions
9
+ and provide a `transmit_to_sailfish` method for all exceptions.
10
+ """
11
+
12
+ def __new__(cls, name, bases, dct):
13
+ # Wrap or define the `__init__` method
14
+ if "__init__" in dct:
15
+ original_init = dct["__init__"]
16
+
17
+ def wrapped_init(self, *args, **kwargs):
18
+ # Add the `capture_even_if_caught` attribute
19
+ self.capture_even_if_caught = kwargs.pop(
20
+ "capture_even_if_caught", False
21
+ )
22
+ self._handled = False # Ensure `_handled` is initialized
23
+ original_init(self, *args, **kwargs)
24
+
25
+ dct["__init__"] = wrapped_init
26
+ else:
27
+
28
+ def default_init(self, *args, **kwargs):
29
+ # Add the `capture_even_if_caught` attribute
30
+ self.capture_even_if_caught = kwargs.pop(
31
+ "capture_even_if_caught", False
32
+ )
33
+ self._handled = False # Ensure `_handled` is initialized
34
+ super(Exception, self).__init__(*args, **kwargs)
35
+
36
+ dct["__init__"] = default_init
37
+
38
+ # Add `transmit_to_sailfish` method to all exceptions
39
+ def transmit_to_sailfish(self, was_caught: bool = False):
40
+ """
41
+ Transmit this exception to Sailfish.
42
+ """
43
+ if not getattr(self, "_handled", False):
44
+ transmit_exception(type(self), self, self.__traceback__, was_caught)
45
+ setattr(
46
+ self, "_handled", True
47
+ ) # Mark as handled to prevent duplication
48
+
49
+ dct["transmit_to_sailfish"] = transmit_to_sailfish
50
+
51
+ return super().__new__(cls, name, bases, dct)
52
+
53
+ def __call__(cls, *args, **kwargs):
54
+ """
55
+ Intercept exception instantiation to handle `capture_even_if_caught`.
56
+ """
57
+ instance = super().__call__(*args, **kwargs)
58
+ # Automatically handle `capture_even_if_caught` exceptions
59
+ if getattr(instance, "capture_even_if_caught", False):
60
+ instance.transmit_to_sailfish()
61
+ return instance
62
+
63
+
64
+ class PatchedException(Exception, metaclass=ExceptionMeta):
65
+ """
66
+ A patched version of the built-in Exception class with universal interception capabilities.
67
+ """
68
+
69
+ pass
@@ -0,0 +1,116 @@
1
+ """
2
+ Ultra-fast frame introspection using C extension.
3
+ Falls back to Python if C extension is not available.
4
+ """
5
+
6
+ import sysconfig
7
+ from functools import lru_cache
8
+ from typing import Optional, Tuple
9
+
10
+ # Try to import C extension
11
+ try:
12
+ from . import _sfframeinfo
13
+
14
+ _FAST_FRAME_OK = True
15
+ except ImportError:
16
+ _sfframeinfo = None
17
+ _FAST_FRAME_OK = False
18
+
19
+ # Fallback Python implementation
20
+ _STDLIB = sysconfig.get_paths()["stdlib"]
21
+ _SITE_TAGS = ("site-packages", "dist-packages")
22
+ _SKIP_PREFIXES = (_STDLIB, "/usr/local/lib/python", "/usr/lib/python")
23
+
24
+
25
+ @lru_cache(maxsize=512)
26
+ def _is_user_code_py(path: Optional[str]) -> bool:
27
+ """Python fallback for checking if path is user code."""
28
+ if not path or path.startswith("<"):
29
+ return False
30
+ for p in _SKIP_PREFIXES:
31
+ if path.startswith(p):
32
+ return False
33
+ return not any(tag in path for tag in _SITE_TAGS)
34
+
35
+
36
+ def get_code_info(func) -> Optional[Tuple[str, int, str]]:
37
+ """
38
+ Extract (filename, lineno, name) from a function object.
39
+ Returns None if not user code or extraction fails.
40
+
41
+ Uses C extension if available for ~10x speedup.
42
+ """
43
+ if _FAST_FRAME_OK:
44
+ try:
45
+ result = _sfframeinfo.get_code_info(func)
46
+ if result:
47
+ # Result is (filename, lineno, name, is_user)
48
+ return (result[0], result[1], result[2])
49
+ return None
50
+ except Exception:
51
+ pass # Fall back to Python
52
+
53
+ # Python fallback
54
+ try:
55
+ code = getattr(func, "__code__", None)
56
+ if not code:
57
+ return None
58
+
59
+ filename = code.co_filename
60
+ if not _is_user_code_py(filename):
61
+ return None
62
+
63
+ return (filename, code.co_firstlineno, func.__name__)
64
+ except Exception:
65
+ return None
66
+
67
+
68
+ def get_frame_info(frame) -> Optional[Tuple[str, int, str]]:
69
+ """
70
+ Extract (filename, lineno, name) from a frame object.
71
+ Returns None if not user code or extraction fails.
72
+
73
+ Uses C extension if available for ~10x speedup.
74
+ """
75
+ if _FAST_FRAME_OK:
76
+ try:
77
+ result = _sfframeinfo.get_frame_info(frame)
78
+ if result:
79
+ # Result is (filename, lineno, name, is_user)
80
+ return (result[0], result[1], result[2])
81
+ return None
82
+ except Exception:
83
+ pass # Fall back to Python
84
+
85
+ # Python fallback
86
+ try:
87
+ code = frame.f_code
88
+ filename = code.co_filename
89
+ if not _is_user_code_py(filename):
90
+ return None
91
+
92
+ return (filename, frame.f_lineno, code.co_name)
93
+ except Exception:
94
+ return None
95
+
96
+
97
+ def is_user_code_func(func) -> bool:
98
+ """
99
+ Quick check if function is user code.
100
+
101
+ Uses C extension if available for ~10x speedup.
102
+ """
103
+ if _FAST_FRAME_OK:
104
+ try:
105
+ return bool(_sfframeinfo.is_user_code_func(func))
106
+ except Exception:
107
+ pass
108
+
109
+ # Python fallback
110
+ try:
111
+ code = getattr(func, "__code__", None)
112
+ if not code:
113
+ return False
114
+ return _is_user_code_py(code.co_filename)
115
+ except Exception:
116
+ return False