log-foundry 0.10.2.dev3__tar.gz → 0.10.2.dev5__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.dev3 → log_foundry-0.10.2.dev5}/PKG-INFO +1 -1
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/__init__.py +5 -2
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/context.py +85 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/decorator.py +16 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/LICENSE +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/README.md +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/config.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.10.2.dev3 → log_foundry-0.10.2.dev5}/src/log_foundry/worker.py +0 -0
|
@@ -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",
|
|
@@ -29,8 +29,11 @@ __all__ = [
|
|
|
29
29
|
"current_trace_context",
|
|
30
30
|
"current_traceparent",
|
|
31
31
|
"get_baggage",
|
|
32
|
+
"pop_baggage_scope",
|
|
32
33
|
"pop_span",
|
|
34
|
+
"push_baggage_scope",
|
|
33
35
|
"push_span",
|
|
36
|
+
"reset_context",
|
|
34
37
|
"set_baggage",
|
|
35
38
|
]
|
|
36
39
|
|
|
@@ -81,6 +84,88 @@ def set_baggage(**kv: object) -> None:
|
|
|
81
84
|
_baggage.set({**_baggage.get(), **kv})
|
|
82
85
|
|
|
83
86
|
|
|
87
|
+
# -- the root-span scope (SPEC-024) ------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def push_baggage_scope() -> contextvars.Token[dict[str, object]]:
|
|
91
|
+
"""Open a root span's baggage scope; hand the token back to :func:`pop_baggage_scope`.
|
|
92
|
+
|
|
93
|
+
Setting the variable to its own current value is what mints the token — there is no other
|
|
94
|
+
way to say "restore whatever was here", including the case where nothing was ever set. It
|
|
95
|
+
is safe because no baggage dict is ever mutated in place (see this module's docstring).
|
|
96
|
+
"""
|
|
97
|
+
return _baggage.set(_baggage.get())
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def pop_baggage_scope(token: contextvars.Token[dict[str, object]]) -> None:
|
|
101
|
+
"""Close a root span's scope: restore baggage, discard any adopted trace context.
|
|
102
|
+
|
|
103
|
+
The two are deliberately **asymmetric**. Baggage is *restored*, so a process-level default
|
|
104
|
+
set before any span outlives the traces that run under it while a request's own keys do not
|
|
105
|
+
(SPEC-024 FR-001). The adopted context is *cleared*, because it is a one-shot handoff to the
|
|
106
|
+
trace it was adopted for: restoring it would leave the next invocation still joining the
|
|
107
|
+
previous caller's trace, which is the defect this scope exists to close (FR-002). A caller
|
|
108
|
+
who opens no span at all clears both with :func:`reset_context`.
|
|
109
|
+
|
|
110
|
+
Total — never raises, because a decorated function must not fail on the way out (arch §4).
|
|
111
|
+
|
|
112
|
+
One caveat the ``contextvars`` model makes unavoidable: both writes land in the context the
|
|
113
|
+
root span's ``finally`` runs in. An adoption made *outside* a span that then runs in a child
|
|
114
|
+
context — any ``asyncio.Task``, including the one ``asyncio.run`` creates — is cleared in
|
|
115
|
+
the copy, not in the parent that holds it. :func:`continue_trace` is documented to be called on
|
|
116
|
+
the entry point's first line, which is inside the span and unaffected; a caller who adopts
|
|
117
|
+
before dispatching across a task boundary clears it with :func:`reset_context`.
|
|
118
|
+
"""
|
|
119
|
+
# Cleared first, and deliberately ahead of anything that could fail: a stale trace id puts
|
|
120
|
+
# *wrong* data in the log stream, while a missed baggage restore only leaves a stale field.
|
|
121
|
+
_adopted.set(None)
|
|
122
|
+
try:
|
|
123
|
+
_baggage.reset(token)
|
|
124
|
+
except Exception:
|
|
125
|
+
# `reset` rejects a token minted in another context (``ValueError``, reachable when a
|
|
126
|
+
# span body hands work to another thread) and one already used (``RuntimeError``).
|
|
127
|
+
# Setting the captured value directly is equivalent: this context never had the scope
|
|
128
|
+
# pushed onto it, so there is nothing else to unwind. Broad because the alternative is
|
|
129
|
+
# raising from a `finally` and replacing the caller's own exception (arch §4).
|
|
130
|
+
# `old_value` is ``Token.MISSING`` when the variable was unset at capture, and absent
|
|
131
|
+
# entirely if this was never a Token; neither can have come from `set_baggage`, so both
|
|
132
|
+
# land on empty rather than poisoning baggage with whatever was passed.
|
|
133
|
+
old = getattr(token, "old_value", None)
|
|
134
|
+
_baggage.set(old if isinstance(old, dict) else {})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def reset_context() -> None:
|
|
138
|
+
"""Clear baggage and any adopted trace context outright (SPEC-024 FR-003).
|
|
139
|
+
|
|
140
|
+
For the caller who uses the emitters **without** ``@trace``: the orphan path opens no span,
|
|
141
|
+
so there is no root-span exit to hang the release on, and both values would otherwise live
|
|
142
|
+
as long as the context does. A ``@trace`` user does not need this —
|
|
143
|
+
:func:`pop_baggage_scope` already runs at every root span's exit.
|
|
144
|
+
|
|
145
|
+
It is also the remedy for the one case that scope cannot reach: an adoption made outside a
|
|
146
|
+
span whose root span then runs in a child context, where the clear lands in the copy. Call
|
|
147
|
+
this in the context that made the adoption.
|
|
148
|
+
|
|
149
|
+
One function rather than two because the two values have the same lifetime and the same
|
|
150
|
+
failure mode, and a caller who wants one almost always wants the other.
|
|
151
|
+
|
|
152
|
+
Unlike the scope release this **clears** rather than restores — a process-level baggage
|
|
153
|
+
default set before any span is erased too, which is the point of an explicit reset. Safe
|
|
154
|
+
with nothing ever set, with no span open, and inside an open span. Never raises, like every
|
|
155
|
+
other entry point on this path (arch §4).
|
|
156
|
+
|
|
157
|
+
Prefer calling it **outside** a span. Inside one it does the obvious thing to the events
|
|
158
|
+
that follow, but SPEC-015 backfills ``span.start`` and ``span.end`` from the baggage live at
|
|
159
|
+
*close*, so a mid-span reset also empties the two boundary events — which describe the whole
|
|
160
|
+
span and carry ``duration_ms``/``status`` — of baggage that was live for most of it. That
|
|
161
|
+
span's exit then restores the baggage from before the span, as it always does.
|
|
162
|
+
"""
|
|
163
|
+
# Cleared in the same order as `pop_baggage_scope`, for the reason given there: a stale
|
|
164
|
+
# trace id is wrong data, a stale baggage field is only stale.
|
|
165
|
+
_adopted.set(None)
|
|
166
|
+
_baggage.set({})
|
|
167
|
+
|
|
168
|
+
|
|
84
169
|
# -- adopted inbound context (SPEC-014) --------------------------------------------------
|
|
85
170
|
|
|
86
171
|
|
|
@@ -114,6 +114,12 @@ def continue_trace(
|
|
|
114
114
|
Call it on the **first line** of the entry point: a child span that already finished has
|
|
115
115
|
been handed to the worker and can no longer be rewritten.
|
|
116
116
|
|
|
117
|
+
The adoption is **consumed by that one root span** and does not survive it (SPEC-024): the
|
|
118
|
+
next root span opened with no fresh call of its own starts a new trace, which is what stops
|
|
119
|
+
a warm container from logging every later invocation into the first caller's trace. So a
|
|
120
|
+
batch that fans out to several *sibling* root spans needs one call per item — or, better, a
|
|
121
|
+
single ``@trace`` entry point so the items are nested spans of one trace.
|
|
122
|
+
|
|
117
123
|
``baggage`` is a W3C ``baggage`` header merged into the current context. It succeeds or
|
|
118
124
|
fails independently of the trace context.
|
|
119
125
|
|
|
@@ -298,8 +304,11 @@ def trace(
|
|
|
298
304
|
|
|
299
305
|
@functools.wraps(fn)
|
|
300
306
|
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
307
|
+
# Before _open_span, which is what makes the new span current (SPEC-024).
|
|
308
|
+
is_root = context.current_span() is None
|
|
301
309
|
span = _open_span(name or fn.__qualname__, defaults)
|
|
302
310
|
token = context.push_span(span)
|
|
311
|
+
scope = context.push_baggage_scope() if is_root else None
|
|
303
312
|
try:
|
|
304
313
|
result = await fn(*args, **kwargs)
|
|
305
314
|
_close_span(span, "ok", None)
|
|
@@ -312,13 +321,18 @@ def trace(
|
|
|
312
321
|
raise
|
|
313
322
|
finally:
|
|
314
323
|
context.pop_span(token)
|
|
324
|
+
if scope is not None:
|
|
325
|
+
context.pop_baggage_scope(scope)
|
|
315
326
|
|
|
316
327
|
return cast("F", async_wrapper)
|
|
317
328
|
|
|
318
329
|
@functools.wraps(fn)
|
|
319
330
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
331
|
+
# Before _open_span, which is what makes the new span current (SPEC-024).
|
|
332
|
+
is_root = context.current_span() is None
|
|
320
333
|
span = _open_span(name or fn.__qualname__, defaults)
|
|
321
334
|
token = context.push_span(span)
|
|
335
|
+
scope = context.push_baggage_scope() if is_root else None
|
|
322
336
|
try:
|
|
323
337
|
result = fn(*args, **kwargs)
|
|
324
338
|
_close_span(span, "ok", None)
|
|
@@ -329,6 +343,8 @@ def trace(
|
|
|
329
343
|
raise
|
|
330
344
|
finally:
|
|
331
345
|
context.pop_span(token)
|
|
346
|
+
if scope is not None:
|
|
347
|
+
context.pop_baggage_scope(scope)
|
|
332
348
|
|
|
333
349
|
return cast("F", wrapper)
|
|
334
350
|
|
|
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
|
|
File without changes
|