insider-python 0.1.3__tar.gz → 0.1.5__tar.gz

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 (62) hide show
  1. insider_python-0.1.5/PKG-INFO +238 -0
  2. insider_python-0.1.5/README.md +210 -0
  3. {insider_python-0.1.3 → insider_python-0.1.5}/pyproject.toml +1 -1
  4. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/__init__.py +7 -0
  5. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/_envelope.py +2 -2
  6. insider_python-0.1.5/src/insider/_footprint.py +64 -0
  7. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/_version.py +1 -1
  8. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/client.py +298 -4
  9. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/integrations/__init__.py +4 -0
  10. insider_python-0.1.5/src/insider/integrations/django/__init__.py +121 -0
  11. insider_python-0.1.5/src/insider/integrations/django/asgi.py +123 -0
  12. insider_python-0.1.5/src/insider/integrations/django/capture.py +59 -0
  13. insider_python-0.1.5/src/insider/integrations/django/handler.py +133 -0
  14. insider_python-0.1.5/src/insider/integrations/django/perf.py +55 -0
  15. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/integrations/django/request.py +41 -0
  16. insider_python-0.1.5/src/insider/integrations/django/signals.py +79 -0
  17. insider_python-0.1.5/src/insider/integrations/logging/__init__.py +66 -0
  18. insider_python-0.1.5/src/insider/integrations/logging/handler.py +74 -0
  19. insider_python-0.1.5/src/insider/integrations/logging/levels.py +29 -0
  20. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/safety.py +26 -0
  21. insider_python-0.1.5/src/insider/scope.py +195 -0
  22. insider_python-0.1.5/src/insider_python.egg-info/PKG-INFO +238 -0
  23. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider_python.egg-info/SOURCES.txt +10 -0
  24. insider_python-0.1.5/tests/test_asgi_integration.py +138 -0
  25. insider_python-0.1.5/tests/test_capture_log.py +41 -0
  26. insider_python-0.1.5/tests/test_capture_perf.py +60 -0
  27. insider_python-0.1.5/tests/test_django.py +93 -0
  28. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_django_integration.py +29 -11
  29. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_drf_integration.py +16 -12
  30. insider_python-0.1.5/tests/test_logging_integration.py +97 -0
  31. insider_python-0.1.3/PKG-INFO +0 -132
  32. insider_python-0.1.3/README.md +0 -104
  33. insider_python-0.1.3/src/insider/integrations/django/__init__.py +0 -50
  34. insider_python-0.1.3/src/insider/integrations/django/capture.py +0 -43
  35. insider_python-0.1.3/src/insider/integrations/django/handler.py +0 -42
  36. insider_python-0.1.3/src/insider/integrations/django/signals.py +0 -39
  37. insider_python-0.1.3/src/insider/scope.py +0 -54
  38. insider_python-0.1.3/src/insider_python.egg-info/PKG-INFO +0 -132
  39. insider_python-0.1.3/tests/test_django.py +0 -101
  40. {insider_python-0.1.3 → insider_python-0.1.5}/setup.cfg +0 -0
  41. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/contrib/__init__.py +0 -0
  42. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/contrib/django/__init__.py +0 -0
  43. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/contrib/django/apps.py +0 -0
  44. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/contrib/django/middleware.py +0 -0
  45. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/dsn.py +0 -0
  46. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/integrations/django/drf.py +0 -0
  47. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/integrations/django/wsgi.py +0 -0
  48. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/py.typed +0 -0
  49. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/scrubbing.py +0 -0
  50. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/stacktrace.py +0 -0
  51. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider/transport.py +0 -0
  52. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider_python.egg-info/dependency_links.txt +0 -0
  53. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider_python.egg-info/requires.txt +0 -0
  54. {insider_python-0.1.3 → insider_python-0.1.5}/src/insider_python.egg-info/top_level.txt +0 -0
  55. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_capture.py +0 -0
  56. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_dsn.py +0 -0
  57. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_envelope.py +0 -0
  58. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_never_crash.py +0 -0
  59. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_safety.py +0 -0
  60. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_scrubbing.py +0 -0
  61. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_stacktrace.py +0 -0
  62. {insider_python-0.1.3 → insider_python-0.1.5}/tests/test_transport.py +0 -0
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.4
2
+ Name: insider-python
3
+ Version: 0.1.5
4
+ Summary: Python SDK for Insider — ship Beacons to your Insider server.
5
+ Author: Insider
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Insider-Inc/insider-python
8
+ Keywords: insider,telemetry,errors,monitoring,sdk
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: urllib3>=1.26
21
+ Provides-Extra: django
22
+ Requires-Dist: django>=4.2; extra == "django"
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == "dev"
25
+ Requires-Dist: pytest-django>=4.8; extra == "dev"
26
+ Requires-Dist: django>=4.2; extra == "dev"
27
+ Requires-Dist: djangorestframework>=3.14; extra == "dev"
28
+
29
+ # insider-python
30
+
31
+ The Python SDK for [Insider](https://insider.moraks.cloud/).
32
+
33
+ Beam Beacons from your Python service to your Insider server with a
34
+ one-line setup. No runtime overhead on your request path. Never raises
35
+ into your code, no matter what.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install insider-python
41
+ ```
42
+
43
+ For the Django integration:
44
+
45
+ ```bash
46
+ pip install "insider-python[django]"
47
+ ```
48
+
49
+ ## Quick start
50
+
51
+ ### Plain Python
52
+
53
+ ```python
54
+ import insider
55
+
56
+ insider.init(
57
+ dsn="https://<beacon_token>@insider.example.com/<project_uuid>",
58
+ environment="production",
59
+ release="1.2.3",
60
+ )
61
+
62
+ try:
63
+ risky()
64
+ except Exception as exc:
65
+ insider.capture_exception(exc)
66
+ ```
67
+
68
+ Out-of-band events (background jobs, explicit calls) use standalone beacons:
69
+ `capture_exception`, `capture_log`, `capture_perf`.
70
+
71
+ ### Django
72
+
73
+ Initialize in `wsgi.py` or `asgi.py` **before** `get_wsgi_application()` /
74
+ `get_asgi_application()`:
75
+
76
+ #### WSGI (Gunicorn)
77
+
78
+ ```python
79
+ import os
80
+ import insider
81
+ from insider.integrations.django import DjangoIntegration
82
+ from insider.integrations.logging import LoggingIntegration
83
+
84
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
85
+
86
+ insider.init(
87
+ dsn=os.environ.get("INSIDER_DSN"),
88
+ environment="production",
89
+ release="1.2.3",
90
+ enable_logs=True,
91
+ integrations=[DjangoIntegration(), LoggingIntegration()],
92
+ )
93
+
94
+ from django.core.wsgi import get_wsgi_application
95
+ application = get_wsgi_application()
96
+ ```
97
+
98
+ #### ASGI (Daphne / Uvicorn — plain Django)
99
+
100
+ ```python
101
+ import os
102
+ import insider
103
+ from insider.integrations.django import DjangoIntegration
104
+ from insider.integrations.logging import LoggingIntegration
105
+
106
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
107
+
108
+ insider.init(
109
+ dsn=os.environ.get("INSIDER_DSN"),
110
+ environment="production",
111
+ release="1.2.3",
112
+ enable_logs=True,
113
+ integrations=[DjangoIntegration(), LoggingIntegration()],
114
+ )
115
+
116
+ from django.core.asgi import get_asgi_application
117
+ application = get_asgi_application()
118
+ ```
119
+
120
+ #### ASGI + Channels (`ProtocolTypeRouter`)
121
+
122
+ Wrap only the HTTP branch; disable handler auto-perf to avoid double capture:
123
+
124
+ ```python
125
+ import os
126
+ import django
127
+
128
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
129
+
130
+ import django
131
+
132
+ django.setup()
133
+
134
+ import insider
135
+ from insider.integrations.django import DjangoIntegration
136
+ from insider.integrations.django.asgi import wrap_asgi_application
137
+ from insider.integrations.logging import LoggingIntegration
138
+
139
+ insider.init(
140
+ dsn=os.environ.get("INSIDER_DSN"),
141
+ environment="production",
142
+ enable_logs=True,
143
+ integrations=[DjangoIntegration(auto_perf=False), LoggingIntegration()],
144
+ )
145
+
146
+ from channels.routing import ProtocolTypeRouter, URLRouter
147
+ from django.core.asgi import get_asgi_application
148
+
149
+ application = ProtocolTypeRouter({
150
+ "http": wrap_asgi_application(get_asgi_application()),
151
+ "websocket": URLRouter(websocket_urlpatterns),
152
+ })
153
+ ```
154
+
155
+ Set `INSIDER_DEBUG=true` to print which hooks installed at startup.
156
+
157
+ That's the whole setup. **Every HTTP request** emits **one** `kind=request`
158
+ beacon containing:
159
+
160
+ - timing (duration, method, path, status)
161
+ - request context (headers, path, query string)
162
+ - stdlib logs during that request (when `enable_logs=True`)
163
+ - unhandled exception + stack trace (when the request fails)
164
+
165
+ No middleware, no `INSTALLED_APPS`, and no `EXCEPTION_HANDLER` wiring.
166
+
167
+ Disable auto capture on high-traffic apps until sampling lands:
168
+
169
+ ```python
170
+ integrations=[DjangoIntegration(auto_perf=False)]
171
+ ```
172
+
173
+ ### Logging
174
+
175
+ During an HTTP request, stdlib `logging` lines are **buffered into the
176
+ request envelope** — not beamed as separate rows:
177
+
178
+ ```python
179
+ import logging
180
+
181
+ logger = logging.getLogger(__name__)
182
+ logger.info("checkout completed") # → payload.logs[] on the request beacon
183
+ ```
184
+
185
+ Requires `enable_logs=True`, `LoggingIntegration()`, and a configured
186
+ logger level (see your app's `LOGGING` settings).
187
+
188
+ Outside an HTTP request, stdlib logs still beam as standalone `kind=log`
189
+ beacons. For explicit structured events, use `capture_log()`:
190
+
191
+ ```python
192
+ insider.capture_log(
193
+ "User checkout completed",
194
+ level="info",
195
+ source="checkout.service",
196
+ tags={"feature": "checkout"},
197
+ )
198
+ ```
199
+
200
+ Manual perf timings (Celery, custom spans):
201
+
202
+ ```python
203
+ insider.capture_perf(
204
+ op="celery.tasks.send_email",
205
+ duration_ms=842,
206
+ )
207
+ ```
208
+
209
+ ## Footprint kinds
210
+
211
+ | Kind | When |
212
+ |------|------|
213
+ | `request` | One per HTTP request (DjangoIntegration) |
214
+ | `error` | Manual `capture_exception()` outside request cycle |
215
+ | `log` | Manual `capture_log()` or stdlib logs outside request cycle |
216
+ | `perf` | Manual `capture_perf()` for non-HTTP timings |
217
+
218
+ ## Configuration
219
+
220
+ If no DSN is found anywhere, the SDK enters **disabled mode**: every
221
+ public call is a no-op.
222
+
223
+ | Option | Default | Notes |
224
+ |--------|---------|-------|
225
+ | `dsn` | env `INSIDER_DSN` | If absent, SDK is disabled |
226
+ | `environment` | `"production"` | Top-level Footprint field |
227
+ | `release` | `None` | Top-level Footprint field |
228
+ | `enable_logs` | `False` | Buffer stdlib logs into request envelopes |
229
+ | `send_default_pii` | `False` | Required to capture request bodies |
230
+ | `debug` | `False` | Print SDK warnings to stderr |
231
+
232
+ ## Promise
233
+
234
+ The SDK never raises into your code.
235
+
236
+ ## License
237
+
238
+ MIT
@@ -0,0 +1,210 @@
1
+ # insider-python
2
+
3
+ The Python SDK for [Insider](https://insider.moraks.cloud/).
4
+
5
+ Beam Beacons from your Python service to your Insider server with a
6
+ one-line setup. No runtime overhead on your request path. Never raises
7
+ into your code, no matter what.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install insider-python
13
+ ```
14
+
15
+ For the Django integration:
16
+
17
+ ```bash
18
+ pip install "insider-python[django]"
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ### Plain Python
24
+
25
+ ```python
26
+ import insider
27
+
28
+ insider.init(
29
+ dsn="https://<beacon_token>@insider.example.com/<project_uuid>",
30
+ environment="production",
31
+ release="1.2.3",
32
+ )
33
+
34
+ try:
35
+ risky()
36
+ except Exception as exc:
37
+ insider.capture_exception(exc)
38
+ ```
39
+
40
+ Out-of-band events (background jobs, explicit calls) use standalone beacons:
41
+ `capture_exception`, `capture_log`, `capture_perf`.
42
+
43
+ ### Django
44
+
45
+ Initialize in `wsgi.py` or `asgi.py` **before** `get_wsgi_application()` /
46
+ `get_asgi_application()`:
47
+
48
+ #### WSGI (Gunicorn)
49
+
50
+ ```python
51
+ import os
52
+ import insider
53
+ from insider.integrations.django import DjangoIntegration
54
+ from insider.integrations.logging import LoggingIntegration
55
+
56
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
57
+
58
+ insider.init(
59
+ dsn=os.environ.get("INSIDER_DSN"),
60
+ environment="production",
61
+ release="1.2.3",
62
+ enable_logs=True,
63
+ integrations=[DjangoIntegration(), LoggingIntegration()],
64
+ )
65
+
66
+ from django.core.wsgi import get_wsgi_application
67
+ application = get_wsgi_application()
68
+ ```
69
+
70
+ #### ASGI (Daphne / Uvicorn — plain Django)
71
+
72
+ ```python
73
+ import os
74
+ import insider
75
+ from insider.integrations.django import DjangoIntegration
76
+ from insider.integrations.logging import LoggingIntegration
77
+
78
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
79
+
80
+ insider.init(
81
+ dsn=os.environ.get("INSIDER_DSN"),
82
+ environment="production",
83
+ release="1.2.3",
84
+ enable_logs=True,
85
+ integrations=[DjangoIntegration(), LoggingIntegration()],
86
+ )
87
+
88
+ from django.core.asgi import get_asgi_application
89
+ application = get_asgi_application()
90
+ ```
91
+
92
+ #### ASGI + Channels (`ProtocolTypeRouter`)
93
+
94
+ Wrap only the HTTP branch; disable handler auto-perf to avoid double capture:
95
+
96
+ ```python
97
+ import os
98
+ import django
99
+
100
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
101
+
102
+ import django
103
+
104
+ django.setup()
105
+
106
+ import insider
107
+ from insider.integrations.django import DjangoIntegration
108
+ from insider.integrations.django.asgi import wrap_asgi_application
109
+ from insider.integrations.logging import LoggingIntegration
110
+
111
+ insider.init(
112
+ dsn=os.environ.get("INSIDER_DSN"),
113
+ environment="production",
114
+ enable_logs=True,
115
+ integrations=[DjangoIntegration(auto_perf=False), LoggingIntegration()],
116
+ )
117
+
118
+ from channels.routing import ProtocolTypeRouter, URLRouter
119
+ from django.core.asgi import get_asgi_application
120
+
121
+ application = ProtocolTypeRouter({
122
+ "http": wrap_asgi_application(get_asgi_application()),
123
+ "websocket": URLRouter(websocket_urlpatterns),
124
+ })
125
+ ```
126
+
127
+ Set `INSIDER_DEBUG=true` to print which hooks installed at startup.
128
+
129
+ That's the whole setup. **Every HTTP request** emits **one** `kind=request`
130
+ beacon containing:
131
+
132
+ - timing (duration, method, path, status)
133
+ - request context (headers, path, query string)
134
+ - stdlib logs during that request (when `enable_logs=True`)
135
+ - unhandled exception + stack trace (when the request fails)
136
+
137
+ No middleware, no `INSTALLED_APPS`, and no `EXCEPTION_HANDLER` wiring.
138
+
139
+ Disable auto capture on high-traffic apps until sampling lands:
140
+
141
+ ```python
142
+ integrations=[DjangoIntegration(auto_perf=False)]
143
+ ```
144
+
145
+ ### Logging
146
+
147
+ During an HTTP request, stdlib `logging` lines are **buffered into the
148
+ request envelope** — not beamed as separate rows:
149
+
150
+ ```python
151
+ import logging
152
+
153
+ logger = logging.getLogger(__name__)
154
+ logger.info("checkout completed") # → payload.logs[] on the request beacon
155
+ ```
156
+
157
+ Requires `enable_logs=True`, `LoggingIntegration()`, and a configured
158
+ logger level (see your app's `LOGGING` settings).
159
+
160
+ Outside an HTTP request, stdlib logs still beam as standalone `kind=log`
161
+ beacons. For explicit structured events, use `capture_log()`:
162
+
163
+ ```python
164
+ insider.capture_log(
165
+ "User checkout completed",
166
+ level="info",
167
+ source="checkout.service",
168
+ tags={"feature": "checkout"},
169
+ )
170
+ ```
171
+
172
+ Manual perf timings (Celery, custom spans):
173
+
174
+ ```python
175
+ insider.capture_perf(
176
+ op="celery.tasks.send_email",
177
+ duration_ms=842,
178
+ )
179
+ ```
180
+
181
+ ## Footprint kinds
182
+
183
+ | Kind | When |
184
+ |------|------|
185
+ | `request` | One per HTTP request (DjangoIntegration) |
186
+ | `error` | Manual `capture_exception()` outside request cycle |
187
+ | `log` | Manual `capture_log()` or stdlib logs outside request cycle |
188
+ | `perf` | Manual `capture_perf()` for non-HTTP timings |
189
+
190
+ ## Configuration
191
+
192
+ If no DSN is found anywhere, the SDK enters **disabled mode**: every
193
+ public call is a no-op.
194
+
195
+ | Option | Default | Notes |
196
+ |--------|---------|-------|
197
+ | `dsn` | env `INSIDER_DSN` | If absent, SDK is disabled |
198
+ | `environment` | `"production"` | Top-level Footprint field |
199
+ | `release` | `None` | Top-level Footprint field |
200
+ | `enable_logs` | `False` | Buffer stdlib logs into request envelopes |
201
+ | `send_default_pii` | `False` | Required to capture request bodies |
202
+ | `debug` | `False` | Print SDK warnings to stderr |
203
+
204
+ ## Promise
205
+
206
+ The SDK never raises into your code.
207
+
208
+ ## License
209
+
210
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "insider-python"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "Python SDK for Insider — ship Beacons to your Insider server."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -6,6 +6,7 @@ Public API:
6
6
  insider.init(dsn=..., environment=..., release=..., ...)
7
7
  insider.capture_exception(exc, level="error", tags=..., extra=...)
8
8
  insider.capture_message("text", level="info", tags=..., extra=...)
9
+ insider.capture_perf("GET /api/users/", duration_ms=45, status_code=200)
9
10
  insider.flush(timeout=2.0)
10
11
  insider.close(timeout=2.0)
11
12
 
@@ -17,12 +18,15 @@ from ._version import __version__
17
18
  from .client import (
18
19
  Client,
19
20
  capture_exception,
21
+ capture_log,
20
22
  capture_message,
23
+ capture_perf,
21
24
  close,
22
25
  flush,
23
26
  init,
24
27
  )
25
28
  from .dsn import DSN, InvalidDSNError
29
+ from .integrations.logging import LoggingIntegration
26
30
 
27
31
  __all__ = [
28
32
  "Client",
@@ -30,8 +34,11 @@ __all__ = [
30
34
  "InvalidDSNError",
31
35
  "__version__",
32
36
  "capture_exception",
37
+ "capture_log",
33
38
  "capture_message",
39
+ "capture_perf",
34
40
  "close",
35
41
  "flush",
36
42
  "init",
43
+ "LoggingIntegration",
37
44
  ]
@@ -1,5 +1,5 @@
1
1
  """
2
- Beacon envelope construction + size-budget enforcement.
2
+ Footprint envelope construction + size-budget enforcement.
3
3
 
4
4
  `build_envelope` is called from the capture functions in `client.py`. It
5
5
  takes the raw bits (kind, level, message, exception payload, scope,
@@ -58,7 +58,7 @@ def build_envelope(
58
58
  occurred_at: Optional[str] = None,
59
59
  commit_hash: Optional[str] = None,
60
60
  ) -> Dict[str, Any]:
61
- """Assemble the Beacon envelope. Pure: no I/O, no globals."""
61
+ """Assemble the Footprint envelope. Pure: no I/O, no globals."""
62
62
  body: Dict[str, Any] = dict(payload or {})
63
63
  if tags:
64
64
  body["tags"] = tags
@@ -0,0 +1,64 @@
1
+ """Build flat footprint payloads for beam ingest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from ._version import __version__
8
+ from .stacktrace import runtime_payload
9
+
10
+
11
+ def build_footprint_payload(
12
+ *,
13
+ request_id: Optional[str],
14
+ request_path: str,
15
+ request_method: Optional[str],
16
+ request_user: str = "anonymous",
17
+ request_body: Any = None,
18
+ response_body: Any = None,
19
+ response_time: float,
20
+ status_code: int,
21
+ system_logs: Optional[list] = None,
22
+ ip_address: Optional[str] = None,
23
+ user_agent: Optional[str] = None,
24
+ db_query_count: int = 0,
25
+ exception_block: Optional[Dict[str, Any]] = None,
26
+ environment: str = "production",
27
+ release: Optional[str] = None,
28
+ service_name: Optional[str] = None,
29
+ commit_hash: Optional[str] = None,
30
+ ) -> Dict[str, Any]:
31
+ runtime = runtime_payload(__version__)
32
+ stack_trace = None
33
+ exception_name = None
34
+ if exception_block:
35
+ exception_name = exception_block.get("type")
36
+ stack_trace = dict(exception_block)
37
+ if commit_hash:
38
+ stack_trace["commit_hash"] = commit_hash
39
+
40
+ body = request_body
41
+ if body is not None and not isinstance(body, (dict, list, str, int, float, bool)):
42
+ body = str(body)
43
+
44
+ return {
45
+ "request_id": request_id,
46
+ "request_user": request_user,
47
+ "request_path": request_path,
48
+ "request_body": body if body is not None else None,
49
+ "request_method": (request_method or "").lower() or None,
50
+ "response_body": response_body,
51
+ "response_time": float(response_time),
52
+ "status_code": status_code,
53
+ "system_logs": system_logs,
54
+ "ip_address": ip_address,
55
+ "user_agent": user_agent,
56
+ "db_query_count": db_query_count,
57
+ "exception_name": exception_name,
58
+ "stack_trace": stack_trace,
59
+ "service_name": service_name,
60
+ "environment": environment,
61
+ "language": runtime.get("language"),
62
+ "framework": runtime.get("framework"),
63
+ "release": release,
64
+ }
@@ -5,4 +5,4 @@ lookup on every beacon. Bump this and `[project].version` together when
5
5
  cutting a release.
6
6
  """
7
7
 
8
- __version__ = "0.1.3"
8
+ __version__ = "0.1.5"