yapl-kit 0.2.1__tar.gz → 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.
Files changed (61) hide show
  1. yapl_kit-0.2.1/PUBLIC.md → yapl_kit-0.3.0/PKG-INFO +159 -3
  2. yapl_kit-0.2.1/PKG-INFO → yapl_kit-0.3.0/PUBLIC.md +147 -12
  3. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/pyproject.toml +5 -2
  4. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/pyproject.toml.orig +5 -4
  5. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/__init__.py +0 -4
  6. yapl_kit-0.3.0/src/yapl/_optional.py +20 -0
  7. yapl_kit-0.3.0/src/yapl/_validation.py +21 -0
  8. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/config.py +85 -26
  9. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/buffering/capture.py +6 -0
  10. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/buffering/flush.py +5 -0
  11. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/buffering/models.py +19 -0
  12. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/models.py +1 -1
  13. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/plain.py +3 -1
  14. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/policy.py +5 -0
  15. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/text.py +3 -1
  16. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/__init__.py +2 -0
  17. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/_types.py +2 -0
  18. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/formatters.py +17 -2
  19. yapl_kit-0.3.0/src/yapl/extensions/formatting/policy.py +111 -0
  20. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/processors.py +18 -2
  21. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/structured.py +20 -4
  22. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/handler.py +10 -8
  23. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/models.py +5 -0
  24. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/policy.py +2 -5
  25. yapl_kit-0.3.0/src/yapl/extensions/tracing.py +30 -0
  26. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/webhook/client.py +18 -12
  27. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/webhook/handler.py +29 -2
  28. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/webhook/models.py +7 -3
  29. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/webhook/payloads.py +24 -8
  30. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/factory.py +168 -50
  31. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/filters/enrichment.py +2 -3
  32. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/filters/suppression.py +6 -6
  33. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/levels.py +5 -0
  34. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/logger.py +2 -2
  35. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/stack/callsite.py +1 -1
  36. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/types.py +0 -6
  37. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/context.py +0 -0
  38. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/buffering/__init__.py +0 -0
  39. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/buffering/context.py +0 -0
  40. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/__init__.py +0 -0
  41. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/colors.py +0 -0
  42. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/registry.py +0 -0
  43. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/structured.py +0 -0
  44. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/coloring/themes.py +0 -0
  45. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/_helpers.py +0 -0
  46. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/json.py +0 -0
  47. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/logfmt.py +0 -0
  48. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/plain.py +0 -0
  49. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/formatting/text.py +0 -0
  50. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/__init__.py +0 -0
  51. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/queue.py +0 -0
  52. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/queueing/worker.py +0 -0
  53. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/extensions/webhook/__init__.py +0 -0
  54. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/filters/__init__.py +0 -0
  55. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/py.typed +0 -0
  56. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/stack/__init__.py +0 -0
  57. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/stack/tracer.py +0 -0
  58. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/status.py +0 -0
  59. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl/testing.py +0 -0
  60. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl_kit/__init__.py +0 -0
  61. {yapl_kit-0.2.1 → yapl_kit-0.3.0}/src/yapl_kit/py.typed +0 -0
@@ -1,3 +1,15 @@
1
+ Metadata-Version: 2.3
2
+ Name: yapl-kit
3
+ Version: 0.3.0
4
+ Summary: YetAnotherPythonLogger - a minimal, extensible logging toolkit built on top of Python's standard logging module.
5
+ Author: SirKaiMartin
6
+ Requires-Dist: opentelemetry-api>=1.0 ; extra == 'opentelemetry'
7
+ Requires-Dist: httpx>=0.28.1 ; extra == 'webhook'
8
+ Requires-Python: >=3.13
9
+ Provides-Extra: opentelemetry
10
+ Provides-Extra: webhook
11
+ Description-Content-Type: text/markdown
12
+
1
13
  # YAPL Kit
2
14
 
3
15
  ### Yet Another Python Logger
@@ -6,8 +18,9 @@
6
18
 
7
19
  YAPL keeps the stdlib logging ecosystem intact while adding structured logging, queue-backed delivery, formatting, coloring, buffering, stack helpers, and webhook support.
8
20
 
9
- All YAPL feature families ship in the single `yapl-kit` distribution. Webhook
10
- support uses `httpx`, which is installed automatically.
21
+ All YAPL feature families ship in the single `yapl-kit` distribution. The base
22
+ install uses only the standard library; optional integrations install their
23
+ runtime dependencies through extras.
11
24
 
12
25
  ## Installation
13
26
 
@@ -25,6 +38,16 @@ With `pip`:
25
38
  pip install yapl-kit
26
39
  ```
27
40
 
41
+ Install webhook or OpenTelemetry integrations only when needed:
42
+
43
+ ```bash
44
+ pip install 'yapl-kit[webhook]'
45
+ pip install 'yapl-kit[opentelemetry]'
46
+ ```
47
+
48
+ With `uv`, use `uv add 'yapl-kit[webhook]'` or
49
+ `uv add 'yapl-kit[opentelemetry]'`.
50
+
28
51
  The canonical import namespace is `yapl`:
29
52
 
30
53
  ```python
@@ -104,6 +127,7 @@ The main design goals are:
104
127
  ## Configuration
105
128
 
106
129
  YAPL supports direct setup arguments as well as typed configuration.
130
+ Install `yapl-kit[webhook]` before configuring a webhook sink.
107
131
 
108
132
  ```python
109
133
  from yapl import WebhookConfig, YaplConfig, get_logger, setup_logging
@@ -127,6 +151,54 @@ log.info("Application started")
127
151
 
128
152
  Setup arguments override the supplied `YaplConfig` for that call without mutating the configuration object.
129
153
 
154
+ ### Filtering and custom levels
155
+
156
+ Use suppression rules to drop matching records from every configured sink. All
157
+ conditions in one rule must match; a record is dropped when any rule matches.
158
+ Use an `OutputConfig` filter when a condition should apply to just that sink.
159
+ Sink filters can be standard `logging.Filter` objects or callables that accept a
160
+ `LogRecord` and return whether to keep it.
161
+
162
+ ```python
163
+ import logging
164
+
165
+ import yapl
166
+ from yapl import OutputConfig, SuppressionRules, YaplConfig
167
+
168
+ NOTICE_LEVEL = 25
169
+ yapl.register_level("NOTICE", NOTICE_LEVEL)
170
+ yapl.register_level_method("NOTICE")
171
+
172
+ config = YaplConfig(
173
+ level=logging.DEBUG,
174
+ queue_enabled=False,
175
+ sinks=(
176
+ OutputConfig(
177
+ "stdout",
178
+ filters=(lambda record: record.levelno >= NOTICE_LEVEL,),
179
+ ),
180
+ ),
181
+ suppression=SuppressionRules().drop(
182
+ logger="myapp.noisy",
183
+ logger_prefix=True,
184
+ ),
185
+ )
186
+
187
+ try:
188
+ log = yapl.setup_logging(config)
189
+ log.notice("operator_notice")
190
+ finally:
191
+ yapl.shutdown_logging()
192
+ ```
193
+
194
+ Register a custom level before using it in configuration or logging calls. The
195
+ optional `register_level_method` call adds a convenience method such as
196
+ `log.notice`; without it, use the standard `log.log(NOTICE_LEVEL, ...)` method.
197
+ The root logging level and each sink's filter are separate gates: this example
198
+ keeps DEBUG enabled globally, then applies the NOTICE threshold only to the
199
+ console sink. See [`development/demos/filters_and_levels.py`](development/demos/filters_and_levels.py)
200
+ for a runnable example with both suppression rules and sink filtering.
201
+
130
202
  ## Output Profiles
131
203
 
132
204
  A single process can use different output profiles for different sinks.
@@ -161,6 +233,86 @@ log.info("payment_processed", request_id="req-42")
161
233
 
162
234
  Use `resource` for stable source metadata such as service, environment, region, or cluster. Keep request IDs, user IDs, trace IDs, and other high-cardinality values as normal structured fields. File output profiles also support bounded rotation with `file_max_bytes` and `file_backup_count`; those options are valid only for file sinks.
163
235
 
236
+ ### Trace correlation
237
+
238
+ When an application uses OpenTelemetry, YAPL can copy the active span's IDs
239
+ onto each record so structured outputs include `trace_id` and `span_id`:
240
+
241
+ ```python
242
+ from yapl import YaplConfig, setup_logging, shutdown_logging
243
+
244
+ setup_logging(YaplConfig(capture_trace_context=True))
245
+ # Call this during application shutdown to drain queued output.
246
+ shutdown_logging()
247
+ ```
248
+
249
+ Install the `opentelemetry` extra, which supplies `opentelemetry-api`, and
250
+ configure an OpenTelemetry SDK or instrumentation package to create active
251
+ spans. YAPL reads the IDs while the log call runs, so they are retained when sink
252
+ work is queued. Existing `trace_id` or `span_id` record fields take precedence.
253
+
254
+ ### Per-sink processing
255
+
256
+ Each `OutputConfig` can add processing that applies only to that sink:
257
+
258
+ ```python
259
+ import logging
260
+
261
+ from yapl import OutputConfig, YaplConfig
262
+ from yapl.extensions.formatting import AddServiceName, RedactSecrets
263
+
264
+
265
+ class OnlyErrors(logging.Filter):
266
+ def filter(self, record: logging.LogRecord) -> bool:
267
+ return record.levelno >= logging.ERROR
268
+
269
+
270
+ config = YaplConfig(
271
+ sinks=(
272
+ OutputConfig(
273
+ "stdout",
274
+ format="text",
275
+ processors=(AddServiceName("checkout-console"),),
276
+ enrichment={"audience": "operator"},
277
+ filters=(OnlyErrors(),),
278
+ ),
279
+ OutputConfig(
280
+ "file",
281
+ format="jsonl",
282
+ file_path="checkout.jsonl",
283
+ processors=(RedactSecrets(("password", "token")),),
284
+ fields=("timestamp", "level", "message", "context"),
285
+ ),
286
+ ),
287
+ )
288
+ ```
289
+
290
+ `filters` are standard record filters and `enrichment` adds context fields on
291
+ an isolated copy of the record. `processors` transform the normalized event.
292
+ `redact_secret_fields` and `redaction_replacement` provide a concise redaction
293
+ policy, `schema` maps output names to dotted event paths, and `fields` retains
294
+ top-level event fields. A sink with a stricter redaction policy cannot leak its
295
+ unredacted record through another sink: each sink receives an isolated record
296
+ and event copy.
297
+
298
+ The deterministic order is: record filters, context enrichment, global
299
+ processors, sink processors, redaction, schema projection, field selection,
300
+ then formatting or webhook payload construction. Existing per-sink levels and
301
+ formatters still run at their normal handler boundaries. Queue workers apply
302
+ the same policy before each target handler, and webhook processing remains on
303
+ the webhook worker.
304
+
305
+ `RedactSecrets` remains available for custom processor pipelines. When using
306
+ `redact_secret_fields`, set it to an empty tuple to explicitly disable the
307
+ webhook's default redaction for that sink; omit it to retain the webhook
308
+ configuration's default policy.
309
+
310
+ For a standard-library custom formatter, set `OutputConfig.formatter`. YAPL
311
+ applies that sink's processors and event policy to an isolated record before
312
+ delegating to the formatter. The transformed normalized event is also
313
+ available as `record.yapl_event`; without sink policies, the original formatter
314
+ instance is used unchanged.
315
+
164
316
  ## Formatting
165
317
 
166
318
  Structured, text, JSON Lines, and logfmt output are available through the formatting extension.
@@ -200,7 +352,11 @@ Queue capacity limits the number of retained records, not their total byte size.
200
352
 
201
353
  ## Webhooks
202
354
 
203
- Webhook delivery uses `httpx`, included automatically with `yapl-kit`.
355
+ Webhook delivery requires the optional `webhook` extra, which installs `httpx`:
356
+
357
+ ```bash
358
+ pip install 'yapl-kit[webhook]'
359
+ ```
204
360
 
205
361
  Configure a webhook:
206
362
 
@@ -1,12 +1,3 @@
1
- Metadata-Version: 2.3
2
- Name: yapl-kit
3
- Version: 0.2.1
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
7
- Requires-Python: >=3.13
8
- Description-Content-Type: text/markdown
9
-
10
1
  # YAPL Kit
11
2
 
12
3
  ### Yet Another Python Logger
@@ -15,8 +6,9 @@ Description-Content-Type: text/markdown
15
6
 
16
7
  YAPL keeps the stdlib logging ecosystem intact while adding structured logging, queue-backed delivery, formatting, coloring, buffering, stack helpers, and webhook support.
17
8
 
18
- All YAPL feature families ship in the single `yapl-kit` distribution. Webhook
19
- support uses `httpx`, which is installed automatically.
9
+ All YAPL feature families ship in the single `yapl-kit` distribution. The base
10
+ install uses only the standard library; optional integrations install their
11
+ runtime dependencies through extras.
20
12
 
21
13
  ## Installation
22
14
 
@@ -34,6 +26,16 @@ With `pip`:
34
26
  pip install yapl-kit
35
27
  ```
36
28
 
29
+ Install webhook or OpenTelemetry integrations only when needed:
30
+
31
+ ```bash
32
+ pip install 'yapl-kit[webhook]'
33
+ pip install 'yapl-kit[opentelemetry]'
34
+ ```
35
+
36
+ With `uv`, use `uv add 'yapl-kit[webhook]'` or
37
+ `uv add 'yapl-kit[opentelemetry]'`.
38
+
37
39
  The canonical import namespace is `yapl`:
38
40
 
39
41
  ```python
@@ -113,6 +115,7 @@ The main design goals are:
113
115
  ## Configuration
114
116
 
115
117
  YAPL supports direct setup arguments as well as typed configuration.
118
+ Install `yapl-kit[webhook]` before configuring a webhook sink.
116
119
 
117
120
  ```python
118
121
  from yapl import WebhookConfig, YaplConfig, get_logger, setup_logging
@@ -136,6 +139,54 @@ log.info("Application started")
136
139
 
137
140
  Setup arguments override the supplied `YaplConfig` for that call without mutating the configuration object.
138
141
 
142
+ ### Filtering and custom levels
143
+
144
+ Use suppression rules to drop matching records from every configured sink. All
145
+ conditions in one rule must match; a record is dropped when any rule matches.
146
+ Use an `OutputConfig` filter when a condition should apply to just that sink.
147
+ Sink filters can be standard `logging.Filter` objects or callables that accept a
148
+ `LogRecord` and return whether to keep it.
149
+
150
+ ```python
151
+ import logging
152
+
153
+ import yapl
154
+ from yapl import OutputConfig, SuppressionRules, YaplConfig
155
+
156
+ NOTICE_LEVEL = 25
157
+ yapl.register_level("NOTICE", NOTICE_LEVEL)
158
+ yapl.register_level_method("NOTICE")
159
+
160
+ config = YaplConfig(
161
+ level=logging.DEBUG,
162
+ queue_enabled=False,
163
+ sinks=(
164
+ OutputConfig(
165
+ "stdout",
166
+ filters=(lambda record: record.levelno >= NOTICE_LEVEL,),
167
+ ),
168
+ ),
169
+ suppression=SuppressionRules().drop(
170
+ logger="myapp.noisy",
171
+ logger_prefix=True,
172
+ ),
173
+ )
174
+
175
+ try:
176
+ log = yapl.setup_logging(config)
177
+ log.notice("operator_notice")
178
+ finally:
179
+ yapl.shutdown_logging()
180
+ ```
181
+
182
+ Register a custom level before using it in configuration or logging calls. The
183
+ optional `register_level_method` call adds a convenience method such as
184
+ `log.notice`; without it, use the standard `log.log(NOTICE_LEVEL, ...)` method.
185
+ The root logging level and each sink's filter are separate gates: this example
186
+ keeps DEBUG enabled globally, then applies the NOTICE threshold only to the
187
+ console sink. See [`development/demos/filters_and_levels.py`](development/demos/filters_and_levels.py)
188
+ for a runnable example with both suppression rules and sink filtering.
189
+
139
190
  ## Output Profiles
140
191
 
141
192
  A single process can use different output profiles for different sinks.
@@ -170,6 +221,86 @@ log.info("payment_processed", request_id="req-42")
170
221
 
171
222
  Use `resource` for stable source metadata such as service, environment, region, or cluster. Keep request IDs, user IDs, trace IDs, and other high-cardinality values as normal structured fields. File output profiles also support bounded rotation with `file_max_bytes` and `file_backup_count`; those options are valid only for file sinks.
172
223
 
224
+ ### Trace correlation
225
+
226
+ When an application uses OpenTelemetry, YAPL can copy the active span's IDs
227
+ onto each record so structured outputs include `trace_id` and `span_id`:
228
+
229
+ ```python
230
+ from yapl import YaplConfig, setup_logging, shutdown_logging
231
+
232
+ setup_logging(YaplConfig(capture_trace_context=True))
233
+ # Call this during application shutdown to drain queued output.
234
+ shutdown_logging()
235
+ ```
236
+
237
+ Install the `opentelemetry` extra, which supplies `opentelemetry-api`, and
238
+ configure an OpenTelemetry SDK or instrumentation package to create active
239
+ spans. YAPL reads the IDs while the log call runs, so they are retained when sink
240
+ work is queued. Existing `trace_id` or `span_id` record fields take precedence.
241
+
242
+ ### Per-sink processing
243
+
244
+ Each `OutputConfig` can add processing that applies only to that sink:
245
+
246
+ ```python
247
+ import logging
248
+
249
+ from yapl import OutputConfig, YaplConfig
250
+ from yapl.extensions.formatting import AddServiceName, RedactSecrets
251
+
252
+
253
+ class OnlyErrors(logging.Filter):
254
+ def filter(self, record: logging.LogRecord) -> bool:
255
+ return record.levelno >= logging.ERROR
256
+
257
+
258
+ config = YaplConfig(
259
+ sinks=(
260
+ OutputConfig(
261
+ "stdout",
262
+ format="text",
263
+ processors=(AddServiceName("checkout-console"),),
264
+ enrichment={"audience": "operator"},
265
+ filters=(OnlyErrors(),),
266
+ ),
267
+ OutputConfig(
268
+ "file",
269
+ format="jsonl",
270
+ file_path="checkout.jsonl",
271
+ processors=(RedactSecrets(("password", "token")),),
272
+ fields=("timestamp", "level", "message", "context"),
273
+ ),
274
+ ),
275
+ )
276
+ ```
277
+
278
+ `filters` are standard record filters and `enrichment` adds context fields on
279
+ an isolated copy of the record. `processors` transform the normalized event.
280
+ `redact_secret_fields` and `redaction_replacement` provide a concise redaction
281
+ policy, `schema` maps output names to dotted event paths, and `fields` retains
282
+ top-level event fields. A sink with a stricter redaction policy cannot leak its
283
+ unredacted record through another sink: each sink receives an isolated record
284
+ and event copy.
285
+
286
+ The deterministic order is: record filters, context enrichment, global
287
+ processors, sink processors, redaction, schema projection, field selection,
288
+ then formatting or webhook payload construction. Existing per-sink levels and
289
+ formatters still run at their normal handler boundaries. Queue workers apply
290
+ the same policy before each target handler, and webhook processing remains on
291
+ the webhook worker.
292
+
293
+ `RedactSecrets` remains available for custom processor pipelines. When using
294
+ `redact_secret_fields`, set it to an empty tuple to explicitly disable the
295
+ webhook's default redaction for that sink; omit it to retain the webhook
296
+ configuration's default policy.
297
+
298
+ For a standard-library custom formatter, set `OutputConfig.formatter`. YAPL
299
+ applies that sink's processors and event policy to an isolated record before
300
+ delegating to the formatter. The transformed normalized event is also
301
+ available as `record.yapl_event`; without sink policies, the original formatter
302
+ instance is used unchanged.
303
+
173
304
  ## Formatting
174
305
 
175
306
  Structured, text, JSON Lines, and logfmt output are available through the formatting extension.
@@ -209,7 +340,11 @@ Queue capacity limits the number of retained records, not their total byte size.
209
340
 
210
341
  ## Webhooks
211
342
 
212
- Webhook delivery uses `httpx`, included automatically with `yapl-kit`.
343
+ Webhook delivery requires the optional `webhook` extra, which installs `httpx`:
344
+
345
+ ```bash
346
+ pip install 'yapl-kit[webhook]'
347
+ ```
213
348
 
214
349
  Configure a webhook:
215
350
 
@@ -1,14 +1,17 @@
1
1
  [project]
2
2
  name = "yapl-kit"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "YetAnotherPythonLogger - a minimal, extensible logging toolkit built on top of Python's standard logging module."
5
5
  readme = "PUBLIC.md"
6
6
  requires-python = ">=3.13"
7
- dependencies = ["httpx>=0.28.1"]
8
7
 
9
8
  [[project.authors]]
10
9
  name = "SirKaiMartin"
11
10
 
11
+ [project.optional-dependencies]
12
+ webhook = ["httpx>=0.28.1"]
13
+ opentelemetry = ["opentelemetry-api>=1.0"]
14
+
12
15
  [build-system]
13
16
  requires = ["uv_build>=0.12.16,<0.13.0"]
14
17
  build-backend = "uv_build"
@@ -1,15 +1,16 @@
1
1
  [project]
2
2
  name = "yapl-kit"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "YetAnotherPythonLogger - a minimal, extensible logging toolkit built on top of Python's standard logging module."
5
5
  readme = "PUBLIC.md"
6
6
  authors = [
7
7
  { name = "SirKaiMartin" }
8
8
  ]
9
9
  requires-python = ">=3.13"
10
- dependencies = [
11
- "httpx>=0.28.1",
12
- ]
10
+
11
+ [project.optional-dependencies]
12
+ webhook = ["httpx>=0.28.1"]
13
+ opentelemetry = ["opentelemetry-api>=1.0"]
13
14
 
14
15
  [build-system]
15
16
  requires = ["uv_build>=0.12.16,<0.13.0"]
@@ -4,7 +4,6 @@ from .config import (
4
4
  ConsoleConfig,
5
5
  LoggerOverrides,
6
6
  OutputConfig,
7
- StackConfig,
8
7
  SuppressionRule,
9
8
  SuppressionRules,
10
9
  YaplConfig,
@@ -52,7 +51,6 @@ from .types import (
52
51
  OutputDestination,
53
52
  OutputFormat,
54
53
  QueueOverflowPolicy,
55
- StackMode,
56
54
  )
57
55
 
58
56
 
@@ -72,8 +70,6 @@ __all__ = [
72
70
  "QueueOverflowPolicy",
73
71
  "QueueingConfig",
74
72
  "RecordFlagPolicy",
75
- "StackConfig",
76
- "StackMode",
77
73
  "SuppressionRule",
78
74
  "SuppressionRules",
79
75
  "WebhookAuthConfig",
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+ from typing import Any
5
+
6
+
7
+ def require_optional_dependency(module_name: str, *, extra: str, feature: str) -> Any:
8
+ """Translate an absent optional module into an actionable package-extra error."""
9
+ try:
10
+ return import_module(module_name)
11
+ except ModuleNotFoundError as exc:
12
+ missing_module = exc.name
13
+ if missing_module is None or not (
14
+ module_name == missing_module or module_name.startswith(f"{missing_module}.")
15
+ ):
16
+ raise
17
+ raise ImportError(
18
+ f"{feature} requires the '{extra}' extra. "
19
+ f"Install it with 'pip install yapl-kit[{extra}]'."
20
+ ) from exc
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TypeAliasType, get_args
4
+
5
+
6
+ def validate_literal(
7
+ value: object,
8
+ alias: TypeAliasType,
9
+ field_name: str,
10
+ *,
11
+ error_message: str | None = None,
12
+ ) -> None:
13
+ """Reject values outside a PEP 695 ``Literal`` type alias."""
14
+ annotation: object = alias
15
+ while isinstance(annotation, TypeAliasType):
16
+ annotation = annotation.__value__
17
+
18
+ choices = get_args(annotation)
19
+ if value not in choices:
20
+ message = error_message or f"{field_name} must be one of {choices!r}; got {value!r}"
21
+ raise ValueError(message)