logsetu 0.3.0__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.
@@ -0,0 +1,49 @@
1
+ # deps
2
+ node_modules/
3
+ .pnpm-store/
4
+
5
+ # builds
6
+ dist/
7
+ .next/
8
+ out/
9
+ .turbo/
10
+ *.tsbuildinfo
11
+ apps/server/generated/
12
+
13
+ # python
14
+ __pycache__/
15
+ *.py[cod]
16
+ *.egg-info/
17
+ build/
18
+ .venv/
19
+ venv/
20
+ .pytest_cache/
21
+ .mypy_cache/
22
+ .ruff_cache/
23
+
24
+ # env / secrets
25
+ .env
26
+ .env.*
27
+ !.env.example
28
+
29
+ # data
30
+ data/
31
+ *.db
32
+ *.db-journal
33
+ *.sqlite
34
+ *.sqlite3
35
+ *.sqlite-*
36
+
37
+ # editors / os
38
+ .idea/
39
+ .vscode/
40
+ .DS_Store
41
+ Thumbs.db
42
+
43
+ # logs
44
+ *.log
45
+ npm-debug.log*
46
+ pnpm-debug.log*
47
+
48
+ # generated by next
49
+ next-env.d.ts
logsetu-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raj Verma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
logsetu-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.5
2
+ Name: logsetu
3
+ Version: 0.3.0
4
+ Summary: LogSetu SDK for Python — Django, DRF, Flask and FastAPI: ship logging records and request errors to your self-hosted LogSetu server
5
+ Project-URL: Homepage, https://github.com/itsrajverma/logsetu
6
+ Project-URL: Documentation, https://github.com/itsrajverma/logsetu/tree/main/docs
7
+ Project-URL: Repository, https://github.com/itsrajverma/logsetu
8
+ Project-URL: Issues, https://github.com/itsrajverma/logsetu/issues
9
+ Author: Raj Verma
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: asgi,django,drf,error-tracking,fastapi,flask,logging,logsetu,python,self-hosted,sentry-alternative
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: Django
15
+ Classifier: Framework :: FastAPI
16
+ Classifier: Framework :: Flask
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: System :: Logging
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.9
23
+ Provides-Extra: dev
24
+ Requires-Dist: build; extra == 'dev'
25
+ Requires-Dist: django>=3.2; extra == 'dev'
26
+ Requires-Dist: fastapi>=0.95; extra == 'dev'
27
+ Requires-Dist: flask>=2.2; extra == 'dev'
28
+ Requires-Dist: httpx; extra == 'dev'
29
+ Requires-Dist: pytest-django>=4.8; extra == 'dev'
30
+ Requires-Dist: pytest>=8; extra == 'dev'
31
+ Requires-Dist: twine; extra == 'dev'
32
+ Provides-Extra: django
33
+ Requires-Dist: django>=3.2; extra == 'django'
34
+ Provides-Extra: fastapi
35
+ Requires-Dist: fastapi>=0.95; extra == 'fastapi'
36
+ Provides-Extra: flask
37
+ Requires-Dist: flask>=2.2; extra == 'flask'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # logsetu (Python)
41
+
42
+ Ship Django / DRF, **Flask** and **FastAPI** logs and unhandled errors to your self-hosted [LogSetu](https://github.com/itsrajverma/logsetu) server.
43
+ Zero runtime dependencies, non-blocking (background thread + batching), and it never crashes your app if the log server is down.
44
+
45
+ ```bash
46
+ pip install "logsetu[django]" # Django / DRF
47
+ pip install "logsetu[flask]" # Flask
48
+ pip install "logsetu[fastapi]" # FastAPI / Starlette / any ASGI app
49
+ pip install logsetu # just the client + logging handler (zero dependencies)
50
+ ```
51
+
52
+ The extras only pull in the framework itself; the SDK has no runtime dependencies. Integrations:
53
+ [Django](#setup-2-minutes), [Flask](#flask) and [FastAPI / any ASGI app](#fastapi--starlette--asgi).
54
+
55
+ > **Coming from `logsetu-django`?** The package was renamed to `logsetu` in 0.3.0. The import name (`import logsetu`)
56
+ > and all settings are unchanged. Switch with `pip uninstall -y logsetu-django && pip install "logsetu[django]"` and
57
+ > update your requirements file. Don't keep both installed: they contain the same `logsetu` module. `logsetu-django`
58
+ > is still published with identical code for existing installs.
59
+
60
+ ## Setup (2 minutes)
61
+
62
+ ```python
63
+ # settings.py
64
+ INSTALLED_APPS = [..., "logsetu"] # optional, but nice for discoverability
65
+
66
+ LOGGING = {
67
+ "version": 1,
68
+ "disable_existing_loggers": False,
69
+ "handlers": {
70
+ "logsetu": {
71
+ "class": "logsetu.handler.LogSetuHandler",
72
+ "api_key": env("LOGSETU_API_KEY"), # from the LogSetu dashboard → Projects
73
+ "endpoint": env("LOGSETU_ENDPOINT"), # e.g. https://logs.example.com
74
+ "environment": env("ENVIRONMENT", default="production"),
75
+ "source": "django-backend",
76
+ "level": "WARNING",
77
+ },
78
+ },
79
+ "root": {"handlers": ["logsetu"], "level": "INFO"},
80
+ }
81
+
82
+ MIDDLEWARE = [
83
+ ...,
84
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
85
+ "logsetu.middleware.LogSetuMiddleware", # after auth middleware so user ids are captured
86
+ ]
87
+ ```
88
+
89
+ That's it. Every `logging` call at or above the handler's level is sent to LogSetu, and the middleware
90
+ reports unhandled exceptions (and 5xx responses) with the request path, method, view, user, redacted headers and full traceback.
91
+
92
+ ```python
93
+ import logging
94
+ log = logging.getLogger(__name__)
95
+
96
+ log.warning("Checkout is slow", extra={"duration_ms": 1830, "cart_items": 12})
97
+ try:
98
+ charge(card)
99
+ except PaymentError:
100
+ log.exception("Payment failed") # traceback is attached automatically
101
+ ```
102
+
103
+ ## Multi-tenant apps
104
+
105
+ Tag every log with the current tenant by pointing the SDK at a callable `(request) -> str | None`:
106
+
107
+ ```python
108
+ # settings.py
109
+ LOGGING["handlers"]["logsetu"]["get_tenant_id"] = "myapp.tenants.get_tenant_id"
110
+
111
+ # myapp/tenants.py
112
+ def get_tenant_id(request):
113
+ return getattr(request, "tenant", None) and request.tenant.slug
114
+ ```
115
+
116
+ Or, from anywhere inside a request (e.g. after you resolve the tenant in your own middleware):
117
+
118
+ ```python
119
+ from logsetu import update_request_context
120
+ update_request_context(tenant_id=tenant.slug, plan=tenant.plan)
121
+ ```
122
+
123
+ ## Handler options
124
+
125
+ | Option | Default | Description |
126
+ |---|---|---|
127
+ | `api_key` / `endpoint` | — | Required (or set `LOGSETU = {"API_KEY": ..., "ENDPOINT": ...}` in settings) |
128
+ | `environment` | `"production"` | Tag on every log |
129
+ | `source` | `"django"` | Tag on every log; override per record with `extra={"source": "celery"}` |
130
+ | `get_tenant_id` | `None` | Dotted path or callable `(request) -> str` |
131
+ | `include_extra` | `True` | Forward `extra={...}` fields as metadata |
132
+ | `include_request_context` | `True` | Attach request id / path / method / tenant set by the middleware |
133
+ | `flush_interval` | `2.0` | Seconds between background flushes |
134
+ | `batch_size` | `10` | Flush as soon as this many logs are queued |
135
+ | `max_queue_size` | `1000` | Drop oldest logs beyond this |
136
+ | `max_retries` | `3` | Retries with exponential backoff on 5xx / network errors |
137
+ | `timeout` | `5.0` | HTTP timeout in seconds |
138
+ | `debug` | `False` | Print every transport failure to stderr (otherwise only the first) |
139
+
140
+ ## Middleware options (`settings.LOGSETU`)
141
+
142
+ The middleware reuses the client and options of a configured `LogSetuHandler`. Without a handler it can run standalone:
143
+
144
+ ```python
145
+ LOGSETU = {
146
+ "API_KEY": env("LOGSETU_API_KEY"),
147
+ "ENDPOINT": env("LOGSETU_ENDPOINT"),
148
+ "ENVIRONMENT": "production",
149
+ "SOURCE": "django-backend",
150
+ "GET_TENANT_ID": "myapp.tenants.get_tenant_id",
151
+ "REDACT_HEADERS": ["authorization", "cookie", "x-api-key"], # defaults already cover these
152
+ "CAPTURE_5XX": True, # also report 5xx responses that didn't raise
153
+ }
154
+ ```
155
+
156
+ Every request gets a `request_id` (taken from `X-Request-ID` if present, otherwise generated) which is
157
+ attached to all logs emitted during that request and exposed as `request.logsetu_request_id`.
158
+
159
+ ## Guarantees
160
+
161
+ - **Never blocks** the request cycle: `emit()` only appends to an in-memory queue.
162
+ - **Never raises** into your app: transport errors are printed once to stderr, then suppressed.
163
+ - **Never leaks secrets**: `Authorization`, `Cookie`, `X-Api-Key`, `X-CSRFToken` and `Proxy-Authorization` are redacted.
164
+ - **Never loses the last logs** on shutdown: the queue is flushed via `atexit`.
165
+
166
+ ## Celery / management commands
167
+
168
+ Nothing special — the handler works anywhere Python's `logging` does. Set `extra={"source": "celery"}` to distinguish
169
+ workers, or configure a second handler with a different `source`.
170
+
171
+ ## Flask
172
+
173
+ ```python
174
+ import os
175
+ from flask import Flask
176
+ from logsetu.flask import LogSetu
177
+
178
+ app = Flask(__name__)
179
+ LogSetu(app, api_key=os.environ["LOGSETU_API_KEY"], endpoint=os.environ["LOGSETU_ENDPOINT"], source="flask-api")
180
+ ```
181
+
182
+ App factory? Create `logsetu = LogSetu()` at module level and call `logsetu.init_app(app)`; options you don't pass are
183
+ read from `app.config` (`LOGSETU_API_KEY`, `LOGSETU_ENDPOINT`, `LOGSETU_ENVIRONMENT`, `LOGSETU_SOURCE`).
184
+
185
+ The extension reports unhandled exceptions and 5xx responses (route, endpoint, redacted headers, traceback), and attaches
186
+ a `LogSetuHandler` to the root logger so `logging.warning(...)` and above is shipped with the request id, method and
187
+ path of the current request. Flask's own `"Exception on /path"` log line is de-duplicated.
188
+
189
+ | Option | Default | Description |
190
+ |---|---|---|
191
+ | `api_key` / `endpoint` | `app.config` | Project API key and server URL |
192
+ | `environment` / `source` | `"production"` / `"flask"` | Tags on every log |
193
+ | `get_tenant_id` | — | `callable(request) -> str \| None` or dotted path |
194
+ | `capture_logging` | `True` | Attach a handler to the root logger |
195
+ | `log_level` | `logging.WARNING` | Minimum level forwarded from `logging` |
196
+ | `capture_5xx` | `True` | Report 5xx responses that didn't raise |
197
+ | `log_requests` | `False` | One info/warn log per request |
198
+ | `redact_headers` | auth / cookie / API-key headers | Header names replaced by `[REDACTED]` |
199
+ | `client` | — | Use an existing `LogSetuClient` |
200
+
201
+ ## FastAPI / Starlette / ASGI
202
+
203
+ ```python
204
+ import os
205
+ from fastapi import FastAPI
206
+ from logsetu.fastapi import LogSetuMiddleware # alias of logsetu.asgi — works with any ASGI app
207
+
208
+ app = FastAPI()
209
+ app.add_middleware(
210
+ LogSetuMiddleware,
211
+ api_key=os.environ["LOGSETU_API_KEY"],
212
+ endpoint=os.environ["LOGSETU_ENDPOINT"],
213
+ source="fastapi",
214
+ ignore_paths=["/healthz"],
215
+ )
216
+ ```
217
+
218
+ A pure ASGI middleware (streaming responses aren't buffered). Same options as Flask plus `ignore_paths`;
219
+ `get_tenant_id` receives the ASGI `scope`, and `api_key` / `endpoint` / `environment` / `source` fall back to the
220
+ `LOGSETU_*` environment variables. Request context reaches logs from both `async def` endpoints and sync endpoints
221
+ running in the threadpool. `HTTPException`s with 4xx status codes aren't reported as errors. The request id is
222
+ available as `request.state.logsetu_request_id`.
223
+
224
+ ## License
225
+
226
+ MIT
@@ -0,0 +1,187 @@
1
+ # logsetu (Python)
2
+
3
+ Ship Django / DRF, **Flask** and **FastAPI** logs and unhandled errors to your self-hosted [LogSetu](https://github.com/itsrajverma/logsetu) server.
4
+ Zero runtime dependencies, non-blocking (background thread + batching), and it never crashes your app if the log server is down.
5
+
6
+ ```bash
7
+ pip install "logsetu[django]" # Django / DRF
8
+ pip install "logsetu[flask]" # Flask
9
+ pip install "logsetu[fastapi]" # FastAPI / Starlette / any ASGI app
10
+ pip install logsetu # just the client + logging handler (zero dependencies)
11
+ ```
12
+
13
+ The extras only pull in the framework itself; the SDK has no runtime dependencies. Integrations:
14
+ [Django](#setup-2-minutes), [Flask](#flask) and [FastAPI / any ASGI app](#fastapi--starlette--asgi).
15
+
16
+ > **Coming from `logsetu-django`?** The package was renamed to `logsetu` in 0.3.0. The import name (`import logsetu`)
17
+ > and all settings are unchanged. Switch with `pip uninstall -y logsetu-django && pip install "logsetu[django]"` and
18
+ > update your requirements file. Don't keep both installed: they contain the same `logsetu` module. `logsetu-django`
19
+ > is still published with identical code for existing installs.
20
+
21
+ ## Setup (2 minutes)
22
+
23
+ ```python
24
+ # settings.py
25
+ INSTALLED_APPS = [..., "logsetu"] # optional, but nice for discoverability
26
+
27
+ LOGGING = {
28
+ "version": 1,
29
+ "disable_existing_loggers": False,
30
+ "handlers": {
31
+ "logsetu": {
32
+ "class": "logsetu.handler.LogSetuHandler",
33
+ "api_key": env("LOGSETU_API_KEY"), # from the LogSetu dashboard → Projects
34
+ "endpoint": env("LOGSETU_ENDPOINT"), # e.g. https://logs.example.com
35
+ "environment": env("ENVIRONMENT", default="production"),
36
+ "source": "django-backend",
37
+ "level": "WARNING",
38
+ },
39
+ },
40
+ "root": {"handlers": ["logsetu"], "level": "INFO"},
41
+ }
42
+
43
+ MIDDLEWARE = [
44
+ ...,
45
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
46
+ "logsetu.middleware.LogSetuMiddleware", # after auth middleware so user ids are captured
47
+ ]
48
+ ```
49
+
50
+ That's it. Every `logging` call at or above the handler's level is sent to LogSetu, and the middleware
51
+ reports unhandled exceptions (and 5xx responses) with the request path, method, view, user, redacted headers and full traceback.
52
+
53
+ ```python
54
+ import logging
55
+ log = logging.getLogger(__name__)
56
+
57
+ log.warning("Checkout is slow", extra={"duration_ms": 1830, "cart_items": 12})
58
+ try:
59
+ charge(card)
60
+ except PaymentError:
61
+ log.exception("Payment failed") # traceback is attached automatically
62
+ ```
63
+
64
+ ## Multi-tenant apps
65
+
66
+ Tag every log with the current tenant by pointing the SDK at a callable `(request) -> str | None`:
67
+
68
+ ```python
69
+ # settings.py
70
+ LOGGING["handlers"]["logsetu"]["get_tenant_id"] = "myapp.tenants.get_tenant_id"
71
+
72
+ # myapp/tenants.py
73
+ def get_tenant_id(request):
74
+ return getattr(request, "tenant", None) and request.tenant.slug
75
+ ```
76
+
77
+ Or, from anywhere inside a request (e.g. after you resolve the tenant in your own middleware):
78
+
79
+ ```python
80
+ from logsetu import update_request_context
81
+ update_request_context(tenant_id=tenant.slug, plan=tenant.plan)
82
+ ```
83
+
84
+ ## Handler options
85
+
86
+ | Option | Default | Description |
87
+ |---|---|---|
88
+ | `api_key` / `endpoint` | — | Required (or set `LOGSETU = {"API_KEY": ..., "ENDPOINT": ...}` in settings) |
89
+ | `environment` | `"production"` | Tag on every log |
90
+ | `source` | `"django"` | Tag on every log; override per record with `extra={"source": "celery"}` |
91
+ | `get_tenant_id` | `None` | Dotted path or callable `(request) -> str` |
92
+ | `include_extra` | `True` | Forward `extra={...}` fields as metadata |
93
+ | `include_request_context` | `True` | Attach request id / path / method / tenant set by the middleware |
94
+ | `flush_interval` | `2.0` | Seconds between background flushes |
95
+ | `batch_size` | `10` | Flush as soon as this many logs are queued |
96
+ | `max_queue_size` | `1000` | Drop oldest logs beyond this |
97
+ | `max_retries` | `3` | Retries with exponential backoff on 5xx / network errors |
98
+ | `timeout` | `5.0` | HTTP timeout in seconds |
99
+ | `debug` | `False` | Print every transport failure to stderr (otherwise only the first) |
100
+
101
+ ## Middleware options (`settings.LOGSETU`)
102
+
103
+ The middleware reuses the client and options of a configured `LogSetuHandler`. Without a handler it can run standalone:
104
+
105
+ ```python
106
+ LOGSETU = {
107
+ "API_KEY": env("LOGSETU_API_KEY"),
108
+ "ENDPOINT": env("LOGSETU_ENDPOINT"),
109
+ "ENVIRONMENT": "production",
110
+ "SOURCE": "django-backend",
111
+ "GET_TENANT_ID": "myapp.tenants.get_tenant_id",
112
+ "REDACT_HEADERS": ["authorization", "cookie", "x-api-key"], # defaults already cover these
113
+ "CAPTURE_5XX": True, # also report 5xx responses that didn't raise
114
+ }
115
+ ```
116
+
117
+ Every request gets a `request_id` (taken from `X-Request-ID` if present, otherwise generated) which is
118
+ attached to all logs emitted during that request and exposed as `request.logsetu_request_id`.
119
+
120
+ ## Guarantees
121
+
122
+ - **Never blocks** the request cycle: `emit()` only appends to an in-memory queue.
123
+ - **Never raises** into your app: transport errors are printed once to stderr, then suppressed.
124
+ - **Never leaks secrets**: `Authorization`, `Cookie`, `X-Api-Key`, `X-CSRFToken` and `Proxy-Authorization` are redacted.
125
+ - **Never loses the last logs** on shutdown: the queue is flushed via `atexit`.
126
+
127
+ ## Celery / management commands
128
+
129
+ Nothing special — the handler works anywhere Python's `logging` does. Set `extra={"source": "celery"}` to distinguish
130
+ workers, or configure a second handler with a different `source`.
131
+
132
+ ## Flask
133
+
134
+ ```python
135
+ import os
136
+ from flask import Flask
137
+ from logsetu.flask import LogSetu
138
+
139
+ app = Flask(__name__)
140
+ LogSetu(app, api_key=os.environ["LOGSETU_API_KEY"], endpoint=os.environ["LOGSETU_ENDPOINT"], source="flask-api")
141
+ ```
142
+
143
+ App factory? Create `logsetu = LogSetu()` at module level and call `logsetu.init_app(app)`; options you don't pass are
144
+ read from `app.config` (`LOGSETU_API_KEY`, `LOGSETU_ENDPOINT`, `LOGSETU_ENVIRONMENT`, `LOGSETU_SOURCE`).
145
+
146
+ The extension reports unhandled exceptions and 5xx responses (route, endpoint, redacted headers, traceback), and attaches
147
+ a `LogSetuHandler` to the root logger so `logging.warning(...)` and above is shipped with the request id, method and
148
+ path of the current request. Flask's own `"Exception on /path"` log line is de-duplicated.
149
+
150
+ | Option | Default | Description |
151
+ |---|---|---|
152
+ | `api_key` / `endpoint` | `app.config` | Project API key and server URL |
153
+ | `environment` / `source` | `"production"` / `"flask"` | Tags on every log |
154
+ | `get_tenant_id` | — | `callable(request) -> str \| None` or dotted path |
155
+ | `capture_logging` | `True` | Attach a handler to the root logger |
156
+ | `log_level` | `logging.WARNING` | Minimum level forwarded from `logging` |
157
+ | `capture_5xx` | `True` | Report 5xx responses that didn't raise |
158
+ | `log_requests` | `False` | One info/warn log per request |
159
+ | `redact_headers` | auth / cookie / API-key headers | Header names replaced by `[REDACTED]` |
160
+ | `client` | — | Use an existing `LogSetuClient` |
161
+
162
+ ## FastAPI / Starlette / ASGI
163
+
164
+ ```python
165
+ import os
166
+ from fastapi import FastAPI
167
+ from logsetu.fastapi import LogSetuMiddleware # alias of logsetu.asgi — works with any ASGI app
168
+
169
+ app = FastAPI()
170
+ app.add_middleware(
171
+ LogSetuMiddleware,
172
+ api_key=os.environ["LOGSETU_API_KEY"],
173
+ endpoint=os.environ["LOGSETU_ENDPOINT"],
174
+ source="fastapi",
175
+ ignore_paths=["/healthz"],
176
+ )
177
+ ```
178
+
179
+ A pure ASGI middleware (streaming responses aren't buffered). Same options as Flask plus `ignore_paths`;
180
+ `get_tenant_id` receives the ASGI `scope`, and `api_key` / `endpoint` / `environment` / `source` fall back to the
181
+ `LOGSETU_*` environment variables. Request context reaches logs from both `async def` endpoints and sync endpoints
182
+ running in the threadpool. `HTTPException`s with 4xx status codes aren't reported as errors. The request id is
183
+ available as `request.state.logsetu_request_id`.
184
+
185
+ ## License
186
+
187
+ MIT
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "logsetu"
7
+ version = "0.3.0"
8
+ description = "LogSetu SDK for Python — Django, DRF, Flask and FastAPI: ship logging records and request errors to your self-hosted LogSetu server"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Raj Verma" }]
13
+ requires-python = ">=3.9"
14
+ keywords = ["logging", "python", "django", "drf", "flask", "fastapi", "asgi", "error-tracking", "self-hosted", "sentry-alternative", "logsetu"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Framework :: Django",
18
+ "Framework :: Flask",
19
+ "Framework :: FastAPI",
20
+ "Intended Audience :: Developers",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Topic :: System :: Logging",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = []
27
+
28
+ [project.optional-dependencies]
29
+ django = ["Django>=3.2"]
30
+ flask = ["Flask>=2.2"]
31
+ fastapi = ["fastapi>=0.95"]
32
+ dev = ["Django>=3.2", "Flask>=2.2", "fastapi>=0.95", "httpx", "pytest>=8", "pytest-django>=4.8", "build", "twine"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/itsrajverma/logsetu"
36
+ Documentation = "https://github.com/itsrajverma/logsetu/tree/main/docs"
37
+ Repository = "https://github.com/itsrajverma/logsetu"
38
+ Issues = "https://github.com/itsrajverma/logsetu/issues"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/logsetu"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ include = ["src/logsetu", "tests", "README.md", "LICENSE", "pyproject.toml"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+ DJANGO_SETTINGS_MODULE = "tests.settings"
49
+ pythonpath = ["src", "."]
@@ -0,0 +1,41 @@
1
+ """LogSetu SDK for Python: Django & DRF, Flask (``logsetu.flask``) and FastAPI / ASGI (``logsetu.fastapi``).
2
+
3
+ Quick start (settings.py)::
4
+
5
+ LOGGING = {
6
+ "version": 1,
7
+ "handlers": {
8
+ "logsetu": {
9
+ "class": "logsetu.handler.LogSetuHandler",
10
+ "api_key": env("LOGSETU_API_KEY"),
11
+ "endpoint": env("LOGSETU_ENDPOINT"),
12
+ "environment": "production",
13
+ "source": "django-backend",
14
+ "level": "WARNING",
15
+ },
16
+ },
17
+ "root": {"handlers": ["logsetu"], "level": "INFO"},
18
+ }
19
+ MIDDLEWARE = [..., "logsetu.middleware.LogSetuMiddleware"]
20
+ """
21
+
22
+ from .client import LogSetuClient, get_client, __version__
23
+ from .context import get_request_context, update_request_context
24
+
25
+ __all__ = [
26
+ "LogSetuClient",
27
+ "get_client",
28
+ "get_request_context",
29
+ "update_request_context",
30
+ "capture_exception",
31
+ "__version__",
32
+ ]
33
+
34
+ default_app_config = "logsetu.apps.LogSetuConfig"
35
+
36
+
37
+ def capture_exception(exc: BaseException, message: str = "", **meta: object) -> None:
38
+ """Report an exception through the configured ``LogSetuHandler`` (via the stdlib logger)."""
39
+ import logging
40
+
41
+ logging.getLogger("logsetu.capture").error(message or f"{type(exc).__name__}: {exc}", exc_info=exc, extra=meta)
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class LogSetuConfig(AppConfig):
5
+ name = "logsetu"
6
+ verbose_name = "LogSetu"