log-foundry 0.10.2.dev4__tar.gz → 0.10.2.dev6__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.
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/PKG-INFO +49 -4
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/README.md +48 -3
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/__init__.py +5 -2
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/context.py +70 -3
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/decorator.py +7 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/LICENSE +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/config.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.10.2.dev4 → log_foundry-0.10.2.dev6}/src/log_foundry/worker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: log-foundry
|
|
3
|
-
Version: 0.10.2.
|
|
3
|
+
Version: 0.10.2.dev6
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -184,8 +184,10 @@ span: if one exists, the new span copies its `trace_id` and records its `span_id
|
|
|
184
184
|
new span is then pushed, so anything it calls sees *it* as the parent. On exit the span is
|
|
185
185
|
popped by restoring the stack to its exact prior state (via a token, not a blind pop), which
|
|
186
186
|
stays correct even when code branches into concurrent tasks. Because the stack lives in a
|
|
187
|
-
context variable, every
|
|
188
|
-
|
|
187
|
+
context variable, every asyncio task gets its own isolated copy — so `asyncio.gather` children
|
|
188
|
+
share their parent's trace, and baggage set in one task never leaks into a sibling. A new thread
|
|
189
|
+
gets a fresh context rather than a copy, so nothing follows it there unless the caller copies one
|
|
190
|
+
(as `asyncio.to_thread` does).
|
|
189
191
|
|
|
190
192
|
### When the worker flushes
|
|
191
193
|
|
|
@@ -291,7 +293,14 @@ def process_payment(user_id: int) -> str:
|
|
|
291
293
|
|
|
292
294
|
- **`set_baggage(**kv)`** — attach trace-scoped context that is merged into the `fields` of
|
|
293
295
|
every subsequent event in the same execution flow. Precedence, lowest to highest: config
|
|
294
|
-
`defaults` → span `defaults` → baggage → per-call `fields`.
|
|
296
|
+
`defaults` → span `defaults` → baggage → per-call `fields`. **Trace-scoped means it ends
|
|
297
|
+
with the trace:** when the outermost `@trace` call returns *or raises*, the baggage in effect
|
|
298
|
+
before it is restored, so one request's keys do not reach the next request's events. Nested
|
|
299
|
+
calls do not reset — baggage set three calls deep stays visible to its parent and to the
|
|
300
|
+
siblings after it. Set with no span open it becomes a process-level default that later traces
|
|
301
|
+
inherit and restore to (`configure(defaults=...)` is the better tool for that) — and a process
|
|
302
|
+
that logs *without* `@trace` has no root span to release anything, so it needs
|
|
303
|
+
[`reset_context()`](#clearing-context-in-a-long-lived-process).
|
|
295
304
|
- **`echo=True`** — *additionally* write a human-readable `LEVEL message` line to the console
|
|
296
305
|
(`sys.stderr` by default), synchronously, without waiting for the async flush. The event
|
|
297
306
|
still rides the normal pipeline to the sink — echo never redirects.
|
|
@@ -339,6 +348,7 @@ def handler(event, context):
|
|
|
339
348
|
| `current_traceparent()` | This span as a W3C `traceparent` string, or `None` if no span is active. |
|
|
340
349
|
| `current_trace_context()` | `(trace_id, span_id)`, for when moving two fields beats moving a string. |
|
|
341
350
|
| `current_baggage_header()` | Current baggage in W3C `baggage` format (`""` when empty). |
|
|
351
|
+
| `reset_context()` | Clear baggage and any adopted context. `@trace` users do not need it. Never raises. |
|
|
342
352
|
|
|
343
353
|
Details worth knowing:
|
|
344
354
|
|
|
@@ -363,10 +373,45 @@ Details worth knowing:
|
|
|
363
373
|
join because one field was malformed is worse. Headers over 8192 bytes are rejected. Values
|
|
364
374
|
are percent-encoded, so `,` `=` and non-ASCII round-trip; non-string values are serialized
|
|
365
375
|
with `str()`, so a dict arrives as its repr.
|
|
376
|
+
- **An adopted context is consumed by one root span.** It applies to the next root span opened
|
|
377
|
+
and does not survive it, so the invocation after it starts a fresh trace unless it adopts
|
|
378
|
+
again. That is what stops a warm container from logging every later invocation into the first
|
|
379
|
+
caller's trace. A batch that fans out to several *sibling* root spans therefore needs one
|
|
380
|
+
`continue_trace()` per item — or, better, one `@trace` entry point so the items are nested
|
|
381
|
+
spans of a single trace.
|
|
366
382
|
- **Sampling is not honoured.** `traceparent`'s flags byte is parsed and ignored, and outbound
|
|
367
383
|
is always `01`: this library records every span, so respecting another system's sampling
|
|
368
384
|
decision would mean dropping them.
|
|
369
385
|
|
|
386
|
+
#### Clearing context in a long-lived process
|
|
387
|
+
|
|
388
|
+
`@trace` releases both baggage and the adopted context when the outermost decorated call returns
|
|
389
|
+
or raises, so **most callers never need `reset_context()`**. It exists for the two cases where no
|
|
390
|
+
root-span exit releases them in *your* context:
|
|
391
|
+
|
|
392
|
+
```python
|
|
393
|
+
lf.reset_context() # clears baggage *and* any adopted trace context
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
- **You use the emitters without `@trace`.** An orphan log opens no span, so nothing releases
|
|
397
|
+
what `set_baggage()` or `continue_trace()` set. In a process that reuses one thread across
|
|
398
|
+
requests — the main thread, a pooled worker, a warm Lambda container — that state reaches the
|
|
399
|
+
next request. Call `reset_context()` when a unit of work ends. (An orphan log never joins an
|
|
400
|
+
adopted trace either: it mints its own `trace_id`, and the adoption simply waits to claim the
|
|
401
|
+
next root span, whenever one happens to run.)
|
|
402
|
+
- **You adopt outside the span and dispatch into a task.** The release runs in whichever
|
|
403
|
+
context the root span's `finally` runs in, so `continue_trace()` here followed by
|
|
404
|
+
`asyncio.run(main())` clears the adoption in the task's copy of the context while this one
|
|
405
|
+
keeps it. `contextvars` has no way to write back to a parent context, so clear it yourself.
|
|
406
|
+
Adopting on the entry point's first line — the documented placement — is inside the span and
|
|
407
|
+
needs nothing.
|
|
408
|
+
|
|
409
|
+
It clears rather than restores: a process-level baggage default set before any span is erased
|
|
410
|
+
too — permanently when you call it outside a span. Prefer that. Called *inside* a span it also
|
|
411
|
+
empties the `span.start` / `span.end` events of baggage, because those are stamped with the
|
|
412
|
+
span's *final* baggage at close, and that span's exit then restores the pre-span baggage anyway,
|
|
413
|
+
undoing the erasure. It never raises.
|
|
414
|
+
|
|
370
415
|
### Sinks
|
|
371
416
|
|
|
372
417
|
A **sink** is the swappable output transport — any object satisfying the `Sink` protocol. It
|
|
@@ -148,8 +148,10 @@ span: if one exists, the new span copies its `trace_id` and records its `span_id
|
|
|
148
148
|
new span is then pushed, so anything it calls sees *it* as the parent. On exit the span is
|
|
149
149
|
popped by restoring the stack to its exact prior state (via a token, not a blind pop), which
|
|
150
150
|
stays correct even when code branches into concurrent tasks. Because the stack lives in a
|
|
151
|
-
context variable, every
|
|
152
|
-
|
|
151
|
+
context variable, every asyncio task gets its own isolated copy — so `asyncio.gather` children
|
|
152
|
+
share their parent's trace, and baggage set in one task never leaks into a sibling. A new thread
|
|
153
|
+
gets a fresh context rather than a copy, so nothing follows it there unless the caller copies one
|
|
154
|
+
(as `asyncio.to_thread` does).
|
|
153
155
|
|
|
154
156
|
### When the worker flushes
|
|
155
157
|
|
|
@@ -255,7 +257,14 @@ def process_payment(user_id: int) -> str:
|
|
|
255
257
|
|
|
256
258
|
- **`set_baggage(**kv)`** — attach trace-scoped context that is merged into the `fields` of
|
|
257
259
|
every subsequent event in the same execution flow. Precedence, lowest to highest: config
|
|
258
|
-
`defaults` → span `defaults` → baggage → per-call `fields`.
|
|
260
|
+
`defaults` → span `defaults` → baggage → per-call `fields`. **Trace-scoped means it ends
|
|
261
|
+
with the trace:** when the outermost `@trace` call returns *or raises*, the baggage in effect
|
|
262
|
+
before it is restored, so one request's keys do not reach the next request's events. Nested
|
|
263
|
+
calls do not reset — baggage set three calls deep stays visible to its parent and to the
|
|
264
|
+
siblings after it. Set with no span open it becomes a process-level default that later traces
|
|
265
|
+
inherit and restore to (`configure(defaults=...)` is the better tool for that) — and a process
|
|
266
|
+
that logs *without* `@trace` has no root span to release anything, so it needs
|
|
267
|
+
[`reset_context()`](#clearing-context-in-a-long-lived-process).
|
|
259
268
|
- **`echo=True`** — *additionally* write a human-readable `LEVEL message` line to the console
|
|
260
269
|
(`sys.stderr` by default), synchronously, without waiting for the async flush. The event
|
|
261
270
|
still rides the normal pipeline to the sink — echo never redirects.
|
|
@@ -303,6 +312,7 @@ def handler(event, context):
|
|
|
303
312
|
| `current_traceparent()` | This span as a W3C `traceparent` string, or `None` if no span is active. |
|
|
304
313
|
| `current_trace_context()` | `(trace_id, span_id)`, for when moving two fields beats moving a string. |
|
|
305
314
|
| `current_baggage_header()` | Current baggage in W3C `baggage` format (`""` when empty). |
|
|
315
|
+
| `reset_context()` | Clear baggage and any adopted context. `@trace` users do not need it. Never raises. |
|
|
306
316
|
|
|
307
317
|
Details worth knowing:
|
|
308
318
|
|
|
@@ -327,10 +337,45 @@ Details worth knowing:
|
|
|
327
337
|
join because one field was malformed is worse. Headers over 8192 bytes are rejected. Values
|
|
328
338
|
are percent-encoded, so `,` `=` and non-ASCII round-trip; non-string values are serialized
|
|
329
339
|
with `str()`, so a dict arrives as its repr.
|
|
340
|
+
- **An adopted context is consumed by one root span.** It applies to the next root span opened
|
|
341
|
+
and does not survive it, so the invocation after it starts a fresh trace unless it adopts
|
|
342
|
+
again. That is what stops a warm container from logging every later invocation into the first
|
|
343
|
+
caller's trace. A batch that fans out to several *sibling* root spans therefore needs one
|
|
344
|
+
`continue_trace()` per item — or, better, one `@trace` entry point so the items are nested
|
|
345
|
+
spans of a single trace.
|
|
330
346
|
- **Sampling is not honoured.** `traceparent`'s flags byte is parsed and ignored, and outbound
|
|
331
347
|
is always `01`: this library records every span, so respecting another system's sampling
|
|
332
348
|
decision would mean dropping them.
|
|
333
349
|
|
|
350
|
+
#### Clearing context in a long-lived process
|
|
351
|
+
|
|
352
|
+
`@trace` releases both baggage and the adopted context when the outermost decorated call returns
|
|
353
|
+
or raises, so **most callers never need `reset_context()`**. It exists for the two cases where no
|
|
354
|
+
root-span exit releases them in *your* context:
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
lf.reset_context() # clears baggage *and* any adopted trace context
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
- **You use the emitters without `@trace`.** An orphan log opens no span, so nothing releases
|
|
361
|
+
what `set_baggage()` or `continue_trace()` set. In a process that reuses one thread across
|
|
362
|
+
requests — the main thread, a pooled worker, a warm Lambda container — that state reaches the
|
|
363
|
+
next request. Call `reset_context()` when a unit of work ends. (An orphan log never joins an
|
|
364
|
+
adopted trace either: it mints its own `trace_id`, and the adoption simply waits to claim the
|
|
365
|
+
next root span, whenever one happens to run.)
|
|
366
|
+
- **You adopt outside the span and dispatch into a task.** The release runs in whichever
|
|
367
|
+
context the root span's `finally` runs in, so `continue_trace()` here followed by
|
|
368
|
+
`asyncio.run(main())` clears the adoption in the task's copy of the context while this one
|
|
369
|
+
keeps it. `contextvars` has no way to write back to a parent context, so clear it yourself.
|
|
370
|
+
Adopting on the entry point's first line — the documented placement — is inside the span and
|
|
371
|
+
needs nothing.
|
|
372
|
+
|
|
373
|
+
It clears rather than restores: a process-level baggage default set before any span is erased
|
|
374
|
+
too — permanently when you call it outside a span. Prefer that. Called *inside* a span it also
|
|
375
|
+
empties the `span.start` / `span.end` events of baggage, because those are stamped with the
|
|
376
|
+
span's *final* baggage at close, and that span's exit then restores the pre-span baggage anyway,
|
|
377
|
+
undoing the erasure. It never raises.
|
|
378
|
+
|
|
334
379
|
### Sinks
|
|
335
380
|
|
|
336
381
|
A **sink** is the swappable output transport — any object satisfying the `Sink` protocol. It
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Public façade (module-function shape). Exposes configuration, the ``@trace`` decorator, the
|
|
4
4
|
``debug/info/warning/error/critical`` emitters, ``set_baggage``, the cross-process propagation
|
|
5
5
|
pair (``continue_trace`` to adopt an inbound context; ``current_traceparent`` /
|
|
6
|
-
``current_trace_context`` / ``current_baggage_header`` to publish this one), ``
|
|
7
|
-
|
|
6
|
+
``current_trace_context`` / ``current_baggage_header`` to publish this one), ``reset_context``
|
|
7
|
+
(clear both for a caller who opens no span), ``flush`` (drain and keep logging) and ``shutdown``
|
|
8
|
+
(drain, close the sink, and stop).
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from importlib.metadata import PackageNotFoundError
|
|
@@ -16,6 +17,7 @@ from log_foundry.context import (
|
|
|
16
17
|
current_baggage_header,
|
|
17
18
|
current_trace_context,
|
|
18
19
|
current_traceparent,
|
|
20
|
+
reset_context,
|
|
19
21
|
)
|
|
20
22
|
from log_foundry.decorator import continue_trace, trace
|
|
21
23
|
from log_foundry.worker import Health
|
|
@@ -107,6 +109,7 @@ __all__ = [
|
|
|
107
109
|
"get_config",
|
|
108
110
|
"health",
|
|
109
111
|
"info",
|
|
112
|
+
"reset_context",
|
|
110
113
|
"set_baggage",
|
|
111
114
|
"shutdown",
|
|
112
115
|
"trace",
|
|
@@ -5,9 +5,30 @@ inherits its own copy — so ``log_foundry.info(...)`` can find "the span I'm in
|
|
|
5
5
|
manual passing. This module holds a *stack* of active spans (the top is the current span and
|
|
6
6
|
the parent of the next nested call) plus trace-scoped baggage.
|
|
7
7
|
|
|
8
|
+
**The three variables and how long each lives** (SPEC-024 — before it, the last two lived as
|
|
9
|
+
long as the context did, so one request's data reached the next request's events):
|
|
10
|
+
|
|
11
|
+
``_span_stack``
|
|
12
|
+
One entry per active decorated call. Pushed on enter, popped in the ``finally`` on exit.
|
|
13
|
+
|
|
14
|
+
``_baggage``
|
|
15
|
+
Restored to its pre-span value when the **root** span closes — the span opened when no
|
|
16
|
+
other was active. Nested spans do not reset, so baggage set three calls deep stays visible
|
|
17
|
+
to its parent and to later siblings in the same trace: that is what "trace-scoped" means.
|
|
18
|
+
Baggage set before any span is a process-level default and is restored *to*, not erased.
|
|
19
|
+
|
|
20
|
+
``_adopted``
|
|
21
|
+
Cleared when the root span closes, and consumed by that one span — an inbound context is a
|
|
22
|
+
one-shot handoff to the trace it names, not a standing setting. Restoring it instead would
|
|
23
|
+
leave a warm container joining the first caller's trace forever.
|
|
24
|
+
|
|
25
|
+
The last two are released together by :func:`pop_baggage_scope`; the span stack is released by
|
|
26
|
+
:func:`pop_span`. A caller who opens no span clears both with :func:`reset_context`.
|
|
27
|
+
|
|
8
28
|
Two footguns, both avoided below:
|
|
9
29
|
* Never mutate a ContextVar's default mutable value — the ``()`` / ``{}`` defaults are shared
|
|
10
|
-
across all contexts. Always ``.set()`` a new tuple/dict.
|
|
30
|
+
across all contexts. Always ``.set()`` a new tuple/dict. The same applies to any value a
|
|
31
|
+
*parent* context can still see: emptying a baggage dict in place reaches back into it.
|
|
11
32
|
* Use the token/``reset`` pattern, not a manual pop — ``reset(token)`` restores the exact
|
|
12
33
|
prior state even when tasks branch.
|
|
13
34
|
"""
|
|
@@ -33,6 +54,7 @@ __all__ = [
|
|
|
33
54
|
"pop_span",
|
|
34
55
|
"push_baggage_scope",
|
|
35
56
|
"push_span",
|
|
57
|
+
"reset_context",
|
|
36
58
|
"set_baggage",
|
|
37
59
|
]
|
|
38
60
|
|
|
@@ -74,12 +96,25 @@ def pop_span(token: contextvars.Token[tuple[Span, ...]]) -> None:
|
|
|
74
96
|
|
|
75
97
|
|
|
76
98
|
def get_baggage() -> dict[str, object]:
|
|
77
|
-
"""Return the current trace's baggage (do not mutate the returned dict in place).
|
|
99
|
+
"""Return the current trace's baggage (do not mutate the returned dict in place).
|
|
100
|
+
|
|
101
|
+
The trace's own keys are discarded when the enclosing **root** span closes, restoring the
|
|
102
|
+
baggage in effect before it — so a later trace sees only a process-level default set before
|
|
103
|
+
any span opened. With no span open at all nothing releases them and they accumulate for the
|
|
104
|
+
life of the context; :func:`reset_context` is the release on that path.
|
|
105
|
+
"""
|
|
78
106
|
return _baggage.get()
|
|
79
107
|
|
|
80
108
|
|
|
81
109
|
def set_baggage(**kv: object) -> None:
|
|
82
|
-
"""Merge key/values into the current trace's baggage (replaces with a new dict).
|
|
110
|
+
"""Merge key/values into the current trace's baggage (replaces with a new dict).
|
|
111
|
+
|
|
112
|
+
The keys live until the enclosing **root** span closes, then the baggage in effect before
|
|
113
|
+
that span is restored — so they ride every event at or below this point in the trace and
|
|
114
|
+
none of the next trace's. Called with no span open they become a process-level default that
|
|
115
|
+
later traces inherit and restore to; :func:`reset_context` is what erases those, and
|
|
116
|
+
``configure(defaults=...)`` is the better tool for setting them in the first place.
|
|
117
|
+
"""
|
|
83
118
|
_baggage.set({**_baggage.get(), **kv})
|
|
84
119
|
|
|
85
120
|
|
|
@@ -133,6 +168,38 @@ def pop_baggage_scope(token: contextvars.Token[dict[str, object]]) -> None:
|
|
|
133
168
|
_baggage.set(old if isinstance(old, dict) else {})
|
|
134
169
|
|
|
135
170
|
|
|
171
|
+
def reset_context() -> None:
|
|
172
|
+
"""Clear baggage and any adopted trace context outright (SPEC-024 FR-003).
|
|
173
|
+
|
|
174
|
+
For the caller who uses the emitters **without** ``@trace``: the orphan path opens no span,
|
|
175
|
+
so there is no root-span exit to hang the release on, and both values would otherwise live
|
|
176
|
+
as long as the context does. A ``@trace`` user does not need this —
|
|
177
|
+
:func:`pop_baggage_scope` already runs at every root span's exit.
|
|
178
|
+
|
|
179
|
+
It is also the remedy for the one case that scope cannot reach: an adoption made outside a
|
|
180
|
+
span whose root span then runs in a child context, where the clear lands in the copy. Call
|
|
181
|
+
this in the context that made the adoption.
|
|
182
|
+
|
|
183
|
+
One function rather than two because the two values have the same lifetime and the same
|
|
184
|
+
failure mode, and a caller who wants one almost always wants the other.
|
|
185
|
+
|
|
186
|
+
Unlike the scope release this **clears** rather than restores — a process-level baggage
|
|
187
|
+
default set before any span is erased too, which is the point of an explicit reset. Safe
|
|
188
|
+
with nothing ever set, with no span open, and inside an open span. Never raises, like every
|
|
189
|
+
other entry point on this path (arch §4).
|
|
190
|
+
|
|
191
|
+
Prefer calling it **outside** a span. Inside one it does the obvious thing to the events
|
|
192
|
+
that follow, but SPEC-015 backfills ``span.start`` and ``span.end`` from the baggage live at
|
|
193
|
+
*close*, so a mid-span reset also empties the two boundary events — which describe the whole
|
|
194
|
+
span and carry ``duration_ms``/``status`` — of baggage that was live for most of it. That
|
|
195
|
+
span's exit then restores the baggage from before the span, as it always does.
|
|
196
|
+
"""
|
|
197
|
+
# Cleared in the same order as `pop_baggage_scope`, for the reason given there: a stale
|
|
198
|
+
# trace id is wrong data, a stale baggage field is only stale.
|
|
199
|
+
_adopted.set(None)
|
|
200
|
+
_baggage.set({})
|
|
201
|
+
|
|
202
|
+
|
|
136
203
|
# -- adopted inbound context (SPEC-014) --------------------------------------------------
|
|
137
204
|
|
|
138
205
|
|
|
@@ -120,6 +120,13 @@ def continue_trace(
|
|
|
120
120
|
batch that fans out to several *sibling* root spans needs one call per item — or, better, a
|
|
121
121
|
single ``@trace`` entry point so the items are nested spans of one trace.
|
|
122
122
|
|
|
123
|
+
One constraint on that release: it happens in whichever context the root span's ``finally``
|
|
124
|
+
runs in. Adopt here and then dispatch the span into a **child** context — any
|
|
125
|
+
``asyncio.Task``, including the one ``asyncio.run`` creates — and the clear lands in the
|
|
126
|
+
copy while this context keeps the adoption. Calling this on the entry point's first line, as
|
|
127
|
+
above, is inside the span and unaffected; a caller who adopts before dispatching should call
|
|
128
|
+
:func:`~log_foundry.reset_context` when the work is done.
|
|
129
|
+
|
|
123
130
|
``baggage`` is a W3C ``baggage`` header merged into the current context. It succeeds or
|
|
124
131
|
fails independently of the trace context.
|
|
125
132
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|