xakki-log-python 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.5
2
+ Name: xakki-log-python
3
+ Version: 0.4.0
4
+ Summary: Structured logging library for Python — GELF/Graylog-oriented, Python port of xakki/log-laravel
5
+ Project-URL: Homepage, https://github.com/Xakki/log-python
6
+ Project-URL: Source, https://github.com/Xakki/log-python
7
+ Author: xakki
8
+ License-Expression: GPL-3.0-or-later
9
+ License-File: LICENSE
10
+ Keywords: fluent-bit,gelf,graylog,logging,structured-logging
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: System :: Logging
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Provides-Extra: dev
23
+ Requires-Dist: mypy>=1.11; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
25
+ Requires-Dist: pytest>=8.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
27
+ Provides-Extra: redis
28
+ Requires-Dist: redis>=5.0; extra == 'redis'
29
+ Provides-Extra: sqlalchemy
30
+ Requires-Dist: sqlalchemy>=2.0; extra == 'sqlalchemy'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # xakki-log-python
34
+
35
+ Structured JSON logging for Python, compatible with the field and type contract
36
+ of [`xakki/log-laravel`](https://github.com/Xakki/log-laravel) and intended for
37
+ stdout → fluent-bit → Graylog/GELF pipelines.
38
+
39
+ Distribution: `xakki-log-python` (formerly `xakki-pylog`); import name: `log_python` (formerly `pylog`).
40
+
41
+ The core uses only the Python standard library. Redis and SQLAlchemy support are
42
+ optional extras.
43
+
44
+ The release version has one editable source: `[project].version` in
45
+ `pyproject.toml`. `LOGGER_VER` reads the installed distribution metadata,
46
+ `log_python.__version__` aliases it, and every record emits it as `extra.log_ver`.
47
+
48
+ ## Requirements
49
+
50
+ - Python 3.11+
51
+ - No runtime dependencies for the core package
52
+
53
+ ## Installation
54
+
55
+ The package is published on [PyPI](https://pypi.org/project/xakki-log-python/):
56
+
57
+ ```sh
58
+ pip install xakki-log-python
59
+ # optional integrations
60
+ pip install 'xakki-log-python[redis]'
61
+ pip install 'xakki-log-python[sqlalchemy]'
62
+ ```
63
+
64
+ The same package name works with other package managers:
65
+
66
+ ```sh
67
+ uv add xakki-log-python
68
+ poetry add xakki-log-python
69
+ pdm add xakki-log-python
70
+ pipenv install xakki-log-python
71
+ ```
72
+
73
+ Extras use the same syntax, e.g. `uv add 'xakki-log-python[redis]'`.
74
+
75
+ To install the unreleased development version straight from Git:
76
+
77
+ ```sh
78
+ pip install git+https://github.com/Xakki/log-python
79
+ ```
80
+
81
+ ## Quick start
82
+
83
+ ```python
84
+ import logging
85
+ import log_python
86
+
87
+ log_python.setup()
88
+
89
+ logger = logging.getLogger("orders")
90
+ logger.info(
91
+ "order paid",
92
+ extra={"context": {"order_id": "42", "order_money": "19.99"}},
93
+ )
94
+ ```
95
+
96
+ `order_id` is emitted as an integer and `order_money` as a float according to
97
+ the shared context field naming contract. Every record also receives
98
+ `log_type`, `file`, `request_id`, stable process metadata and, at warning or
99
+ above, a bounded trace.
100
+
101
+ `log_python.setup()` configures one idempotent root `StreamHandler`. It preserves
102
+ unrelated handlers and writes one newline-terminated JSON object per event.
103
+
104
+ ### Configuration
105
+
106
+ `PylogConfig.from_env()` reads a startup snapshot:
107
+
108
+ | Variable | Purpose | Default |
109
+ |---|---|---|
110
+ | `APP_NAME` | `extra.app_name` | unset |
111
+ | `APP_ENV` | `extra.app_env` | unset |
112
+ | `APP_VERSION` | `extra.app_ver` | unset |
113
+ | `LOG_TIER` | `extra.tier` (`web`, `api`, `worker`, `cron`) | unset |
114
+ | `RELEASE_TAG` | release/build id | unset |
115
+ | `RELEASE_TIME` | release time | unset |
116
+ | `LOG_MESSAGE_LIMIT` | source message character limit | `3024` |
117
+ | `LOG_ALLOW_MEMORY` | add process memory fields | `false` |
118
+ | `LOG_SNAKE_CASE` | normalize context keys before coercion | `false` |
119
+ | `LOG_CAPTURE_HANDLERS` | install chained uncaught-exception hooks | `false` |
120
+
121
+ Boolean parsing accepts `1/true/yes/on` and `0/false/no/off` (case-insensitive).
122
+ Invalid values raise `ValueError` during configuration instead of silently
123
+ changing behavior.
124
+
125
+ Programmatic configuration:
126
+
127
+ ```python
128
+ from log_python import ExtraConfig, PylogConfig, setup
129
+
130
+ config = PylogConfig(
131
+ extra=ExtraConfig(
132
+ app_name="orders",
133
+ app_env="prod",
134
+ app_ver="2026.08.13",
135
+ tier="worker",
136
+ ),
137
+ capture_handlers=True,
138
+ )
139
+ setup(config=config)
140
+ ```
141
+
142
+ Available formatters are `JsonFormatter` (default), `LineFormatter`, and
143
+ `ConsoleFormatter`:
144
+
145
+ ```python
146
+ log_python.setup(formatter="console", replace=True)
147
+ ```
148
+
149
+ ## Context and request correlation
150
+
151
+ Bound context is isolated with `contextvars`, so concurrent threads and async
152
+ tasks do not overwrite one another:
153
+
154
+ ```python
155
+ from log_python import bind, bound_context, clear, set_request_id
156
+
157
+ set_request_id(incoming_request_id)
158
+ bind(user_id=7)
159
+
160
+ with bound_context(order_id=42):
161
+ logger.info("order loaded")
162
+
163
+ clear()
164
+ ```
165
+
166
+ Web middleware can call `remote_ip(value)`. Queue workers should call
167
+ `reset_request_id()` and `clear()` between jobs.
168
+
169
+ ## Exceptions and redaction
170
+
171
+ Use normal stdlib exception logging:
172
+
173
+ ```python
174
+ try:
175
+ run_job()
176
+ except RuntimeError:
177
+ logger.exception("job failed", extra={"context": {"job_id": 42}})
178
+ ```
179
+
180
+ Exception class, sanitized message, code, origin and coalesced trace are emitted
181
+ as structured context. Sensitive keys and recognizable Bearer/Basic tokens,
182
+ JWTs, key/value credentials and URL passwords are redacted before
183
+ serialization. `skipTrace` can suppress trace collection for a specific event
184
+ and is never emitted itself.
185
+
186
+ When `LOG_CAPTURE_HANDLERS=true` (or setup enables capture explicitly), log_python
187
+ chains the process/thread exception hooks and `warnings.showwarning`. Uncaught
188
+ exceptions get `log_type=exception`; runtime warnings get `log_type=trigger`.
189
+ Python cannot reliably turn native crashes into structured JSON, so supervisors
190
+ and `faulthandler` remain the fallback for those failures.
191
+
192
+ ## Optional integrations
193
+
194
+ ### Redis
195
+
196
+ `log_python.handlers.redis.RedisHandler` accepts any redis-py-compatible client,
197
+ uses `LPUSH`, and applies `LTRIM` in the same transaction when `cap_size > 0`.
198
+ It stores exactly the result of its configured formatter, so attach
199
+ `JsonFormatter` (or reuse the formatter installed by `log_python.setup()`):
200
+
201
+ ```python
202
+ from redis import Redis
203
+ from log_python.handlers.redis import RedisHandler
204
+
205
+ handler = RedisHandler(Redis.from_url("redis://localhost"), key="logs", cap_size=10_000)
206
+ handler.setFormatter(log_python.JsonFormatter(log_python.PylogConfig.from_env()))
207
+ ```
208
+
209
+ ### SQLAlchemy
210
+
211
+ ```python
212
+ from log_python.integrations.sqlalchemy import install_sqlalchemy_logging
213
+
214
+ install_sqlalchemy_logging(
215
+ engine,
216
+ select_threshold_ms=200,
217
+ all_threshold_ms=500,
218
+ include_bindings=False, # keep disabled in production
219
+ )
220
+ ```
221
+
222
+ The integration emits `db_table`, `db_time_ms`, `db_bindings`, `sql_type`, and
223
+ `tag=sql`. Telescope queries are ignored; long or numerous bindings are not
224
+ expanded. Bindings are disabled by default and pass through the secret redactor
225
+ before serialization when explicitly enabled.
226
+
227
+ ### Worker heartbeat
228
+
229
+ ```python
230
+ from log_python import Heartbeat
231
+
232
+ heartbeat = Heartbeat(logging.getLogger("worker"), name="default", interval=120)
233
+ heartbeat.start()
234
+ # ...
235
+ heartbeat.stop(status=0)
236
+ ```
237
+
238
+ ## Wire contract
239
+
240
+ The normative, language-independent specification remains in the reference
241
+ repository and is not duplicated here:
242
+
243
+ - [LoggingRules.md](https://github.com/Xakki/log-laravel/blob/main/docs/LoggingRules.md)
244
+ - [ContextFieldNaming.md](https://github.com/Xakki/log-laravel/blob/main/docs/ContextFieldNaming.md)
245
+ - [Graylog.md](https://github.com/Xakki/log-laravel/blob/main/docs/Graylog.md)
246
+ - [PhytonLog.md](https://github.com/Xakki/log-laravel/blob/main/docs/PhytonLog.md)
247
+
248
+ The JSON formatter provides lowercase `level_name`, Monolog-compatible numeric
249
+ `level` values (`100`–`600`), RFC-3339 timestamps with timezone and
250
+ microseconds, canonical `app_name`/`app_env` extras, one coalesced trace field,
251
+ and iterative UTF-8-aware trimming to the configured wire budget (1400 bytes by
252
+ default) without dropping the required process-extra fields.
253
+
254
+ ## Development
255
+
256
+ The repository is Docker-only. Do not run Python tooling directly on the host.
257
+
258
+ ```sh
259
+ make docker-build
260
+ make deps-i
261
+ make test # ruff format/check + mypy --strict + pytest
262
+ make build
263
+ make test-install # uv, Poetry, PDM, Pipenv and pip
264
+ ```
265
+
266
+ Run `make help` for all targets.
267
+
268
+ # Related projects
269
+
270
+ Sibling logging libraries sharing one field contract ([Logging Rules](https://github.com/Xakki/log-laravel/blob/main/docs/LoggingRules.md)):
271
+
272
+ * [log-laravel](https://github.com/Xakki/log-laravel) — `xakki/log-laravel`. Laravel / Monolog: custom `LogManager`, processors, redaction, SQL and queue logging.
273
+ * [log-symfony](https://github.com/Xakki/log-symfony) — `xakki/log-symfony`. PSR-3 / Symfony / Monolog 3: context enrichment, type coercion, redaction.
274
+ * [log-php](https://github.com/Xakki/log-php) — `xakki/log-php`. Plain PHP (no framework, PSR-3; PHP 5.6+ on branch `master56`): error/exception catcher with pluggable storages.
275
+ * [log-fluent](https://github.com/Xakki/log-fluent) — `xakki/log-fluent`. Fluent Bit config bundle that ships these logs to Graylog (GELF) and Prometheus.
@@ -0,0 +1,19 @@
1
+ log_python/__init__.py,sha256=3lCnzY_wUI9HWTOo9Ux7Rvy86zbg9Pjz0EmWH3bO1OE,4333
2
+ log_python/config.py,sha256=YRYptW5dZ1jLzywyFhLwpvS-UQmSZC6mXwSTZHclNTA,5713
3
+ log_python/const.py,sha256=ZB3aQU-mH7Ot6kPpGX8vkWy1WL8GgTRirhLU41IMlfo,1587
4
+ log_python/context.py,sha256=xv7Alw8s0kXmyJ0emmKH_JBqnu_54j6GTjOeySPyY64,27603
5
+ log_python/formatters.py,sha256=fIxjUZ_vgsb_HTSB0ZvnQtd-Qw1lkRuUtv1kUFQH70E,8887
6
+ log_python/heartbeat.py,sha256=5u0PWE-G_E-Sc74hb2DAV_PIFxtsSEtNlbmgis-rGN0,1913
7
+ log_python/logtype.py,sha256=NiiW_F_ftZYwiZWYyJhJ2pyJnBR5T550P9TnVX-XakY,7176
8
+ log_python/manager.py,sha256=eawC43t6dzlc7nUYHWMmgoWFrYG5hsjkq1ZYqjiOJps,20825
9
+ log_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ log_python/redactor.py,sha256=0-8xsFEyfvOct-PEoBDvXqbP0WxhAzEp6X6So3DFJak,13584
11
+ log_python/trace.py,sha256=ngOa1TzBkANQ325bqBMW95lOHDtbOjf4HfRayVFsnbs,25477
12
+ log_python/handlers/__init__.py,sha256=8tI5Ju8PY8KnoqfU093056XGymjrJjdNUmH7XmvzWbE,33
13
+ log_python/handlers/redis.py,sha256=DfeAK63gUg6kdnkZK-SiJ3WkNyviGynj8h13jW2mxxc,1452
14
+ log_python/integrations/__init__.py,sha256=OSJkNfP33ZiZVYRru_-r_wI8Wa_PvyaoWsiuXBKCroY,39
15
+ log_python/integrations/sqlalchemy.py,sha256=9UXcHtjKwjbv6jVZvsXGAoypOiOBM65u9_IEecCWcC4,3336
16
+ xakki_log_python-0.4.0.dist-info/METADATA,sha256=fZNN0zGfpmDX7H9CDP3fwSBXbk8liKZuN5b6LcvV4ZQ,9323
17
+ xakki_log_python-0.4.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
18
+ xakki_log_python-0.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
19
+ xakki_log_python-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any