yapl-kit 0.1.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.
- yapl_kit-0.1.0/PKG-INFO +345 -0
- yapl_kit-0.1.0/README.md +331 -0
- yapl_kit-0.1.0/pyproject.toml +37 -0
- yapl_kit-0.1.0/pyproject.toml.orig +36 -0
- yapl_kit-0.1.0/src/yapl/__init__.py +97 -0
- yapl_kit-0.1.0/src/yapl/config.py +266 -0
- yapl_kit-0.1.0/src/yapl/extensions/buffering/__init__.py +24 -0
- yapl_kit-0.1.0/src/yapl/extensions/buffering/capture.py +66 -0
- yapl_kit-0.1.0/src/yapl/extensions/buffering/context.py +162 -0
- yapl_kit-0.1.0/src/yapl/extensions/buffering/flush.py +71 -0
- yapl_kit-0.1.0/src/yapl/extensions/buffering/models.py +65 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/__init__.py +45 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/colors.py +86 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/models.py +121 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/plain.py +113 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/policy.py +31 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/registry.py +119 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/structured.py +100 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/text.py +36 -0
- yapl_kit-0.1.0/src/yapl/extensions/coloring/themes.py +111 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/__init__.py +54 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/_helpers.py +53 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/_types.py +21 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/formatters.py +115 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/json.py +6 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/logfmt.py +6 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/plain.py +103 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/processors.py +391 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/structured.py +656 -0
- yapl_kit-0.1.0/src/yapl/extensions/formatting/text.py +6 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/__init__.py +25 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/handler.py +276 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/models.py +126 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/policy.py +22 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/queue.py +101 -0
- yapl_kit-0.1.0/src/yapl/extensions/queueing/worker.py +50 -0
- yapl_kit-0.1.0/src/yapl/extensions/webhook/__init__.py +54 -0
- yapl_kit-0.1.0/src/yapl/extensions/webhook/client.py +159 -0
- yapl_kit-0.1.0/src/yapl/extensions/webhook/handler.py +244 -0
- yapl_kit-0.1.0/src/yapl/extensions/webhook/models.py +210 -0
- yapl_kit-0.1.0/src/yapl/extensions/webhook/payloads.py +252 -0
- yapl_kit-0.1.0/src/yapl/factory.py +367 -0
- yapl_kit-0.1.0/src/yapl/filters/__init__.py +22 -0
- yapl_kit-0.1.0/src/yapl/filters/enrichment.py +116 -0
- yapl_kit-0.1.0/src/yapl/filters/suppression.py +69 -0
- yapl_kit-0.1.0/src/yapl/levels.py +134 -0
- yapl_kit-0.1.0/src/yapl/logger.py +247 -0
- yapl_kit-0.1.0/src/yapl/py.typed +1 -0
- yapl_kit-0.1.0/src/yapl/stack/__init__.py +14 -0
- yapl_kit-0.1.0/src/yapl/stack/callsite.py +75 -0
- yapl_kit-0.1.0/src/yapl/stack/tracer.py +157 -0
- yapl_kit-0.1.0/src/yapl/types.py +41 -0
- yapl_kit-0.1.0/src/yapl_kit/__init__.py +2 -0
- yapl_kit-0.1.0/src/yapl_kit/py.typed +1 -0
yapl_kit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: yapl-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: YetAnotherPythonLogger - a minimal, extensible logging toolkit built on top of Python's standard logging module.
|
|
5
|
+
Author: SirKaiMartin
|
|
6
|
+
Requires-Dist: httpx>=0.28.1 ; extra == 'webhook'
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Provides-Extra: buffering
|
|
9
|
+
Provides-Extra: coloring
|
|
10
|
+
Provides-Extra: formatting
|
|
11
|
+
Provides-Extra: queueing
|
|
12
|
+
Provides-Extra: webhook
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="../docs/assets/yapl_banner.png" alt="YAPL banner">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
# YAPL
|
|
20
|
+
|
|
21
|
+
### Yet Another Python Logger
|
|
22
|
+
|
|
23
|
+
> A modern, minimal, and extensible logging library built on top of Python's standard `logging` module.
|
|
24
|
+
|
|
25
|
+
YAPL keeps the stdlib logging ecosystem intact while splitting the project into a small core and opt-in modules.
|
|
26
|
+
|
|
27
|
+
- core setup stays dependency-free
|
|
28
|
+
- additional behavior is imported from `yapl.stack` and `yapl.extensions.*`
|
|
29
|
+
- buffering, queueing, formatting, coloring, and webhook delivery stay off the application hot path
|
|
30
|
+
|
|
31
|
+
YAPL does not replace Python logging. It uses stdlib handlers, formatters, filters, and propagation exactly as they are.
|
|
32
|
+
|
|
33
|
+
## Design principles
|
|
34
|
+
|
|
35
|
+
- Configuration is explicit: setup arguments override a `YaplConfig` for one call
|
|
36
|
+
without mutating it.
|
|
37
|
+
- Structured fields are built once at the logger boundary, then remain ordinary
|
|
38
|
+
`LogRecord` attributes for every handler and formatter.
|
|
39
|
+
- The default queue path performs only a bounded, non-blocking enqueue on the
|
|
40
|
+
application thread. Formatting, file output, webhooks, and file-overflow
|
|
41
|
+
fallback run on workers. Choose `overflow_policy="block"` only when deliberate
|
|
42
|
+
backpressure is more important than application latency.
|
|
43
|
+
- `shutdown_logging()` flushes and closes root handlers configured by YAPL; it
|
|
44
|
+
does not invoke the stdlib's process-wide `logging.shutdown()`.
|
|
45
|
+
|
|
46
|
+
## Guarantees and non-guarantees
|
|
47
|
+
|
|
48
|
+
In the default queued configuration, remote and sink I/O never runs on the
|
|
49
|
+
caller thread, and queue insertion does not intentionally wait. This is an
|
|
50
|
+
operational guarantee for YAPL's queue and built-in webhook path, not an
|
|
51
|
+
absolute claim that logging can never block under every configuration.
|
|
52
|
+
|
|
53
|
+
Guaranteed by default:
|
|
54
|
+
|
|
55
|
+
- Queue-backed delivery has a bounded number of records. Queue insertion uses
|
|
56
|
+
non-blocking insertion under the default overflow policy.
|
|
57
|
+
- Formatting, file/console delivery, webhook payload work, HTTP, DNS, and retry
|
|
58
|
+
waits happen on background workers.
|
|
59
|
+
- Webhook retry attempts and retry delays are bounded. A handler reuses one
|
|
60
|
+
lazily created HTTP client while it is active.
|
|
61
|
+
- Shutdown waiting is bounded by `shutdown_timeout_seconds`. When that deadline
|
|
62
|
+
expires, queued records may be discarded so the caller can continue.
|
|
63
|
+
|
|
64
|
+
Not guaranteed:
|
|
65
|
+
|
|
66
|
+
- Delivery of every record during overload, remote failure, or shutdown.
|
|
67
|
+
- A fixed byte-level memory limit. Queue sizes bound record count, while queued
|
|
68
|
+
records retain message arguments, `extra`, exceptions, tracebacks, and any
|
|
69
|
+
object graphs they reference.
|
|
70
|
+
- Termination of arbitrary custom handlers already blocked forever. Python
|
|
71
|
+
cannot safely kill user handler code. During forced reconfiguration YAPL emits
|
|
72
|
+
one `RuntimeWarning` if it observes an old queue worker still alive after its
|
|
73
|
+
bounded close wait; the new configuration uses new workers.
|
|
74
|
+
|
|
75
|
+
Explicit opt-outs:
|
|
76
|
+
|
|
77
|
+
- `overflow_policy="block"` deliberately enables application-level backpressure.
|
|
78
|
+
It may block the caller, indefinitely when `enqueue_timeout_seconds=None`.
|
|
79
|
+
- `queue_enabled=False` runs normal stdlib handlers directly, so file, console,
|
|
80
|
+
or custom sink work can occur on the caller thread.
|
|
81
|
+
|
|
82
|
+
Custom handlers are different from built-in transports: YAPL keeps their calls
|
|
83
|
+
behind its queue by default, but cannot bound how long their own code takes once
|
|
84
|
+
a worker enters it. Keep custom handlers non-blocking or give them their own
|
|
85
|
+
timeouts.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# Architecture
|
|
90
|
+
|
|
91
|
+
## Core
|
|
92
|
+
|
|
93
|
+
Import the base package when you only need standard logging setup:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import yapl
|
|
97
|
+
|
|
98
|
+
log = yapl.setup_logging(level="INFO", outputs=("stdout",), format="text")
|
|
99
|
+
|
|
100
|
+
log.info("hello world")
|
|
101
|
+
log.warning("something happened")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Core imports stay small:
|
|
105
|
+
|
|
106
|
+
- `yapl.setup_logging`
|
|
107
|
+
- `yapl.shutdown_logging`
|
|
108
|
+
- `yapl.get_logger`
|
|
109
|
+
- `yapl.config`
|
|
110
|
+
- `yapl.levels`
|
|
111
|
+
- `yapl.logger`
|
|
112
|
+
- `yapl.types`
|
|
113
|
+
|
|
114
|
+
## Optional modules
|
|
115
|
+
|
|
116
|
+
Import concrete modules when you want extra behavior:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from yapl.stack import FunctionCallTracer, get_callsite
|
|
120
|
+
from yapl.extensions.buffering import BufferedLogs, BufferingConfig
|
|
121
|
+
from yapl.extensions.formatting import JSONFormatter, StructuredFormatterConfig
|
|
122
|
+
from yapl.extensions.coloring import install_color_structured_console
|
|
123
|
+
from yapl.extensions.queueing import QueueingConfig
|
|
124
|
+
from yapl.extensions.webhook import WebhookConfig
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The `yapl.extensions` package is only a namespace. The concrete features live in submodules.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
# Quick Start
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import yapl
|
|
135
|
+
|
|
136
|
+
yapl.setup_logging(level="INFO", outputs=("stdout",), format="text")
|
|
137
|
+
log = yapl.get_logger(__name__)
|
|
138
|
+
|
|
139
|
+
log.info("Hello world")
|
|
140
|
+
log.warning("Something happened")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Structured calls work too:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
log.info("user_login", user_id=123, ip="1.2.3.4")
|
|
147
|
+
log.error("payment_failed", order_id="A-17", exc_info=True)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
For typed configuration and a webhook-enabled application, the common imports
|
|
151
|
+
are available directly from `yapl`:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from yapl import WebhookConfig, YaplConfig, get_logger, setup_logging
|
|
155
|
+
|
|
156
|
+
config = YaplConfig(
|
|
157
|
+
queue_max_size=2_000,
|
|
158
|
+
webhook=WebhookConfig("https://example.com/webhook", min_level="ERROR"),
|
|
159
|
+
)
|
|
160
|
+
setup_logging(config, outputs=("stdout", "webhook"))
|
|
161
|
+
|
|
162
|
+
logger = get_logger(__name__)
|
|
163
|
+
logger.info("Application started")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Call `shutdown_logging()` during an orderly application shutdown when you want
|
|
167
|
+
to give queued work its configured bounded drain window. It is safe to call
|
|
168
|
+
more than once; records remaining at its deadline may be discarded.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
# Buffering
|
|
173
|
+
|
|
174
|
+
Buffering captures log records during a block and replays or discards them on exit:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from yapl.extensions.buffering import BufferedLogs, BufferingConfig, install_buffering
|
|
178
|
+
|
|
179
|
+
install_buffering()
|
|
180
|
+
|
|
181
|
+
with BufferedLogs(BufferingConfig(flush_mode="ordered")):
|
|
182
|
+
log.info("preflight", phase="warmup")
|
|
183
|
+
log.warning("preflight_warning", stage=1)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The default behavior flushes buffered records on success and on error. Use the config to
|
|
187
|
+
discard instead when a block should stay silent.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
# Extended Levels
|
|
192
|
+
|
|
193
|
+
YAPL can register extra development levels:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
yapl.setup_logging(level_mode="extended")
|
|
197
|
+
|
|
198
|
+
log.trace("trace message")
|
|
199
|
+
log.dev("dev message")
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The built-in extended levels are:
|
|
203
|
+
|
|
204
|
+
| Level | Value |
|
|
205
|
+
| ----- | ----- |
|
|
206
|
+
| TRACE | 5 |
|
|
207
|
+
| DEV | 15 |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
# Stack Helpers
|
|
212
|
+
|
|
213
|
+
Callsite and tracing live in `yapl.stack`:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from yapl.stack import FunctionCallTracer, get_callsite
|
|
217
|
+
|
|
218
|
+
callsite = get_callsite()
|
|
219
|
+
|
|
220
|
+
with FunctionCallTracer():
|
|
221
|
+
run_complex_operation()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
# Formatting
|
|
227
|
+
|
|
228
|
+
Structured, text, JSON Lines, and logfmt output live in `yapl.extensions.formatting`:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from yapl.extensions.formatting import JSONFormatter, LogfmtFormatter, TextFormatter
|
|
232
|
+
|
|
233
|
+
formatter = JSONFormatter()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use sink profiles when one process needs both a human terminal and a machine stream:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
import yapl
|
|
240
|
+
from yapl import OutputConfig, YaplConfig
|
|
241
|
+
|
|
242
|
+
config = YaplConfig(
|
|
243
|
+
sinks=(
|
|
244
|
+
OutputConfig("stdout", format="text", color="auto", theme_name="dark"),
|
|
245
|
+
OutputConfig("file", format="jsonl", file_path="service.jsonl"),
|
|
246
|
+
),
|
|
247
|
+
resource={
|
|
248
|
+
"service.name": "billing",
|
|
249
|
+
"deployment.environment": "production",
|
|
250
|
+
},
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
log = yapl.setup_logging(config)
|
|
254
|
+
log.info("payment_processed", request_id="req-42")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`color="auto"` emits ANSI only to a TTY and respects `NO_COLOR`. Color is
|
|
258
|
+
restricted to `text` and `plain` stdout/stderr sinks; `json`, `jsonl`, `logfmt`,
|
|
259
|
+
files, and webhooks remain machine-readable.
|
|
260
|
+
|
|
261
|
+
Use `resource` for stable source metadata such as service, environment, region,
|
|
262
|
+
or cluster. Keep request IDs, user IDs, trace IDs, and other high-cardinality
|
|
263
|
+
values as normal structured fields. This keeps JSON streams portable while
|
|
264
|
+
allowing Grafana Alloy, OpenTelemetry Collector, or another shipper to choose
|
|
265
|
+
safe index labels.
|
|
266
|
+
|
|
267
|
+
[`examples/grafana_alloy.river`](examples/grafana_alloy.river) shows a file-based
|
|
268
|
+
Alloy pipeline. It keeps the source label static and stores request/trace
|
|
269
|
+
identifiers as structured metadata. See
|
|
270
|
+
[`examples/grafana_alloy_events.md`](examples/grafana_alloy_events.md) for
|
|
271
|
+
representative JSONL events, field definitions, and the corresponding Alloy
|
|
272
|
+
extraction. [`examples/log_output_examples.md`](examples/log_output_examples.md)
|
|
273
|
+
is a comprehensive output reference covering terminal text and color, JSONL,
|
|
274
|
+
logfmt, traces, stacks, redaction, queues, and webhooks.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
# Queueing
|
|
279
|
+
|
|
280
|
+
Queue-backed handlers keep application threads fast:
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from yapl.extensions.queueing import QueueingConfig, QueueingHandler
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Use the queue when you want formatting, file writes, or webhook delivery to happen behind a worker. The queue capacity limits records, not bytes; avoid placing unbounded object graphs in log arguments or structured fields when memory limits matter.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# Webhooks
|
|
291
|
+
|
|
292
|
+
Webhook delivery lives in `yapl.extensions.webhook`:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
from yapl.extensions.webhook import WebhookConfig
|
|
296
|
+
|
|
297
|
+
webhook = WebhookConfig(url="https://example.com/webhook", min_level="ERROR")
|
|
298
|
+
log = yapl.setup_logging(webhook=webhook)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Webhook work runs in the background so application threads do not intentionally
|
|
302
|
+
wait for network I/O. HTTP clients are reused per active webhook handler, and
|
|
303
|
+
timeouts/retries are bounded.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
# Tests
|
|
308
|
+
|
|
309
|
+
The functional suite is in `tests/` and is the default target for `pytest`.
|
|
310
|
+
The original feedback-oriented coverage is preserved in `user_tests/` and can
|
|
311
|
+
be run explicitly when checking the historical user-facing scenarios:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
uv run pytest
|
|
315
|
+
uv run pytest user_tests
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
# Installation
|
|
321
|
+
|
|
322
|
+
Core:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
pip install yapl
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
With extensions:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
pip install yapl[formatting,coloring,buffering,queueing,webhook]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
# Examples
|
|
337
|
+
|
|
338
|
+
See:
|
|
339
|
+
|
|
340
|
+
- `examples/basic_demo.py`
|
|
341
|
+
- `examples/buffering_demo.py`
|
|
342
|
+
- `examples/extended_demo.py`
|
|
343
|
+
- `examples/benchmark_demo.py`
|
|
344
|
+
|
|
345
|
+
The demos are configured from a small block at the top so you can turn features on and off quickly.
|
yapl_kit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="../docs/assets/yapl_banner.png" alt="YAPL banner">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# YAPL
|
|
6
|
+
|
|
7
|
+
### Yet Another Python Logger
|
|
8
|
+
|
|
9
|
+
> A modern, minimal, and extensible logging library built on top of Python's standard `logging` module.
|
|
10
|
+
|
|
11
|
+
YAPL keeps the stdlib logging ecosystem intact while splitting the project into a small core and opt-in modules.
|
|
12
|
+
|
|
13
|
+
- core setup stays dependency-free
|
|
14
|
+
- additional behavior is imported from `yapl.stack` and `yapl.extensions.*`
|
|
15
|
+
- buffering, queueing, formatting, coloring, and webhook delivery stay off the application hot path
|
|
16
|
+
|
|
17
|
+
YAPL does not replace Python logging. It uses stdlib handlers, formatters, filters, and propagation exactly as they are.
|
|
18
|
+
|
|
19
|
+
## Design principles
|
|
20
|
+
|
|
21
|
+
- Configuration is explicit: setup arguments override a `YaplConfig` for one call
|
|
22
|
+
without mutating it.
|
|
23
|
+
- Structured fields are built once at the logger boundary, then remain ordinary
|
|
24
|
+
`LogRecord` attributes for every handler and formatter.
|
|
25
|
+
- The default queue path performs only a bounded, non-blocking enqueue on the
|
|
26
|
+
application thread. Formatting, file output, webhooks, and file-overflow
|
|
27
|
+
fallback run on workers. Choose `overflow_policy="block"` only when deliberate
|
|
28
|
+
backpressure is more important than application latency.
|
|
29
|
+
- `shutdown_logging()` flushes and closes root handlers configured by YAPL; it
|
|
30
|
+
does not invoke the stdlib's process-wide `logging.shutdown()`.
|
|
31
|
+
|
|
32
|
+
## Guarantees and non-guarantees
|
|
33
|
+
|
|
34
|
+
In the default queued configuration, remote and sink I/O never runs on the
|
|
35
|
+
caller thread, and queue insertion does not intentionally wait. This is an
|
|
36
|
+
operational guarantee for YAPL's queue and built-in webhook path, not an
|
|
37
|
+
absolute claim that logging can never block under every configuration.
|
|
38
|
+
|
|
39
|
+
Guaranteed by default:
|
|
40
|
+
|
|
41
|
+
- Queue-backed delivery has a bounded number of records. Queue insertion uses
|
|
42
|
+
non-blocking insertion under the default overflow policy.
|
|
43
|
+
- Formatting, file/console delivery, webhook payload work, HTTP, DNS, and retry
|
|
44
|
+
waits happen on background workers.
|
|
45
|
+
- Webhook retry attempts and retry delays are bounded. A handler reuses one
|
|
46
|
+
lazily created HTTP client while it is active.
|
|
47
|
+
- Shutdown waiting is bounded by `shutdown_timeout_seconds`. When that deadline
|
|
48
|
+
expires, queued records may be discarded so the caller can continue.
|
|
49
|
+
|
|
50
|
+
Not guaranteed:
|
|
51
|
+
|
|
52
|
+
- Delivery of every record during overload, remote failure, or shutdown.
|
|
53
|
+
- A fixed byte-level memory limit. Queue sizes bound record count, while queued
|
|
54
|
+
records retain message arguments, `extra`, exceptions, tracebacks, and any
|
|
55
|
+
object graphs they reference.
|
|
56
|
+
- Termination of arbitrary custom handlers already blocked forever. Python
|
|
57
|
+
cannot safely kill user handler code. During forced reconfiguration YAPL emits
|
|
58
|
+
one `RuntimeWarning` if it observes an old queue worker still alive after its
|
|
59
|
+
bounded close wait; the new configuration uses new workers.
|
|
60
|
+
|
|
61
|
+
Explicit opt-outs:
|
|
62
|
+
|
|
63
|
+
- `overflow_policy="block"` deliberately enables application-level backpressure.
|
|
64
|
+
It may block the caller, indefinitely when `enqueue_timeout_seconds=None`.
|
|
65
|
+
- `queue_enabled=False` runs normal stdlib handlers directly, so file, console,
|
|
66
|
+
or custom sink work can occur on the caller thread.
|
|
67
|
+
|
|
68
|
+
Custom handlers are different from built-in transports: YAPL keeps their calls
|
|
69
|
+
behind its queue by default, but cannot bound how long their own code takes once
|
|
70
|
+
a worker enters it. Keep custom handlers non-blocking or give them their own
|
|
71
|
+
timeouts.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
# Architecture
|
|
76
|
+
|
|
77
|
+
## Core
|
|
78
|
+
|
|
79
|
+
Import the base package when you only need standard logging setup:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import yapl
|
|
83
|
+
|
|
84
|
+
log = yapl.setup_logging(level="INFO", outputs=("stdout",), format="text")
|
|
85
|
+
|
|
86
|
+
log.info("hello world")
|
|
87
|
+
log.warning("something happened")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Core imports stay small:
|
|
91
|
+
|
|
92
|
+
- `yapl.setup_logging`
|
|
93
|
+
- `yapl.shutdown_logging`
|
|
94
|
+
- `yapl.get_logger`
|
|
95
|
+
- `yapl.config`
|
|
96
|
+
- `yapl.levels`
|
|
97
|
+
- `yapl.logger`
|
|
98
|
+
- `yapl.types`
|
|
99
|
+
|
|
100
|
+
## Optional modules
|
|
101
|
+
|
|
102
|
+
Import concrete modules when you want extra behavior:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from yapl.stack import FunctionCallTracer, get_callsite
|
|
106
|
+
from yapl.extensions.buffering import BufferedLogs, BufferingConfig
|
|
107
|
+
from yapl.extensions.formatting import JSONFormatter, StructuredFormatterConfig
|
|
108
|
+
from yapl.extensions.coloring import install_color_structured_console
|
|
109
|
+
from yapl.extensions.queueing import QueueingConfig
|
|
110
|
+
from yapl.extensions.webhook import WebhookConfig
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The `yapl.extensions` package is only a namespace. The concrete features live in submodules.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
# Quick Start
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import yapl
|
|
121
|
+
|
|
122
|
+
yapl.setup_logging(level="INFO", outputs=("stdout",), format="text")
|
|
123
|
+
log = yapl.get_logger(__name__)
|
|
124
|
+
|
|
125
|
+
log.info("Hello world")
|
|
126
|
+
log.warning("Something happened")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Structured calls work too:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
log.info("user_login", user_id=123, ip="1.2.3.4")
|
|
133
|
+
log.error("payment_failed", order_id="A-17", exc_info=True)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
For typed configuration and a webhook-enabled application, the common imports
|
|
137
|
+
are available directly from `yapl`:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from yapl import WebhookConfig, YaplConfig, get_logger, setup_logging
|
|
141
|
+
|
|
142
|
+
config = YaplConfig(
|
|
143
|
+
queue_max_size=2_000,
|
|
144
|
+
webhook=WebhookConfig("https://example.com/webhook", min_level="ERROR"),
|
|
145
|
+
)
|
|
146
|
+
setup_logging(config, outputs=("stdout", "webhook"))
|
|
147
|
+
|
|
148
|
+
logger = get_logger(__name__)
|
|
149
|
+
logger.info("Application started")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Call `shutdown_logging()` during an orderly application shutdown when you want
|
|
153
|
+
to give queued work its configured bounded drain window. It is safe to call
|
|
154
|
+
more than once; records remaining at its deadline may be discarded.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
# Buffering
|
|
159
|
+
|
|
160
|
+
Buffering captures log records during a block and replays or discards them on exit:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from yapl.extensions.buffering import BufferedLogs, BufferingConfig, install_buffering
|
|
164
|
+
|
|
165
|
+
install_buffering()
|
|
166
|
+
|
|
167
|
+
with BufferedLogs(BufferingConfig(flush_mode="ordered")):
|
|
168
|
+
log.info("preflight", phase="warmup")
|
|
169
|
+
log.warning("preflight_warning", stage=1)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The default behavior flushes buffered records on success and on error. Use the config to
|
|
173
|
+
discard instead when a block should stay silent.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
# Extended Levels
|
|
178
|
+
|
|
179
|
+
YAPL can register extra development levels:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
yapl.setup_logging(level_mode="extended")
|
|
183
|
+
|
|
184
|
+
log.trace("trace message")
|
|
185
|
+
log.dev("dev message")
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The built-in extended levels are:
|
|
189
|
+
|
|
190
|
+
| Level | Value |
|
|
191
|
+
| ----- | ----- |
|
|
192
|
+
| TRACE | 5 |
|
|
193
|
+
| DEV | 15 |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
# Stack Helpers
|
|
198
|
+
|
|
199
|
+
Callsite and tracing live in `yapl.stack`:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from yapl.stack import FunctionCallTracer, get_callsite
|
|
203
|
+
|
|
204
|
+
callsite = get_callsite()
|
|
205
|
+
|
|
206
|
+
with FunctionCallTracer():
|
|
207
|
+
run_complex_operation()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
# Formatting
|
|
213
|
+
|
|
214
|
+
Structured, text, JSON Lines, and logfmt output live in `yapl.extensions.formatting`:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from yapl.extensions.formatting import JSONFormatter, LogfmtFormatter, TextFormatter
|
|
218
|
+
|
|
219
|
+
formatter = JSONFormatter()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Use sink profiles when one process needs both a human terminal and a machine stream:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
import yapl
|
|
226
|
+
from yapl import OutputConfig, YaplConfig
|
|
227
|
+
|
|
228
|
+
config = YaplConfig(
|
|
229
|
+
sinks=(
|
|
230
|
+
OutputConfig("stdout", format="text", color="auto", theme_name="dark"),
|
|
231
|
+
OutputConfig("file", format="jsonl", file_path="service.jsonl"),
|
|
232
|
+
),
|
|
233
|
+
resource={
|
|
234
|
+
"service.name": "billing",
|
|
235
|
+
"deployment.environment": "production",
|
|
236
|
+
},
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
log = yapl.setup_logging(config)
|
|
240
|
+
log.info("payment_processed", request_id="req-42")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`color="auto"` emits ANSI only to a TTY and respects `NO_COLOR`. Color is
|
|
244
|
+
restricted to `text` and `plain` stdout/stderr sinks; `json`, `jsonl`, `logfmt`,
|
|
245
|
+
files, and webhooks remain machine-readable.
|
|
246
|
+
|
|
247
|
+
Use `resource` for stable source metadata such as service, environment, region,
|
|
248
|
+
or cluster. Keep request IDs, user IDs, trace IDs, and other high-cardinality
|
|
249
|
+
values as normal structured fields. This keeps JSON streams portable while
|
|
250
|
+
allowing Grafana Alloy, OpenTelemetry Collector, or another shipper to choose
|
|
251
|
+
safe index labels.
|
|
252
|
+
|
|
253
|
+
[`examples/grafana_alloy.river`](examples/grafana_alloy.river) shows a file-based
|
|
254
|
+
Alloy pipeline. It keeps the source label static and stores request/trace
|
|
255
|
+
identifiers as structured metadata. See
|
|
256
|
+
[`examples/grafana_alloy_events.md`](examples/grafana_alloy_events.md) for
|
|
257
|
+
representative JSONL events, field definitions, and the corresponding Alloy
|
|
258
|
+
extraction. [`examples/log_output_examples.md`](examples/log_output_examples.md)
|
|
259
|
+
is a comprehensive output reference covering terminal text and color, JSONL,
|
|
260
|
+
logfmt, traces, stacks, redaction, queues, and webhooks.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
# Queueing
|
|
265
|
+
|
|
266
|
+
Queue-backed handlers keep application threads fast:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from yapl.extensions.queueing import QueueingConfig, QueueingHandler
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Use the queue when you want formatting, file writes, or webhook delivery to happen behind a worker. The queue capacity limits records, not bytes; avoid placing unbounded object graphs in log arguments or structured fields when memory limits matter.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
# Webhooks
|
|
277
|
+
|
|
278
|
+
Webhook delivery lives in `yapl.extensions.webhook`:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from yapl.extensions.webhook import WebhookConfig
|
|
282
|
+
|
|
283
|
+
webhook = WebhookConfig(url="https://example.com/webhook", min_level="ERROR")
|
|
284
|
+
log = yapl.setup_logging(webhook=webhook)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Webhook work runs in the background so application threads do not intentionally
|
|
288
|
+
wait for network I/O. HTTP clients are reused per active webhook handler, and
|
|
289
|
+
timeouts/retries are bounded.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
# Tests
|
|
294
|
+
|
|
295
|
+
The functional suite is in `tests/` and is the default target for `pytest`.
|
|
296
|
+
The original feedback-oriented coverage is preserved in `user_tests/` and can
|
|
297
|
+
be run explicitly when checking the historical user-facing scenarios:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
uv run pytest
|
|
301
|
+
uv run pytest user_tests
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
# Installation
|
|
307
|
+
|
|
308
|
+
Core:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
pip install yapl
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
With extensions:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
pip install yapl[formatting,coloring,buffering,queueing,webhook]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
# Examples
|
|
323
|
+
|
|
324
|
+
See:
|
|
325
|
+
|
|
326
|
+
- `examples/basic_demo.py`
|
|
327
|
+
- `examples/buffering_demo.py`
|
|
328
|
+
- `examples/extended_demo.py`
|
|
329
|
+
- `examples/benchmark_demo.py`
|
|
330
|
+
|
|
331
|
+
The demos are configured from a small block at the top so you can turn features on and off quickly.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "yapl-kit"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "YetAnotherPythonLogger - a minimal, extensible logging toolkit built on top of Python's standard logging module."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[[project.authors]]
|
|
10
|
+
name = "SirKaiMartin"
|
|
11
|
+
|
|
12
|
+
[project.optional-dependencies]
|
|
13
|
+
formatting = []
|
|
14
|
+
coloring = []
|
|
15
|
+
buffering = []
|
|
16
|
+
queueing = []
|
|
17
|
+
webhook = ["httpx>=0.28.1"]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build>=0.12.16,<0.13.0"]
|
|
21
|
+
build-backend = "uv_build"
|
|
22
|
+
|
|
23
|
+
[tool.uv.build-backend]
|
|
24
|
+
module-name = [
|
|
25
|
+
"yapl",
|
|
26
|
+
"yapl_kit",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[tool.pytest.ini_options]
|
|
30
|
+
testpaths = ["tests"]
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=9.1.1",
|
|
35
|
+
"ruff>=0.16.8",
|
|
36
|
+
"ty>=0.0.82",
|
|
37
|
+
]
|