sf-veritas 0.10.3__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.

Potentially problematic release.


This version of sf-veritas might be problematic. Click here for more details.

Files changed (132) hide show
  1. sf_veritas/__init__.py +20 -0
  2. sf_veritas/_sffastlog.c +889 -0
  3. sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
  4. sf_veritas/_sffastnet.c +924 -0
  5. sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
  6. sf_veritas/_sffastnetworkrequest.c +730 -0
  7. sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
  8. sf_veritas/_sffuncspan.c +2155 -0
  9. sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
  10. sf_veritas/_sffuncspan_config.c +617 -0
  11. sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
  12. sf_veritas/_sfheadercheck.c +341 -0
  13. sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
  14. sf_veritas/_sfnetworkhop.c +1451 -0
  15. sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
  16. sf_veritas/_sfservice.c +1175 -0
  17. sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
  18. sf_veritas/_sfteepreload.c +5167 -0
  19. sf_veritas/app_config.py +49 -0
  20. sf_veritas/cli.py +336 -0
  21. sf_veritas/constants.py +10 -0
  22. sf_veritas/custom_excepthook.py +304 -0
  23. sf_veritas/custom_log_handler.py +129 -0
  24. sf_veritas/custom_output_wrapper.py +144 -0
  25. sf_veritas/custom_print.py +146 -0
  26. sf_veritas/django_app.py +5 -0
  27. sf_veritas/env_vars.py +186 -0
  28. sf_veritas/exception_handling_middleware.py +18 -0
  29. sf_veritas/exception_metaclass.py +69 -0
  30. sf_veritas/fast_frame_info.py +116 -0
  31. sf_veritas/fast_network_hop.py +293 -0
  32. sf_veritas/frame_tools.py +112 -0
  33. sf_veritas/funcspan_config_loader.py +556 -0
  34. sf_veritas/function_span_profiler.py +1174 -0
  35. sf_veritas/import_hook.py +62 -0
  36. sf_veritas/infra_details/__init__.py +3 -0
  37. sf_veritas/infra_details/get_infra_details.py +24 -0
  38. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  39. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  40. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  41. sf_veritas/infra_details/running_on/__init__.py +17 -0
  42. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  43. sf_veritas/interceptors.py +497 -0
  44. sf_veritas/libsfnettee.so +0 -0
  45. sf_veritas/local_env_detect.py +118 -0
  46. sf_veritas/package_metadata.py +6 -0
  47. sf_veritas/patches/__init__.py +0 -0
  48. sf_veritas/patches/concurrent_futures.py +19 -0
  49. sf_veritas/patches/constants.py +1 -0
  50. sf_veritas/patches/exceptions.py +82 -0
  51. sf_veritas/patches/multiprocessing.py +32 -0
  52. sf_veritas/patches/network_libraries/__init__.py +76 -0
  53. sf_veritas/patches/network_libraries/aiohttp.py +281 -0
  54. sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
  55. sf_veritas/patches/network_libraries/http_client.py +419 -0
  56. sf_veritas/patches/network_libraries/httpcore.py +515 -0
  57. sf_veritas/patches/network_libraries/httplib2.py +204 -0
  58. sf_veritas/patches/network_libraries/httpx.py +515 -0
  59. sf_veritas/patches/network_libraries/niquests.py +211 -0
  60. sf_veritas/patches/network_libraries/pycurl.py +385 -0
  61. sf_veritas/patches/network_libraries/requests.py +633 -0
  62. sf_veritas/patches/network_libraries/tornado.py +341 -0
  63. sf_veritas/patches/network_libraries/treq.py +270 -0
  64. sf_veritas/patches/network_libraries/urllib_request.py +468 -0
  65. sf_veritas/patches/network_libraries/utils.py +398 -0
  66. sf_veritas/patches/os.py +17 -0
  67. sf_veritas/patches/threading.py +218 -0
  68. sf_veritas/patches/web_frameworks/__init__.py +54 -0
  69. sf_veritas/patches/web_frameworks/aiohttp.py +793 -0
  70. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +317 -0
  71. sf_veritas/patches/web_frameworks/blacksheep.py +527 -0
  72. sf_veritas/patches/web_frameworks/bottle.py +502 -0
  73. sf_veritas/patches/web_frameworks/cherrypy.py +678 -0
  74. sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
  75. sf_veritas/patches/web_frameworks/django.py +944 -0
  76. sf_veritas/patches/web_frameworks/eve.py +395 -0
  77. sf_veritas/patches/web_frameworks/falcon.py +926 -0
  78. sf_veritas/patches/web_frameworks/fastapi.py +724 -0
  79. sf_veritas/patches/web_frameworks/flask.py +520 -0
  80. sf_veritas/patches/web_frameworks/klein.py +501 -0
  81. sf_veritas/patches/web_frameworks/litestar.py +551 -0
  82. sf_veritas/patches/web_frameworks/pyramid.py +428 -0
  83. sf_veritas/patches/web_frameworks/quart.py +824 -0
  84. sf_veritas/patches/web_frameworks/robyn.py +697 -0
  85. sf_veritas/patches/web_frameworks/sanic.py +857 -0
  86. sf_veritas/patches/web_frameworks/starlette.py +723 -0
  87. sf_veritas/patches/web_frameworks/strawberry.py +813 -0
  88. sf_veritas/patches/web_frameworks/tornado.py +481 -0
  89. sf_veritas/patches/web_frameworks/utils.py +91 -0
  90. sf_veritas/print_override.py +13 -0
  91. sf_veritas/regular_data_transmitter.py +409 -0
  92. sf_veritas/request_interceptor.py +401 -0
  93. sf_veritas/request_utils.py +550 -0
  94. sf_veritas/server_status.py +1 -0
  95. sf_veritas/shutdown_flag.py +11 -0
  96. sf_veritas/subprocess_startup.py +3 -0
  97. sf_veritas/test_cli.py +145 -0
  98. sf_veritas/thread_local.py +970 -0
  99. sf_veritas/timeutil.py +114 -0
  100. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  101. sf_veritas/transmitter.py +132 -0
  102. sf_veritas/types.py +47 -0
  103. sf_veritas/unified_interceptor.py +1580 -0
  104. sf_veritas/utils.py +39 -0
  105. sf_veritas-0.10.3.dist-info/METADATA +97 -0
  106. sf_veritas-0.10.3.dist-info/RECORD +132 -0
  107. sf_veritas-0.10.3.dist-info/WHEEL +5 -0
  108. sf_veritas-0.10.3.dist-info/entry_points.txt +2 -0
  109. sf_veritas-0.10.3.dist-info/top_level.txt +1 -0
  110. sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  111. sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  112. sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
  113. sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  114. sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  115. sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
  116. sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  117. sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  118. sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  119. sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  120. sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
  121. sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  122. sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  123. sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  124. sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  125. sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  126. sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  127. sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  128. sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
  129. sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
  130. sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
  131. sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  132. sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
@@ -0,0 +1,129 @@
1
+ import logging
2
+ from typing import Optional
3
+
4
+ from . import app_config
5
+ from .env_vars import PRINT_CONFIGURATION_STATUSES, SF_DEBUG
6
+ from .interceptors import LogInterceptor
7
+ from .thread_local import get_or_set_sf_trace_id, get_reentrancy_guard_logging_preactive
8
+
9
+ # Try native fast path (compiled C extension). If missing, we fall back.
10
+ try:
11
+ from . import _sffastlog as _FASTMOD
12
+
13
+ _FAST_OK = True
14
+ except Exception:
15
+ _FASTMOD = None
16
+ _FAST_OK = False
17
+
18
+ # Small constant table avoids attribute access for levelname on hot path
19
+ _LEVEL_NAME = {
20
+ 50: "CRITICAL",
21
+ 40: "ERROR",
22
+ 30: "WARNING",
23
+ 20: "INFO",
24
+ 10: "DEBUG",
25
+ 0: "NOTSET",
26
+ }
27
+
28
+
29
+ class CustomLogHandler(logging.Handler, LogInterceptor):
30
+ """
31
+ Ultra-light log handler:
32
+ - Avoid Formatter unless explicitly set.
33
+ - Avoid %-formatting if record.args is empty.
34
+ - Prefer levelno→name map (micro faster than attribute lookup).
35
+ - Fetch/generate trace_id once per emit (unchanged).
36
+ - DIRECT native send via _sffastlog when available; Python fallback otherwise.
37
+ """
38
+
39
+ def __init__(self):
40
+ logging.Handler.__init__(self)
41
+ LogInterceptor.__init__(self, api_key=app_config._sailfish_api_key)
42
+ if PRINT_CONFIGURATION_STATUSES:
43
+ print("Intercepting log statements")
44
+
45
+ def emit(self, record: logging.LogRecord, trace_id: Optional[str] = None):
46
+ # Bind frequently used symbols to locals to reduce attribute/global lookups
47
+ _formatter = self.formatter
48
+ _get_tid = get_or_set_sf_trace_id
49
+ _get_preactive = get_reentrancy_guard_logging_preactive
50
+ _debug = SF_DEBUG
51
+ _level_table = _LEVEL_NAME
52
+ _fast = _FASTMOD
53
+ _fast_ok = _FAST_OK
54
+ _do_send = self.do_send # Python fallback
55
+
56
+ try:
57
+ # FAST PATH message extraction:
58
+ # - If a formatter is attached, defer to it (caller explicitly wants decorations)
59
+ # - Otherwise:
60
+ # - If there are args, we must perform %-format (getMessage)
61
+ # - If no args and msg is already a str, reuse it directly (no str() allocation)
62
+ # - Else fall back to str(msg)
63
+ if _formatter is None:
64
+ if record.args:
65
+ log_entry = record.getMessage()
66
+ else:
67
+ msg = record.msg
68
+ log_entry = msg if (msg.__class__ is str) else str(msg)
69
+ else:
70
+ log_entry = self.format(record)
71
+
72
+ # Cheap ignore check (lives on LogInterceptor); skip everything if ignored
73
+ # Keeps semantics identical while saving work on hot path
74
+ try:
75
+ if self.check_if_contents_should_be_ignored(log_entry):
76
+ return
77
+ except AttributeError:
78
+ # If not defined for some reason, proceed (older builds)
79
+ pass
80
+
81
+ # CHEAP LEVEL NAME:
82
+ lvlno = record.levelno
83
+ log_level = _level_table.get(lvlno)
84
+ if log_level is None: # uncommon/3rd-party custom levels
85
+ log_level = record.levelname
86
+
87
+ # TRACE ID ONCE (unchanged contract)
88
+ if trace_id is None:
89
+ _, trace_id = _get_tid(None)
90
+
91
+ # Debug printing (rarely enabled)
92
+ if _debug:
93
+ # Avoid f-strings to skip extra work when disabled
94
+ print(
95
+ "[[DEBUG custom_log_handler]]",
96
+ "trace_id=" + str(trace_id),
97
+ "[[" + log_level + "]]",
98
+ log_entry,
99
+ "[[/DEBUG]]",
100
+ log=False,
101
+ )
102
+
103
+ # ---------- Native fast path ----------
104
+ if _fast_ok and _fast is not None:
105
+ try:
106
+ preactive = bool(_get_preactive())
107
+ # Single C call → copy → enqueue → return; HTTP happens on native thread
108
+ _fast.log(
109
+ level=log_level or "UNKNOWN",
110
+ contents=log_entry,
111
+ session_id=str(trace_id),
112
+ preactive=preactive,
113
+ )
114
+ return
115
+ except Exception as _e:
116
+ print(
117
+ "[_sffastlog] fast-path failed; falling back:",
118
+ _e,
119
+ log=False,
120
+ )
121
+ # fall through to Python path
122
+
123
+ # ---------- Python fallback (deferred batching/async) ----------
124
+ _do_send((log_level, log_entry, trace_id), trace_id)
125
+
126
+ except Exception as e: # keep lean; avoid storms on storms
127
+ if _debug:
128
+ print("CustomLogHandler.emit error:", e)
129
+ self.handleError(record)
@@ -0,0 +1,144 @@
1
+ # sf_veritas/custom_output_wrapper.py
2
+ import os
3
+ import sys
4
+
5
+ from . import app_config
6
+ from .env_vars import PRINT_CONFIGURATION_STATUSES
7
+ from .interceptors import PrintInterceptor
8
+ from .thread_local import _thread_locals, get_or_set_sf_trace_id
9
+
10
+ # ---- optional native fast path (C extension) ----
11
+ try:
12
+ from . import _sffastlog # compiled extension with ring + libcurl sender
13
+
14
+ _FAST_OK = True
15
+ except Exception: # pragma: no cover
16
+ _sffastlog = None
17
+ _FAST_OK = False
18
+
19
+ _FAST_PRINT_READY = False # one-time init guard
20
+
21
+
22
+ def _ensure_fast_print_initialized() -> bool:
23
+ """Idempotent, cheap check to init native print path once."""
24
+ global _FAST_PRINT_READY
25
+ if not _FAST_OK or _FAST_PRINT_READY:
26
+ return _FAST_PRINT_READY
27
+
28
+ endpoint = getattr(app_config, "_sailfish_graphql_endpoint", None)
29
+ api_key = getattr(app_config, "_sailfish_api_key", None)
30
+ service_uuid = getattr(app_config, "_service_uuid", None)
31
+ library = getattr(app_config, "library", "sailfish-python")
32
+ version = getattr(app_config, "version", "0.0.0")
33
+ http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
34
+
35
+ if not (endpoint and api_key and service_uuid):
36
+ return False # not configured yet
37
+
38
+ # GraphQL mutation for print statements (must match server)
39
+ query = (
40
+ "mutation CollectPrintStatements("
41
+ "$apiKey: String!,"
42
+ "$serviceUuid: String!,"
43
+ "$sessionId: String!,"
44
+ "$contents: String!,"
45
+ "$reentrancyGuardPreactive: Boolean!,"
46
+ "$library: String!,"
47
+ "$timestampMs: String!,"
48
+ "$version: String!"
49
+ "){collectPrintStatements("
50
+ "apiKey:$apiKey,serviceUuid:$serviceUuid,sessionId:$sessionId,"
51
+ "contents:$contents,reentrancyGuardPreactive:$reentrancyGuardPreactive,"
52
+ "library:$library,timestampMs:$timestampMs,version:$version)}"
53
+ )
54
+
55
+ try:
56
+ ok = _sffastlog.init_print(
57
+ url=endpoint,
58
+ query=query,
59
+ api_key=str(api_key),
60
+ service_uuid=str(service_uuid),
61
+ library=str(library),
62
+ version=str(version),
63
+ http2=http2,
64
+ )
65
+ _FAST_PRINT_READY = bool(ok)
66
+ except Exception:
67
+ _FAST_PRINT_READY = False
68
+
69
+ return _FAST_PRINT_READY
70
+
71
+
72
+ class CustomOutputWrapper:
73
+ """
74
+ Ultra-thin sys.stdout wrapper:
75
+ - Writes straight to the real stream to preserve console output.
76
+ - If native fast path is ready, send to C ring immediately.
77
+ - Otherwise, fall back to PrintInterceptor (Python path).
78
+ - No locks, no regex, no unconditional flush.
79
+ """
80
+
81
+ __slots__ = ("original", "print_interceptor")
82
+
83
+ def __init__(self, original):
84
+ self.original = original
85
+ self.print_interceptor = PrintInterceptor()
86
+
87
+ def write(self, msg):
88
+ # Respect your reentrancy guard quickly
89
+ if getattr(_thread_locals, "reentrancy_guard_logging_active", False):
90
+ self.original.write(msg)
91
+ return
92
+
93
+ # Always write to the real stream first (don’t flush unless caller asks)
94
+ self.original.write(msg)
95
+
96
+ # Cheap ignore cases (optional): skip totally empty or pure newline
97
+ if not msg or msg == "\n":
98
+ return
99
+
100
+ # Build once
101
+ message = msg
102
+ _, trace_id = get_or_set_sf_trace_id()
103
+
104
+ # Native fast path (C) — lowest overhead
105
+ if _ensure_fast_print_initialized():
106
+ try:
107
+ _sffastlog.print_(
108
+ contents=message, session_id=str(trace_id), preactive=0
109
+ )
110
+ return
111
+ except Exception:
112
+ pass # fall through to Python fallback
113
+
114
+ # Python fallback path
115
+ self.print_interceptor.do_send((message, trace_id), trace_id)
116
+
117
+ def flush(self):
118
+ # Only flush when caller flushes (keeps latency down)
119
+ self.original.flush()
120
+
121
+ def __getattr__(self, attr):
122
+ return getattr(self.original, attr)
123
+
124
+
125
+ def setup_custom_output_wrappers():
126
+ # Import here to avoid circular dependency
127
+ from .print_override import override_print
128
+
129
+ if PRINT_CONFIGURATION_STATUSES:
130
+ sys.__stdout__.write("setup_custom_output_wrappers\n")
131
+
132
+ # First override print to support log=True/False parameter
133
+ override_print()
134
+
135
+ # Then wrap stdout for interception
136
+ sys.stdout = CustomOutputWrapper(sys.stdout)
137
+
138
+ if PRINT_CONFIGURATION_STATUSES:
139
+ sys.__stdout__.write("setup_custom_output_wrappers...DONE\n")
140
+
141
+
142
+ def get_custom_output_wrapper_django():
143
+ """Django-specific wrapper setup function called by Django patch."""
144
+ setup_custom_output_wrappers()
@@ -0,0 +1,146 @@
1
+ # sf_veritas/custom_print.py
2
+ import builtins
3
+ import os
4
+ import sys
5
+
6
+ from . import app_config
7
+ from .env_vars import SF_DEBUG
8
+ from .thread_local import activate_reentrancy_guards_sys_stdout, get_or_set_sf_trace_id
9
+
10
+ # --- Optional native fast path (C extension) ---
11
+ try:
12
+ from . import _sffastlog # compiled extension
13
+
14
+ _FAST_OK = True
15
+ except Exception: # pragma: no cover
16
+ _sffastlog = None
17
+ _FAST_OK = False
18
+
19
+ # Keep an internal one-time init guard for the print fast path
20
+ _FAST_PRINT_READY = False
21
+
22
+ # GraphQL mutation for print statements (must match your server exactly)
23
+ # Mirrors interceptors.PrintInterceptor._QUERY variables/shape.
24
+ _COLLECT_PRINT_MUTATION = """
25
+ mutation CollectPrintStatements(
26
+ $apiKey: String!,
27
+ $serviceUuid: String!,
28
+ $sessionId: String!,
29
+ $contents: String!,
30
+ $reentrancyGuardPreactive: Boolean!,
31
+ $library: String!,
32
+ $timestampMs: String!,
33
+ $version: String!
34
+ ) {
35
+ collectPrintStatements(
36
+ apiKey: $apiKey,
37
+ serviceUuid: $serviceUuid,
38
+ sessionId: $sessionId,
39
+ contents: $contents,
40
+ reentrancyGuardPreactive: $reentrancyGuardPreactive,
41
+ library: $library,
42
+ timestampMs: $timestampMs,
43
+ version: $version
44
+ )
45
+ }
46
+ """.strip()
47
+
48
+
49
+ def _ensure_fast_print_initialized() -> bool:
50
+ """
51
+ Lazily initialize the native print path. Safe to call every print; it becomes a no-op after the first success.
52
+ """
53
+ global _FAST_PRINT_READY
54
+
55
+ if not _FAST_OK or _FAST_PRINT_READY:
56
+ return _FAST_PRINT_READY
57
+
58
+ # We require the same config used elsewhere in the package
59
+ endpoint = getattr(app_config, "_sailfish_graphql_endpoint", None)
60
+ api_key = getattr(app_config, "_sailfish_api_key", None)
61
+ service_uuid = getattr(app_config, "_service_uuid", None)
62
+ library = getattr(app_config, "library", "sailfish-python")
63
+ version = getattr(app_config, "version", "0.0.0")
64
+ http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
65
+
66
+ if not (endpoint and api_key and service_uuid):
67
+ # Not configured yet; stay in Python fallback for now
68
+ return False
69
+
70
+ try:
71
+ ok = _sffastlog.init_print(
72
+ url=endpoint,
73
+ query=_COLLECT_PRINT_MUTATION,
74
+ api_key=str(api_key),
75
+ service_uuid=str(service_uuid),
76
+ library=str(library),
77
+ version=str(version),
78
+ http2=http2,
79
+ )
80
+ _FAST_PRINT_READY = bool(ok)
81
+ except Exception:
82
+ _FAST_PRINT_READY = False
83
+
84
+ return _FAST_PRINT_READY
85
+
86
+
87
+ def custom_print(*args, log=True, **kwargs):
88
+ """
89
+ Custom print function to intercept print statements.
90
+ - Writes to real stdout (sys.__stdout__) to avoid recursion.
91
+ - If log=True, ships the message via native _sffastlog.print_ when available,
92
+ otherwise falls back to the Python interceptor path: sys.stdout.print_interceptor.do_send(...)
93
+ """
94
+ # Tag in debug without creating extra strings when not needed
95
+ activate_reentrancy_guards_sys_stdout()
96
+
97
+ # Build the message ONCE; use it for both console and network
98
+ if args:
99
+ # Fastest reasonable join; avoid f-strings on the hot path
100
+ msg = " ".join(map(str, args))
101
+ else:
102
+ msg = ""
103
+
104
+ # Write to the actual stdout (not our wrapper)
105
+ if SF_DEBUG:
106
+ # Keep your debug envelope; avoid doing any extra work when not in debug
107
+ builtins._original_print( # pylint: disable=protected-access
108
+ "[[CUSTOM-PRINT]]", msg, "[[/CUSTOM-PRINT]]", file=sys.__stdout__, **kwargs
109
+ )
110
+ else:
111
+ builtins._original_print( # pylint: disable=protected-access
112
+ msg, file=sys.__stdout__, **kwargs
113
+ )
114
+
115
+ # Optionally ship to backend
116
+ if not log:
117
+ return
118
+ if not msg.strip():
119
+ return
120
+
121
+ # Get (or set) trace id exactly the way you do elsewhere
122
+ _, trace_id = get_or_set_sf_trace_id()
123
+
124
+ # Try native fast path; otherwise Python fallback via the PrintInterceptor attached to sys.stdout
125
+ if _ensure_fast_print_initialized():
126
+ try:
127
+ # Your GraphQL schema includes "reentrancyGuardPreactive" for prints;
128
+ # keep it false here to mirror previous behavior.
129
+ _sffastlog.print_(contents=msg, session_id=str(trace_id), preactive=0)
130
+ return
131
+ except Exception:
132
+ # fall back below
133
+ pass
134
+
135
+ # Python fallback (existing path)
136
+ # NOTE: sys.stdout is replaced by UnifiedInterceptor, which carries `print_interceptor`.
137
+ # If you’re not using UnifiedInterceptor in a given environment, ensure sys.stdout provides it.
138
+ try:
139
+ sys.stdout.print_interceptor.do_send((msg, trace_id), trace_id)
140
+ except Exception:
141
+ # As a last resort, swallow to keep print() non-fatal
142
+ if SF_DEBUG:
143
+ builtins._original_print(
144
+ "[[CUSTOM-PRINT-FALLBACK-ERROR]] failed to send print payload [[/CUSTOM-PRINT-FALLBACK-ERROR]]",
145
+ file=sys.__stdout__,
146
+ )
@@ -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)