fetchnode 0.4.0__tar.gz → 0.4.2__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.
- fetchnode-0.4.2/PKG-INFO +284 -0
- fetchnode-0.4.2/README.md +247 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/pyproject.toml +4 -1
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/apps.py +4 -2
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/conf.py +4 -1
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/handlers.py +2 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/middleware.py +6 -2
- fetchnode-0.4.2/src/fetchnode/py.typed +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/query.py +1 -1
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/scrub.py +7 -1
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/sender.py +1 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/version.py +1 -1
- fetchnode-0.4.2/src/fetchnode_client/py.typed +0 -0
- fetchnode-0.4.0/PKG-INFO +0 -166
- fetchnode-0.4.0/README.md +0 -132
- {fetchnode-0.4.0 → fetchnode-0.4.2}/.gitignore +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/LICENSE +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/__init__.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/api.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/capture.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/celery.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/demo.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/excepthook.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/scope.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode/utils.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/__init__.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/api.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/apps.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/capture.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/celery.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/conf.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/demo.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/excepthook.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/handlers.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/middleware.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/query.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/scope.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/scrub.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/sender.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/utils.py +0 -0
- {fetchnode-0.4.0 → fetchnode-0.4.2}/src/fetchnode_client/version.py +0 -0
fetchnode-0.4.2/PKG-INFO
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fetchnode
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Official Django and Python client SDK for FetchNode monitoring
|
|
5
|
+
Project-URL: Homepage, https://fetchnode.com
|
|
6
|
+
Project-URL: Documentation, https://fetchnode.com/guide/
|
|
7
|
+
Project-URL: Repository, https://github.com/remcovanwijk040/fetchnode
|
|
8
|
+
Project-URL: Issues, https://github.com/remcovanwijk040/fetchnode/issues
|
|
9
|
+
Project-URL: Changelog, https://fetchnode.com/changelog/
|
|
10
|
+
Author-email: Remco van Wijk <remcovanwijk040@users.noreply.github.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Framework :: Django
|
|
16
|
+
Classifier: Framework :: Django :: 4.2
|
|
17
|
+
Classifier: Framework :: Django :: 5.0
|
|
18
|
+
Classifier: Framework :: Django :: 5.1
|
|
19
|
+
Classifier: Framework :: Django :: 5.2
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
22
|
+
Classifier: Operating System :: OS Independent
|
|
23
|
+
Classifier: Programming Language :: Python :: 3
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
+
Classifier: Topic :: System :: Monitoring
|
|
31
|
+
Classifier: Typing :: Typed
|
|
32
|
+
Requires-Python: >=3.10
|
|
33
|
+
Requires-Dist: django>=4.2
|
|
34
|
+
Provides-Extra: celery
|
|
35
|
+
Requires-Dist: celery>=5.0; extra == 'celery'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# FetchNode Python & Django SDK
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/fetchnode/)
|
|
41
|
+
[](https://pypi.org/project/fetchnode/)
|
|
42
|
+
[](https://www.djangoproject.com/)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
[](https://peps.python.org/pep-0561/)
|
|
45
|
+
|
|
46
|
+
Official Python and Django client SDK for [FetchNode](https://fetchnode.com) — the focused operations workspace for developers and teams managing multiple production Django websites.
|
|
47
|
+
|
|
48
|
+
FetchNode captures unhandled exceptions, error logs, slow database queries, N+1 query patterns, Celery background tasks, and authoritative business conversions with zero bloat and privacy-safe defaults.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Table of Contents
|
|
53
|
+
|
|
54
|
+
- [Installation](#installation)
|
|
55
|
+
- [Quickstart (Django)](#quickstart-django)
|
|
56
|
+
- [Configuration Options](#configuration-options)
|
|
57
|
+
- [Telemetry & Usage](#telemetry--usage)
|
|
58
|
+
- [Conversion & Lead Tracking](#conversion--lead-tracking)
|
|
59
|
+
- [Automatic Error Monitoring](#automatic-error-monitoring)
|
|
60
|
+
- [Manual Exception Capture](#manual-exception-capture)
|
|
61
|
+
- [Celery Job Monitoring](#celery-job-monitoring)
|
|
62
|
+
- [Logging Handler](#logging-handler)
|
|
63
|
+
- [Privacy & Security Defaults](#privacy--security-defaults)
|
|
64
|
+
- [Testing in Development](#testing-in-development)
|
|
65
|
+
- [Compatibility](#compatibility)
|
|
66
|
+
- [Links & Resources](#links--resources)
|
|
67
|
+
- [License](#license)
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
Install via `pip`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install fetchnode
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or using `uv`:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv add fetchnode
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
*(For Celery support, you can optionally install `fetchnode[celery]`)*
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Quickstart (Django)
|
|
90
|
+
|
|
91
|
+
### 1. Add to `INSTALLED_APPS`
|
|
92
|
+
|
|
93
|
+
In your Django `settings.py`:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
INSTALLED_APPS = [
|
|
97
|
+
# ...
|
|
98
|
+
"fetchnode",
|
|
99
|
+
]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 2. Add the Middleware
|
|
103
|
+
|
|
104
|
+
Add `FetchNodeMiddleware` near the top of your `MIDDLEWARE` list for comprehensive request, query, and exception coverage:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
MIDDLEWARE = [
|
|
108
|
+
"fetchnode.middleware.FetchNodeMiddleware",
|
|
109
|
+
"django.middleware.security.SecurityMiddleware",
|
|
110
|
+
# ...
|
|
111
|
+
]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Configure `settings.FETCHNODE`
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
FETCHNODE = {
|
|
118
|
+
"API_KEY": "fn_live_your_project_key_here",
|
|
119
|
+
"ENVIRONMENT": "production", # e.g. "production", "staging"
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
That's it! Django will automatically initialize FetchNode upon startup and capture unhandled exceptions, slow queries, and request telemetry.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Configuration Options
|
|
128
|
+
|
|
129
|
+
Configure via `settings.FETCHNODE` or environment variables:
|
|
130
|
+
|
|
131
|
+
| Setting Key | Environment Variable | Default | Description |
|
|
132
|
+
| :--- | :--- | :--- | :--- |
|
|
133
|
+
| `API_KEY` | `FETCHNODE_API_KEY` | `""` | **Required.** Your FetchNode project key (starts with `fn_live_` or `fn_test_`). |
|
|
134
|
+
| `ENDPOINT` / `API_URL` | `FETCHNODE_ENDPOINT` | `https://monitor.fetchnode.com/api/events/ingest/` | Processing server URL. Point to your self-hosted instance if applicable. |
|
|
135
|
+
| `ENVIRONMENT` | `FETCHNODE_ENVIRONMENT` | `"development"` | Active environment name (`"production"`, `"staging"`). |
|
|
136
|
+
| `RELEASE` | `FETCHNODE_RELEASE` | `""` | Release tag or git commit hash (e.g. `git rev-parse HEAD`). |
|
|
137
|
+
| `ENABLED` | `FETCHNODE_ENABLED` | `True` if API_KEY present | Master toggle for event sending. |
|
|
138
|
+
| `CAPTURE_TRANSACTIONS` | — | `True` | Track request duration and database query counts. |
|
|
139
|
+
| `CAPTURE_BODY` | — | `False` | Privacy safeguard: request bodies are excluded by default. |
|
|
140
|
+
| `CAPTURE_HEADERS` | — | `False` | Privacy safeguard: request headers are excluded by default. |
|
|
141
|
+
| `QUERY_SLOW_THRESHOLD_MS` | — | `100` | Minimum execution time (ms) to flag an ORM query as slow. |
|
|
142
|
+
| `N_PLUS_ONE_THRESHOLD` | — | `5` | Identical SELECT query repetition threshold to flag N+1 issues. |
|
|
143
|
+
| `SENSITIVE_KEYS` | — | *(built-in list)* | Extra field names to scrub from payloads (additive). |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Telemetry & Usage
|
|
148
|
+
|
|
149
|
+
### Conversion & Lead Tracking
|
|
150
|
+
|
|
151
|
+
Track authoritative business events server-side (form submissions, payments, lead captures) with automatic session and visitor cookie attribution:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import fetchnode
|
|
155
|
+
|
|
156
|
+
def contact_view(request):
|
|
157
|
+
form = ContactForm(request.POST)
|
|
158
|
+
if form.is_valid():
|
|
159
|
+
lead = form.save()
|
|
160
|
+
|
|
161
|
+
# Track authoritative conversion
|
|
162
|
+
fetchnode.track(
|
|
163
|
+
"lead_submitted",
|
|
164
|
+
request=request,
|
|
165
|
+
idempotency_key=f"lead:{lead.id}",
|
|
166
|
+
properties={"email": lead.email},
|
|
167
|
+
conversion=True,
|
|
168
|
+
)
|
|
169
|
+
return redirect("thank_you")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Why `idempotency_key` matters:** Providing an `idempotency_key` (such as the database record ID or payment provider ID) ensures that retried requests, webhook replays, or browser reloads do not inflate your conversion metrics. FetchNode securely hashes the key client-side to produce a deterministic event ID.
|
|
173
|
+
|
|
174
|
+
### Automatic Error Monitoring
|
|
175
|
+
|
|
176
|
+
Unhandled exceptions in Django views are caught by `FetchNodeMiddleware`, decorated with:
|
|
177
|
+
* The exact failing line of code and stack trace.
|
|
178
|
+
* In-app vs. framework/library frame classification.
|
|
179
|
+
* Database query count and execution time during the failing request.
|
|
180
|
+
* Correlated request ID and trace ID.
|
|
181
|
+
|
|
182
|
+
### Manual Exception Capture
|
|
183
|
+
|
|
184
|
+
Capture handled exceptions or background exceptions:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
import fetchnode
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
external_service_call()
|
|
191
|
+
except ThirdPartyAPIError as exc:
|
|
192
|
+
fetchnode.capture_exception(exc, tags={"provider": "stripe"})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Celery Job Monitoring
|
|
196
|
+
|
|
197
|
+
FetchNode automatically instruments Celery when installed. You can also explicitly decorate tasks:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from fetchnode import capture_job
|
|
201
|
+
|
|
202
|
+
@capture_job(name="sync_external_inventory")
|
|
203
|
+
def sync_inventory():
|
|
204
|
+
...
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Logging Handler
|
|
208
|
+
|
|
209
|
+
Send standard `logging.ERROR` and `logging.CRITICAL` records to FetchNode:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
LOGGING = {
|
|
213
|
+
"version": 1,
|
|
214
|
+
"disable_existing_loggers": False,
|
|
215
|
+
"handlers": {
|
|
216
|
+
"fetchnode": {
|
|
217
|
+
"level": "ERROR",
|
|
218
|
+
"class": "fetchnode.handlers.FetchNodeHandler",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
"loggers": {
|
|
222
|
+
"django": {
|
|
223
|
+
"handlers": ["fetchnode"],
|
|
224
|
+
"level": "ERROR",
|
|
225
|
+
"propagate": True,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Privacy & Security Defaults
|
|
234
|
+
|
|
235
|
+
FetchNode is engineered with a strict **privacy-first architecture**:
|
|
236
|
+
1. **Client-side Scrubbing:** Sensitive keys (`password`, `token`, `secret`, `api_key`, `authorization`, `cookie`, `csrfmiddlewaretoken`) and any compound keys like `user_password` or `client_secret` are scrubbed before payload transmission.
|
|
237
|
+
2. **Safe SQL Normalization:** Raw SQL text is stripped of literals and parameters (`SELECT * FROM user WHERE id = 123` becomes `SELECT * FROM user WHERE id = ?`).
|
|
238
|
+
3. **No Unintentional PII:** Request bodies, query strings, and raw IP addresses are excluded by default.
|
|
239
|
+
4. **Inspectable & Lightweight:** The entire client is open-source Python with zero compiled binaries and zero unnecessary dependencies.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Testing in Development
|
|
244
|
+
|
|
245
|
+
In your local test suite (e.g. `pytest` or `python manage.py test`), FetchNode can be disabled by omitting the API key or explicitly setting:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
# settings_test.py
|
|
249
|
+
FETCHNODE = {
|
|
250
|
+
"ENABLED": False,
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
To test that event transmission works in your staging environment, run:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
import fetchnode
|
|
258
|
+
fetchnode.capture_message("FetchNode verification ping", level="info")
|
|
259
|
+
fetchnode.flush()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Compatibility
|
|
265
|
+
|
|
266
|
+
- **Python:** 3.10, 3.11, 3.12, 3.13, 3.14
|
|
267
|
+
- **Django:** 4.2 LTS, 5.0, 5.1, 5.2
|
|
268
|
+
- **Operating Systems:** Linux, macOS, Windows
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Links & Resources
|
|
273
|
+
|
|
274
|
+
- **Website & Cloud App:** [fetchnode.com](https://fetchnode.com)
|
|
275
|
+
- **Documentation & Setup Guide:** [fetchnode.com/guide/](https://fetchnode.com/guide/)
|
|
276
|
+
- **Changelog:** [fetchnode.com/changelog/](https://fetchnode.com/changelog/)
|
|
277
|
+
- **GitHub Repository:** [github.com/remcovanwijk040/fetchnode](https://github.com/remcovanwijk040/fetchnode)
|
|
278
|
+
- **Issue Tracker:** [github.com/remcovanwijk040/fetchnode/issues](https://github.com/remcovanwijk040/fetchnode/issues)
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# FetchNode Python & Django SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fetchnode/)
|
|
4
|
+
[](https://pypi.org/project/fetchnode/)
|
|
5
|
+
[](https://www.djangoproject.com/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://peps.python.org/pep-0561/)
|
|
8
|
+
|
|
9
|
+
Official Python and Django client SDK for [FetchNode](https://fetchnode.com) — the focused operations workspace for developers and teams managing multiple production Django websites.
|
|
10
|
+
|
|
11
|
+
FetchNode captures unhandled exceptions, error logs, slow database queries, N+1 query patterns, Celery background tasks, and authoritative business conversions with zero bloat and privacy-safe defaults.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Quickstart (Django)](#quickstart-django)
|
|
19
|
+
- [Configuration Options](#configuration-options)
|
|
20
|
+
- [Telemetry & Usage](#telemetry--usage)
|
|
21
|
+
- [Conversion & Lead Tracking](#conversion--lead-tracking)
|
|
22
|
+
- [Automatic Error Monitoring](#automatic-error-monitoring)
|
|
23
|
+
- [Manual Exception Capture](#manual-exception-capture)
|
|
24
|
+
- [Celery Job Monitoring](#celery-job-monitoring)
|
|
25
|
+
- [Logging Handler](#logging-handler)
|
|
26
|
+
- [Privacy & Security Defaults](#privacy--security-defaults)
|
|
27
|
+
- [Testing in Development](#testing-in-development)
|
|
28
|
+
- [Compatibility](#compatibility)
|
|
29
|
+
- [Links & Resources](#links--resources)
|
|
30
|
+
- [License](#license)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install via `pip`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install fetchnode
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or using `uv`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add fetchnode
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
*(For Celery support, you can optionally install `fetchnode[celery]`)*
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quickstart (Django)
|
|
53
|
+
|
|
54
|
+
### 1. Add to `INSTALLED_APPS`
|
|
55
|
+
|
|
56
|
+
In your Django `settings.py`:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
INSTALLED_APPS = [
|
|
60
|
+
# ...
|
|
61
|
+
"fetchnode",
|
|
62
|
+
]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Add the Middleware
|
|
66
|
+
|
|
67
|
+
Add `FetchNodeMiddleware` near the top of your `MIDDLEWARE` list for comprehensive request, query, and exception coverage:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
MIDDLEWARE = [
|
|
71
|
+
"fetchnode.middleware.FetchNodeMiddleware",
|
|
72
|
+
"django.middleware.security.SecurityMiddleware",
|
|
73
|
+
# ...
|
|
74
|
+
]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. Configure `settings.FETCHNODE`
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
FETCHNODE = {
|
|
81
|
+
"API_KEY": "fn_live_your_project_key_here",
|
|
82
|
+
"ENVIRONMENT": "production", # e.g. "production", "staging"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
That's it! Django will automatically initialize FetchNode upon startup and capture unhandled exceptions, slow queries, and request telemetry.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Configuration Options
|
|
91
|
+
|
|
92
|
+
Configure via `settings.FETCHNODE` or environment variables:
|
|
93
|
+
|
|
94
|
+
| Setting Key | Environment Variable | Default | Description |
|
|
95
|
+
| :--- | :--- | :--- | :--- |
|
|
96
|
+
| `API_KEY` | `FETCHNODE_API_KEY` | `""` | **Required.** Your FetchNode project key (starts with `fn_live_` or `fn_test_`). |
|
|
97
|
+
| `ENDPOINT` / `API_URL` | `FETCHNODE_ENDPOINT` | `https://monitor.fetchnode.com/api/events/ingest/` | Processing server URL. Point to your self-hosted instance if applicable. |
|
|
98
|
+
| `ENVIRONMENT` | `FETCHNODE_ENVIRONMENT` | `"development"` | Active environment name (`"production"`, `"staging"`). |
|
|
99
|
+
| `RELEASE` | `FETCHNODE_RELEASE` | `""` | Release tag or git commit hash (e.g. `git rev-parse HEAD`). |
|
|
100
|
+
| `ENABLED` | `FETCHNODE_ENABLED` | `True` if API_KEY present | Master toggle for event sending. |
|
|
101
|
+
| `CAPTURE_TRANSACTIONS` | — | `True` | Track request duration and database query counts. |
|
|
102
|
+
| `CAPTURE_BODY` | — | `False` | Privacy safeguard: request bodies are excluded by default. |
|
|
103
|
+
| `CAPTURE_HEADERS` | — | `False` | Privacy safeguard: request headers are excluded by default. |
|
|
104
|
+
| `QUERY_SLOW_THRESHOLD_MS` | — | `100` | Minimum execution time (ms) to flag an ORM query as slow. |
|
|
105
|
+
| `N_PLUS_ONE_THRESHOLD` | — | `5` | Identical SELECT query repetition threshold to flag N+1 issues. |
|
|
106
|
+
| `SENSITIVE_KEYS` | — | *(built-in list)* | Extra field names to scrub from payloads (additive). |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Telemetry & Usage
|
|
111
|
+
|
|
112
|
+
### Conversion & Lead Tracking
|
|
113
|
+
|
|
114
|
+
Track authoritative business events server-side (form submissions, payments, lead captures) with automatic session and visitor cookie attribution:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import fetchnode
|
|
118
|
+
|
|
119
|
+
def contact_view(request):
|
|
120
|
+
form = ContactForm(request.POST)
|
|
121
|
+
if form.is_valid():
|
|
122
|
+
lead = form.save()
|
|
123
|
+
|
|
124
|
+
# Track authoritative conversion
|
|
125
|
+
fetchnode.track(
|
|
126
|
+
"lead_submitted",
|
|
127
|
+
request=request,
|
|
128
|
+
idempotency_key=f"lead:{lead.id}",
|
|
129
|
+
properties={"email": lead.email},
|
|
130
|
+
conversion=True,
|
|
131
|
+
)
|
|
132
|
+
return redirect("thank_you")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Why `idempotency_key` matters:** Providing an `idempotency_key` (such as the database record ID or payment provider ID) ensures that retried requests, webhook replays, or browser reloads do not inflate your conversion metrics. FetchNode securely hashes the key client-side to produce a deterministic event ID.
|
|
136
|
+
|
|
137
|
+
### Automatic Error Monitoring
|
|
138
|
+
|
|
139
|
+
Unhandled exceptions in Django views are caught by `FetchNodeMiddleware`, decorated with:
|
|
140
|
+
* The exact failing line of code and stack trace.
|
|
141
|
+
* In-app vs. framework/library frame classification.
|
|
142
|
+
* Database query count and execution time during the failing request.
|
|
143
|
+
* Correlated request ID and trace ID.
|
|
144
|
+
|
|
145
|
+
### Manual Exception Capture
|
|
146
|
+
|
|
147
|
+
Capture handled exceptions or background exceptions:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
import fetchnode
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
external_service_call()
|
|
154
|
+
except ThirdPartyAPIError as exc:
|
|
155
|
+
fetchnode.capture_exception(exc, tags={"provider": "stripe"})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Celery Job Monitoring
|
|
159
|
+
|
|
160
|
+
FetchNode automatically instruments Celery when installed. You can also explicitly decorate tasks:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from fetchnode import capture_job
|
|
164
|
+
|
|
165
|
+
@capture_job(name="sync_external_inventory")
|
|
166
|
+
def sync_inventory():
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Logging Handler
|
|
171
|
+
|
|
172
|
+
Send standard `logging.ERROR` and `logging.CRITICAL` records to FetchNode:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
LOGGING = {
|
|
176
|
+
"version": 1,
|
|
177
|
+
"disable_existing_loggers": False,
|
|
178
|
+
"handlers": {
|
|
179
|
+
"fetchnode": {
|
|
180
|
+
"level": "ERROR",
|
|
181
|
+
"class": "fetchnode.handlers.FetchNodeHandler",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
"loggers": {
|
|
185
|
+
"django": {
|
|
186
|
+
"handlers": ["fetchnode"],
|
|
187
|
+
"level": "ERROR",
|
|
188
|
+
"propagate": True,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Privacy & Security Defaults
|
|
197
|
+
|
|
198
|
+
FetchNode is engineered with a strict **privacy-first architecture**:
|
|
199
|
+
1. **Client-side Scrubbing:** Sensitive keys (`password`, `token`, `secret`, `api_key`, `authorization`, `cookie`, `csrfmiddlewaretoken`) and any compound keys like `user_password` or `client_secret` are scrubbed before payload transmission.
|
|
200
|
+
2. **Safe SQL Normalization:** Raw SQL text is stripped of literals and parameters (`SELECT * FROM user WHERE id = 123` becomes `SELECT * FROM user WHERE id = ?`).
|
|
201
|
+
3. **No Unintentional PII:** Request bodies, query strings, and raw IP addresses are excluded by default.
|
|
202
|
+
4. **Inspectable & Lightweight:** The entire client is open-source Python with zero compiled binaries and zero unnecessary dependencies.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Testing in Development
|
|
207
|
+
|
|
208
|
+
In your local test suite (e.g. `pytest` or `python manage.py test`), FetchNode can be disabled by omitting the API key or explicitly setting:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
# settings_test.py
|
|
212
|
+
FETCHNODE = {
|
|
213
|
+
"ENABLED": False,
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
To test that event transmission works in your staging environment, run:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import fetchnode
|
|
221
|
+
fetchnode.capture_message("FetchNode verification ping", level="info")
|
|
222
|
+
fetchnode.flush()
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Compatibility
|
|
228
|
+
|
|
229
|
+
- **Python:** 3.10, 3.11, 3.12, 3.13, 3.14
|
|
230
|
+
- **Django:** 4.2 LTS, 5.0, 5.1, 5.2
|
|
231
|
+
- **Operating Systems:** Linux, macOS, Windows
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Links & Resources
|
|
236
|
+
|
|
237
|
+
- **Website & Cloud App:** [fetchnode.com](https://fetchnode.com)
|
|
238
|
+
- **Documentation & Setup Guide:** [fetchnode.com/guide/](https://fetchnode.com/guide/)
|
|
239
|
+
- **Changelog:** [fetchnode.com/changelog/](https://fetchnode.com/changelog/)
|
|
240
|
+
- **GitHub Repository:** [github.com/remcovanwijk040/fetchnode](https://github.com/remcovanwijk040/fetchnode)
|
|
241
|
+
- **Issue Tracker:** [github.com/remcovanwijk040/fetchnode/issues](https://github.com/remcovanwijk040/fetchnode/issues)
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fetchnode"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "Official Django and Python client SDK for FetchNode monitoring"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -31,6 +31,7 @@ classifiers = [
|
|
|
31
31
|
"Programming Language :: Python :: 3.14",
|
|
32
32
|
"Topic :: System :: Monitoring",
|
|
33
33
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
"Typing :: Typed",
|
|
34
35
|
]
|
|
35
36
|
dependencies = [
|
|
36
37
|
"django>=4.2",
|
|
@@ -43,8 +44,10 @@ celery = [
|
|
|
43
44
|
|
|
44
45
|
[project.urls]
|
|
45
46
|
Homepage = "https://fetchnode.com"
|
|
47
|
+
Documentation = "https://fetchnode.com/guide/"
|
|
46
48
|
Repository = "https://github.com/remcovanwijk040/fetchnode"
|
|
47
49
|
Issues = "https://github.com/remcovanwijk040/fetchnode/issues"
|
|
50
|
+
Changelog = "https://fetchnode.com/changelog/"
|
|
48
51
|
|
|
49
52
|
[tool.hatch.build.targets.wheel]
|
|
50
53
|
packages = ["src/fetchnode", "src/fetchnode_client"]
|
|
@@ -13,8 +13,10 @@ class FetchNodeConfig(AppConfig):
|
|
|
13
13
|
# Ensure settings.fetchnode_track is available if needed by legacy code
|
|
14
14
|
try:
|
|
15
15
|
from django.conf import settings
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if not hasattr(settings, "fetchnode_track"):
|
|
17
|
+
setattr(settings, "fetchnode_track", track_event)
|
|
18
|
+
if not hasattr(settings, "FETCHNODE_TRACK"):
|
|
19
|
+
setattr(settings, "FETCHNODE_TRACK", track_event)
|
|
18
20
|
except Exception:
|
|
19
21
|
pass
|
|
20
22
|
|
|
@@ -22,6 +22,7 @@ DEFAULTS = {
|
|
|
22
22
|
"KEEP_TRANSACTION_OUTLIER_MS": 1000,
|
|
23
23
|
"CAPTURE_TRANSACTIONS": True,
|
|
24
24
|
"CAPTURE_JOBS": True,
|
|
25
|
+
"CAPTURE_404": True,
|
|
25
26
|
"INSTRUMENT_CELERY": True,
|
|
26
27
|
"INSTRUMENT_LOGGING": True,
|
|
27
28
|
"INSTRUMENT_EXCEPTHOOK": True,
|
|
@@ -118,7 +119,9 @@ def get_client_settings() -> dict:
|
|
|
118
119
|
elif "FETCHNODE_ENABLED" in os.environ:
|
|
119
120
|
merged["ENABLED"] = os.environ["FETCHNODE_ENABLED"].lower() in {"1", "true", "yes"}
|
|
120
121
|
else:
|
|
121
|
-
|
|
122
|
+
import sys
|
|
123
|
+
is_testing = any(arg == "test" or "pytest" in arg for arg in sys.argv)
|
|
124
|
+
merged["ENABLED"] = bool(merged["API_KEY"]) and not is_testing
|
|
122
125
|
|
|
123
126
|
# Security filters are additive: a project can protect extra business
|
|
124
127
|
# fields without accidentally unprotecting cookies or authorization data.
|
|
@@ -26,6 +26,8 @@ class FetchNodeHandler(logging.Handler):
|
|
|
26
26
|
record.levelno < logging.ERROR
|
|
27
27
|
or record.name == "fetchnode"
|
|
28
28
|
or record.name.startswith("fetchnode.")
|
|
29
|
+
or record.name == "fetchnode_client"
|
|
30
|
+
or record.name.startswith("fetchnode_client.")
|
|
29
31
|
or getattr(record, _CAPTURED_RECORD_ATTRIBUTE, False)
|
|
30
32
|
):
|
|
31
33
|
return
|
|
@@ -41,8 +41,10 @@ class ErrorCaptureMiddleware:
|
|
|
41
41
|
try:
|
|
42
42
|
from django.conf import settings
|
|
43
43
|
from fetchnode.api import track_event
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if not hasattr(settings, "fetchnode_track"):
|
|
45
|
+
setattr(settings, "fetchnode_track", track_event)
|
|
46
|
+
if not hasattr(settings, "FETCHNODE_TRACK"):
|
|
47
|
+
setattr(settings, "FETCHNODE_TRACK", track_event)
|
|
46
48
|
except Exception:
|
|
47
49
|
pass
|
|
48
50
|
|
|
@@ -120,6 +122,8 @@ class ErrorCaptureMiddleware:
|
|
|
120
122
|
request.path.startswith(prefix) for prefix in config["IGNORE_PATH_PREFIXES"]
|
|
121
123
|
):
|
|
122
124
|
return
|
|
125
|
+
if not config.get("CAPTURE_404", True) and status_code == 404:
|
|
126
|
+
return
|
|
123
127
|
resolver_match = getattr(request, "resolver_match", None)
|
|
124
128
|
auth_user = getattr(request, "user", None)
|
|
125
129
|
user_identifier = ""
|
|
File without changes
|
|
@@ -39,7 +39,7 @@ def application_stack_location() -> str:
|
|
|
39
39
|
"""Return the nearest application callsite without local variables or values."""
|
|
40
40
|
for frame in reversed(traceback.extract_stack(limit=30)[:-1]):
|
|
41
41
|
filename = frame.filename.replace("\\", "/")
|
|
42
|
-
if any(marker in filename for marker in ("/django/", "/site-packages/", "/fetchnode/")):
|
|
42
|
+
if any(marker in filename for marker in ("/django/", "/site-packages/", "/fetchnode/", "/fetchnode_client/")):
|
|
43
43
|
continue
|
|
44
44
|
return f"{frame.filename}:{frame.lineno} in {frame.name}"[:1024]
|
|
45
45
|
return ""
|
|
@@ -8,7 +8,13 @@ MAX_VALUE_LENGTH = 4000
|
|
|
8
8
|
|
|
9
9
|
def is_sensitive_key(key: str, sensitive_keys: list[str]) -> bool:
|
|
10
10
|
normalized = (key or "").replace("-", "_").lower()
|
|
11
|
-
|
|
11
|
+
norm_set = {(item or "").replace("-", "_").lower() for item in (sensitive_keys or []) if item}
|
|
12
|
+
if normalized in norm_set:
|
|
13
|
+
return True
|
|
14
|
+
for item in norm_set:
|
|
15
|
+
if normalized.startswith(f"{item}_") or normalized.endswith(f"_{item}") or f"_{item}_" in normalized:
|
|
16
|
+
return True
|
|
17
|
+
return False
|
|
12
18
|
|
|
13
19
|
|
|
14
20
|
def scrub_value(value, *, key: str = "", sensitive_keys: list[str] | None = None, depth: int = 0):
|
|
@@ -63,6 +63,7 @@ def _deliver(payload: dict, config: dict) -> bool:
|
|
|
63
63
|
headers = {
|
|
64
64
|
"Content-Type": "application/json",
|
|
65
65
|
"Authorization": f"Bearer {config['API_KEY']}",
|
|
66
|
+
"User-Agent": f"fetchnode-python/{SDK_VERSION}",
|
|
66
67
|
}
|
|
67
68
|
if config.get("COMPRESS_PAYLOADS", True) and len(data) >= int(config.get("COMPRESSION_THRESHOLD_BYTES", 1024)):
|
|
68
69
|
data = gzip.compress(data, compresslevel=5)
|
|
File without changes
|
fetchnode-0.4.0/PKG-INFO
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: fetchnode
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: Official Django and Python client SDK for FetchNode monitoring
|
|
5
|
-
Project-URL: Homepage, https://fetchnode.com
|
|
6
|
-
Project-URL: Repository, https://github.com/remcovanwijk040/fetchnode
|
|
7
|
-
Project-URL: Issues, https://github.com/remcovanwijk040/fetchnode/issues
|
|
8
|
-
Author-email: Remco van Wijk <remcovanwijk040@users.noreply.github.com>
|
|
9
|
-
License-Expression: MIT
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Web Environment
|
|
13
|
-
Classifier: Framework :: Django
|
|
14
|
-
Classifier: Framework :: Django :: 4.2
|
|
15
|
-
Classifier: Framework :: Django :: 5.0
|
|
16
|
-
Classifier: Framework :: Django :: 5.1
|
|
17
|
-
Classifier: Framework :: Django :: 5.2
|
|
18
|
-
Classifier: Intended Audience :: Developers
|
|
19
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
-
Classifier: Operating System :: OS Independent
|
|
21
|
-
Classifier: Programming Language :: Python :: 3
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
27
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
-
Classifier: Topic :: System :: Monitoring
|
|
29
|
-
Requires-Python: >=3.10
|
|
30
|
-
Requires-Dist: django>=4.2
|
|
31
|
-
Provides-Extra: celery
|
|
32
|
-
Requires-Dist: celery>=5.0; extra == 'celery'
|
|
33
|
-
Description-Content-Type: text/markdown
|
|
34
|
-
|
|
35
|
-
# FetchNode Python & Django SDK
|
|
36
|
-
|
|
37
|
-
[](https://pypi.org/project/fetchnode/)
|
|
38
|
-
[](https://pypi.org/project/fetchnode/)
|
|
39
|
-
[](https://opensource.org/licenses/MIT)
|
|
40
|
-
|
|
41
|
-
Official Python and Django client SDK for [FetchNode](https://fetchnode.com), the focused operations and monitoring workspace for Django teams and portfolio operators.
|
|
42
|
-
|
|
43
|
-
FetchNode captures unhandled exceptions, error logs, slow database queries, Celery background tasks, and business conversions with zero configuration bloat and privacy-safe defaults.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Installation
|
|
48
|
-
|
|
49
|
-
Install the package using `pip` or `uv`:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pip install fetchnode
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
*(Or `uv add fetchnode`)*
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Quickstart (Django)
|
|
60
|
-
|
|
61
|
-
### 1. Add to `INSTALLED_APPS`
|
|
62
|
-
|
|
63
|
-
In your Django `settings.py`:
|
|
64
|
-
|
|
65
|
-
```python
|
|
66
|
-
INSTALLED_APPS = [
|
|
67
|
-
# ...
|
|
68
|
-
"fetchnode",
|
|
69
|
-
]
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 2. Add the Middleware
|
|
73
|
-
|
|
74
|
-
Add `FetchNodeMiddleware` near the top of your `MIDDLEWARE` list:
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
MIDDLEWARE = [
|
|
78
|
-
"fetchnode.middleware.FetchNodeMiddleware",
|
|
79
|
-
"django.middleware.security.SecurityMiddleware",
|
|
80
|
-
# ...
|
|
81
|
-
]
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 3. Configure `settings.FETCHNODE`
|
|
85
|
-
|
|
86
|
-
```python
|
|
87
|
-
FETCHNODE = {
|
|
88
|
-
"API_KEY": "fn_live_your_project_key_here",
|
|
89
|
-
"ENVIRONMENT": "production", # e.g. "production", "staging"
|
|
90
|
-
|
|
91
|
-
# Optional: override endpoint for self-hosted FetchNode instances
|
|
92
|
-
# Defaults to FetchNode Cloud: "https://monitor.fetchnode.com"
|
|
93
|
-
# "ENDPOINT": "https://monitor.fetchnode.com",
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
You can also configure via environment variables:
|
|
98
|
-
* `FETCHNODE_API_KEY`
|
|
99
|
-
* `FETCHNODE_ENVIRONMENT`
|
|
100
|
-
* `FETCHNODE_RELEASE`
|
|
101
|
-
* `FETCHNODE_ENDPOINT`
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Usage
|
|
106
|
-
|
|
107
|
-
### Milestone & Conversion Tracking
|
|
108
|
-
|
|
109
|
-
Track business events (form submits, leads, signups, payments) server-side with automatic visitor attribution:
|
|
110
|
-
|
|
111
|
-
```python
|
|
112
|
-
import fetchnode
|
|
113
|
-
|
|
114
|
-
def contact_view(request):
|
|
115
|
-
form = ContactForm(request.POST)
|
|
116
|
-
if form.is_valid():
|
|
117
|
-
lead = form.save()
|
|
118
|
-
|
|
119
|
-
# Track authoritative conversion
|
|
120
|
-
fetchnode.track(
|
|
121
|
-
"lead_submitted",
|
|
122
|
-
request=request,
|
|
123
|
-
idempotency_key=str(lead.id),
|
|
124
|
-
properties={"email": lead.email},
|
|
125
|
-
conversion=True,
|
|
126
|
-
)
|
|
127
|
-
return redirect("thank_you")
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Manual Exception Capture
|
|
131
|
-
|
|
132
|
-
```python
|
|
133
|
-
import fetchnode
|
|
134
|
-
|
|
135
|
-
try:
|
|
136
|
-
risky_operation()
|
|
137
|
-
except Exception as exc:
|
|
138
|
-
fetchnode.capture_exception(exc)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Celery Task Monitoring
|
|
142
|
-
|
|
143
|
-
FetchNode automatically instruments Celery when installed. To manually monitor tasks or commands:
|
|
144
|
-
|
|
145
|
-
```python
|
|
146
|
-
from fetchnode import capture_job
|
|
147
|
-
|
|
148
|
-
@capture_job(name="sync_external_inventory")
|
|
149
|
-
def sync_inventory():
|
|
150
|
-
...
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
## Privacy by Default
|
|
156
|
-
|
|
157
|
-
FetchNode is engineered with privacy-safe defaults:
|
|
158
|
-
* Sensitive keys (`password`, `token`, `secret`, `api_key`, `authorization`, `cookie`, `csrfmiddlewaretoken`) are scrubbed before payload transmission.
|
|
159
|
-
* Request bodies and raw IP addresses are excluded by default.
|
|
160
|
-
* Query string parameters and raw SQL values are normalized.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## License
|
|
165
|
-
|
|
166
|
-
MIT License. See [LICENSE](LICENSE) for details.
|
fetchnode-0.4.0/README.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# FetchNode Python & Django SDK
|
|
2
|
-
|
|
3
|
-
[](https://pypi.org/project/fetchnode/)
|
|
4
|
-
[](https://pypi.org/project/fetchnode/)
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
|
|
7
|
-
Official Python and Django client SDK for [FetchNode](https://fetchnode.com), the focused operations and monitoring workspace for Django teams and portfolio operators.
|
|
8
|
-
|
|
9
|
-
FetchNode captures unhandled exceptions, error logs, slow database queries, Celery background tasks, and business conversions with zero configuration bloat and privacy-safe defaults.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
Install the package using `pip` or `uv`:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pip install fetchnode
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
*(Or `uv add fetchnode`)*
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Quickstart (Django)
|
|
26
|
-
|
|
27
|
-
### 1. Add to `INSTALLED_APPS`
|
|
28
|
-
|
|
29
|
-
In your Django `settings.py`:
|
|
30
|
-
|
|
31
|
-
```python
|
|
32
|
-
INSTALLED_APPS = [
|
|
33
|
-
# ...
|
|
34
|
-
"fetchnode",
|
|
35
|
-
]
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Add the Middleware
|
|
39
|
-
|
|
40
|
-
Add `FetchNodeMiddleware` near the top of your `MIDDLEWARE` list:
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
MIDDLEWARE = [
|
|
44
|
-
"fetchnode.middleware.FetchNodeMiddleware",
|
|
45
|
-
"django.middleware.security.SecurityMiddleware",
|
|
46
|
-
# ...
|
|
47
|
-
]
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 3. Configure `settings.FETCHNODE`
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
FETCHNODE = {
|
|
54
|
-
"API_KEY": "fn_live_your_project_key_here",
|
|
55
|
-
"ENVIRONMENT": "production", # e.g. "production", "staging"
|
|
56
|
-
|
|
57
|
-
# Optional: override endpoint for self-hosted FetchNode instances
|
|
58
|
-
# Defaults to FetchNode Cloud: "https://monitor.fetchnode.com"
|
|
59
|
-
# "ENDPOINT": "https://monitor.fetchnode.com",
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
You can also configure via environment variables:
|
|
64
|
-
* `FETCHNODE_API_KEY`
|
|
65
|
-
* `FETCHNODE_ENVIRONMENT`
|
|
66
|
-
* `FETCHNODE_RELEASE`
|
|
67
|
-
* `FETCHNODE_ENDPOINT`
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## Usage
|
|
72
|
-
|
|
73
|
-
### Milestone & Conversion Tracking
|
|
74
|
-
|
|
75
|
-
Track business events (form submits, leads, signups, payments) server-side with automatic visitor attribution:
|
|
76
|
-
|
|
77
|
-
```python
|
|
78
|
-
import fetchnode
|
|
79
|
-
|
|
80
|
-
def contact_view(request):
|
|
81
|
-
form = ContactForm(request.POST)
|
|
82
|
-
if form.is_valid():
|
|
83
|
-
lead = form.save()
|
|
84
|
-
|
|
85
|
-
# Track authoritative conversion
|
|
86
|
-
fetchnode.track(
|
|
87
|
-
"lead_submitted",
|
|
88
|
-
request=request,
|
|
89
|
-
idempotency_key=str(lead.id),
|
|
90
|
-
properties={"email": lead.email},
|
|
91
|
-
conversion=True,
|
|
92
|
-
)
|
|
93
|
-
return redirect("thank_you")
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Manual Exception Capture
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
import fetchnode
|
|
100
|
-
|
|
101
|
-
try:
|
|
102
|
-
risky_operation()
|
|
103
|
-
except Exception as exc:
|
|
104
|
-
fetchnode.capture_exception(exc)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Celery Task Monitoring
|
|
108
|
-
|
|
109
|
-
FetchNode automatically instruments Celery when installed. To manually monitor tasks or commands:
|
|
110
|
-
|
|
111
|
-
```python
|
|
112
|
-
from fetchnode import capture_job
|
|
113
|
-
|
|
114
|
-
@capture_job(name="sync_external_inventory")
|
|
115
|
-
def sync_inventory():
|
|
116
|
-
...
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## Privacy by Default
|
|
122
|
-
|
|
123
|
-
FetchNode is engineered with privacy-safe defaults:
|
|
124
|
-
* Sensitive keys (`password`, `token`, `secret`, `api_key`, `authorization`, `cookie`, `csrfmiddlewaretoken`) are scrubbed before payload transmission.
|
|
125
|
-
* Request bodies and raw IP addresses are excluded by default.
|
|
126
|
-
* Query string parameters and raw SQL values are normalized.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## License
|
|
131
|
-
|
|
132
|
-
MIT License. See [LICENSE](LICENSE) for details.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|