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