insider-python 0.1.2__tar.gz → 0.1.4__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 (57) hide show
  1. insider_python-0.1.4/PKG-INFO +174 -0
  2. insider_python-0.1.4/README.md +146 -0
  3. {insider_python-0.1.2 → insider_python-0.1.4}/pyproject.toml +2 -1
  4. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/__init__.py +7 -0
  5. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/_envelope.py +2 -2
  6. insider_python-0.1.4/src/insider/_footprint.py +64 -0
  7. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/_version.py +1 -1
  8. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/client.py +317 -5
  9. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/contrib/django/apps.py +2 -1
  10. insider_python-0.1.4/src/insider/contrib/django/middleware.py +65 -0
  11. insider_python-0.1.4/src/insider/integrations/__init__.py +24 -0
  12. insider_python-0.1.4/src/insider/integrations/django/__init__.py +60 -0
  13. insider_python-0.1.4/src/insider/integrations/django/capture.py +45 -0
  14. insider_python-0.1.4/src/insider/integrations/django/drf.py +40 -0
  15. insider_python-0.1.4/src/insider/integrations/django/handler.py +71 -0
  16. insider_python-0.1.4/src/insider/integrations/django/perf.py +38 -0
  17. insider_python-0.1.4/src/insider/integrations/django/request.py +140 -0
  18. insider_python-0.1.4/src/insider/integrations/django/signals.py +39 -0
  19. insider_python-0.1.4/src/insider/integrations/django/wsgi.py +43 -0
  20. insider_python-0.1.4/src/insider/integrations/logging/__init__.py +66 -0
  21. insider_python-0.1.4/src/insider/integrations/logging/handler.py +74 -0
  22. insider_python-0.1.4/src/insider/integrations/logging/levels.py +29 -0
  23. insider_python-0.1.4/src/insider/scope.py +195 -0
  24. insider_python-0.1.4/src/insider_python.egg-info/PKG-INFO +174 -0
  25. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider_python.egg-info/SOURCES.txt +18 -0
  26. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider_python.egg-info/requires.txt +1 -0
  27. insider_python-0.1.4/tests/test_capture_log.py +41 -0
  28. insider_python-0.1.4/tests/test_capture_perf.py +60 -0
  29. insider_python-0.1.4/tests/test_django.py +93 -0
  30. insider_python-0.1.4/tests/test_django_integration.py +76 -0
  31. insider_python-0.1.4/tests/test_drf_integration.py +63 -0
  32. insider_python-0.1.4/tests/test_logging_integration.py +97 -0
  33. insider_python-0.1.2/PKG-INFO +0 -126
  34. insider_python-0.1.2/README.md +0 -99
  35. insider_python-0.1.2/src/insider/contrib/django/middleware.py +0 -164
  36. insider_python-0.1.2/src/insider/scope.py +0 -54
  37. insider_python-0.1.2/src/insider_python.egg-info/PKG-INFO +0 -126
  38. insider_python-0.1.2/tests/test_django.py +0 -101
  39. {insider_python-0.1.2 → insider_python-0.1.4}/setup.cfg +0 -0
  40. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/contrib/__init__.py +0 -0
  41. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/contrib/django/__init__.py +0 -0
  42. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/dsn.py +0 -0
  43. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/py.typed +0 -0
  44. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/safety.py +0 -0
  45. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/scrubbing.py +0 -0
  46. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/stacktrace.py +0 -0
  47. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider/transport.py +0 -0
  48. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider_python.egg-info/dependency_links.txt +0 -0
  49. {insider_python-0.1.2 → insider_python-0.1.4}/src/insider_python.egg-info/top_level.txt +0 -0
  50. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_capture.py +0 -0
  51. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_dsn.py +0 -0
  52. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_envelope.py +0 -0
  53. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_never_crash.py +0 -0
  54. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_safety.py +0 -0
  55. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_scrubbing.py +0 -0
  56. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_stacktrace.py +0 -0
  57. {insider_python-0.1.2 → insider_python-0.1.4}/tests/test_transport.py +0 -0
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: insider-python
3
+ Version: 0.1.4
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
+
75
+ ```python
76
+ import os
77
+ import insider
78
+ from insider.integrations.django import DjangoIntegration
79
+ from insider.integrations.logging import LoggingIntegration
80
+
81
+ insider.init(
82
+ dsn=os.environ.get("INSIDER_DSN"),
83
+ environment="production",
84
+ release="1.2.3",
85
+ enable_logs=True,
86
+ integrations=[DjangoIntegration(), LoggingIntegration()],
87
+ )
88
+
89
+ from django.core.wsgi import get_wsgi_application
90
+ application = get_wsgi_application()
91
+ ```
92
+
93
+ That's the whole setup. **Every HTTP request** emits **one** `kind=request`
94
+ beacon containing:
95
+
96
+ - timing (duration, method, path, status)
97
+ - request context (headers, path, query string)
98
+ - stdlib logs during that request (when `enable_logs=True`)
99
+ - unhandled exception + stack trace (when the request fails)
100
+
101
+ No middleware, no `INSTALLED_APPS`, and no `EXCEPTION_HANDLER` wiring.
102
+
103
+ Disable auto capture on high-traffic apps until sampling lands:
104
+
105
+ ```python
106
+ integrations=[DjangoIntegration(auto_perf=False)]
107
+ ```
108
+
109
+ ### Logging
110
+
111
+ During an HTTP request, stdlib `logging` lines are **buffered into the
112
+ request envelope** — not beamed as separate rows:
113
+
114
+ ```python
115
+ import logging
116
+
117
+ logger = logging.getLogger(__name__)
118
+ logger.info("checkout completed") # → payload.logs[] on the request beacon
119
+ ```
120
+
121
+ Requires `enable_logs=True`, `LoggingIntegration()`, and a configured
122
+ logger level (see your app's `LOGGING` settings).
123
+
124
+ Outside an HTTP request, stdlib logs still beam as standalone `kind=log`
125
+ beacons. For explicit structured events, use `capture_log()`:
126
+
127
+ ```python
128
+ insider.capture_log(
129
+ "User checkout completed",
130
+ level="info",
131
+ source="checkout.service",
132
+ tags={"feature": "checkout"},
133
+ )
134
+ ```
135
+
136
+ Manual perf timings (Celery, custom spans):
137
+
138
+ ```python
139
+ insider.capture_perf(
140
+ op="celery.tasks.send_email",
141
+ duration_ms=842,
142
+ )
143
+ ```
144
+
145
+ ## Footprint kinds
146
+
147
+ | Kind | When |
148
+ |------|------|
149
+ | `request` | One per HTTP request (DjangoIntegration) |
150
+ | `error` | Manual `capture_exception()` outside request cycle |
151
+ | `log` | Manual `capture_log()` or stdlib logs outside request cycle |
152
+ | `perf` | Manual `capture_perf()` for non-HTTP timings |
153
+
154
+ ## Configuration
155
+
156
+ If no DSN is found anywhere, the SDK enters **disabled mode**: every
157
+ public call is a no-op.
158
+
159
+ | Option | Default | Notes |
160
+ |--------|---------|-------|
161
+ | `dsn` | env `INSIDER_DSN` | If absent, SDK is disabled |
162
+ | `environment` | `"production"` | Top-level Footprint field |
163
+ | `release` | `None` | Top-level Footprint field |
164
+ | `enable_logs` | `False` | Buffer stdlib logs into request envelopes |
165
+ | `send_default_pii` | `False` | Required to capture request bodies |
166
+ | `debug` | `False` | Print SDK warnings to stderr |
167
+
168
+ ## Promise
169
+
170
+ The SDK never raises into your code.
171
+
172
+ ## License
173
+
174
+ MIT
@@ -0,0 +1,146 @@
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
+
47
+ ```python
48
+ import os
49
+ import insider
50
+ from insider.integrations.django import DjangoIntegration
51
+ from insider.integrations.logging import LoggingIntegration
52
+
53
+ insider.init(
54
+ dsn=os.environ.get("INSIDER_DSN"),
55
+ environment="production",
56
+ release="1.2.3",
57
+ enable_logs=True,
58
+ integrations=[DjangoIntegration(), LoggingIntegration()],
59
+ )
60
+
61
+ from django.core.wsgi import get_wsgi_application
62
+ application = get_wsgi_application()
63
+ ```
64
+
65
+ That's the whole setup. **Every HTTP request** emits **one** `kind=request`
66
+ beacon containing:
67
+
68
+ - timing (duration, method, path, status)
69
+ - request context (headers, path, query string)
70
+ - stdlib logs during that request (when `enable_logs=True`)
71
+ - unhandled exception + stack trace (when the request fails)
72
+
73
+ No middleware, no `INSTALLED_APPS`, and no `EXCEPTION_HANDLER` wiring.
74
+
75
+ Disable auto capture on high-traffic apps until sampling lands:
76
+
77
+ ```python
78
+ integrations=[DjangoIntegration(auto_perf=False)]
79
+ ```
80
+
81
+ ### Logging
82
+
83
+ During an HTTP request, stdlib `logging` lines are **buffered into the
84
+ request envelope** — not beamed as separate rows:
85
+
86
+ ```python
87
+ import logging
88
+
89
+ logger = logging.getLogger(__name__)
90
+ logger.info("checkout completed") # → payload.logs[] on the request beacon
91
+ ```
92
+
93
+ Requires `enable_logs=True`, `LoggingIntegration()`, and a configured
94
+ logger level (see your app's `LOGGING` settings).
95
+
96
+ Outside an HTTP request, stdlib logs still beam as standalone `kind=log`
97
+ beacons. For explicit structured events, use `capture_log()`:
98
+
99
+ ```python
100
+ insider.capture_log(
101
+ "User checkout completed",
102
+ level="info",
103
+ source="checkout.service",
104
+ tags={"feature": "checkout"},
105
+ )
106
+ ```
107
+
108
+ Manual perf timings (Celery, custom spans):
109
+
110
+ ```python
111
+ insider.capture_perf(
112
+ op="celery.tasks.send_email",
113
+ duration_ms=842,
114
+ )
115
+ ```
116
+
117
+ ## Footprint kinds
118
+
119
+ | Kind | When |
120
+ |------|------|
121
+ | `request` | One per HTTP request (DjangoIntegration) |
122
+ | `error` | Manual `capture_exception()` outside request cycle |
123
+ | `log` | Manual `capture_log()` or stdlib logs outside request cycle |
124
+ | `perf` | Manual `capture_perf()` for non-HTTP timings |
125
+
126
+ ## Configuration
127
+
128
+ If no DSN is found anywhere, the SDK enters **disabled mode**: every
129
+ public call is a no-op.
130
+
131
+ | Option | Default | Notes |
132
+ |--------|---------|-------|
133
+ | `dsn` | env `INSIDER_DSN` | If absent, SDK is disabled |
134
+ | `environment` | `"production"` | Top-level Footprint field |
135
+ | `release` | `None` | Top-level Footprint field |
136
+ | `enable_logs` | `False` | Buffer stdlib logs into request envelopes |
137
+ | `send_default_pii` | `False` | Required to capture request bodies |
138
+ | `debug` | `False` | Print SDK warnings to stderr |
139
+
140
+ ## Promise
141
+
142
+ The SDK never raises into your code.
143
+
144
+ ## License
145
+
146
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "insider-python"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Python SDK for Insider — ship Beacons to your Insider server."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -32,6 +32,7 @@ dev = [
32
32
  "pytest>=8",
33
33
  "pytest-django>=4.8",
34
34
  "django>=4.2",
35
+ "djangorestframework>=3.14",
35
36
  ]
36
37
 
37
38
  [project.urls]
@@ -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.2"
8
+ __version__ = "0.1.4"