log-foundry 0.10.2.dev80__tar.gz → 0.10.2.dev82__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.dev80 → log_foundry-0.10.2.dev82}/PKG-INFO +5 -2
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/README.md +4 -1
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/_lifecycle.py +99 -60
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/config.py +12 -4
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/worker.py +1 -1
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/LICENSE +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/_diag.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/_fork.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/context.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/results.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/_retry.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/memory.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/null.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/transform.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.dev82
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -317,7 +317,10 @@ drain from ever running — the live sink never drained, and your own `atexit` h
|
|
|
317
317
|
either. The grace is what recovers the slow-close case that the daemon flag alone would lose.
|
|
318
318
|
|
|
319
319
|
`configure()` is still a startup call. It is not thread-safe, and a span finishing on another
|
|
320
|
-
thread mid-swap may land on either sink.
|
|
320
|
+
thread mid-swap may land on either sink. The **closes** are the exception: every sink you pass
|
|
321
|
+
here is closed, however many swaps are outstanding and whatever races the call (SPEC-045). That
|
|
322
|
+
is a guarantee about closes, not about buffers — an event still in flight when a `configure()`
|
|
323
|
+
lands can arrive at a sink whose close has already run, and nothing owes that sink another.
|
|
321
324
|
|
|
322
325
|
### `@trace`
|
|
323
326
|
|
|
@@ -281,7 +281,10 @@ drain from ever running — the live sink never drained, and your own `atexit` h
|
|
|
281
281
|
either. The grace is what recovers the slow-close case that the daemon flag alone would lose.
|
|
282
282
|
|
|
283
283
|
`configure()` is still a startup call. It is not thread-safe, and a span finishing on another
|
|
284
|
-
thread mid-swap may land on either sink.
|
|
284
|
+
thread mid-swap may land on either sink. The **closes** are the exception: every sink you pass
|
|
285
|
+
here is closed, however many swaps are outstanding and whatever races the call (SPEC-045). That
|
|
286
|
+
is a guarantee about closes, not about buffers — an event still in flight when a `configure()`
|
|
287
|
+
lands can arrive at a sink whose close has already run, and nothing owes that sink another.
|
|
285
288
|
|
|
286
289
|
### `@trace`
|
|
287
290
|
|
|
@@ -64,11 +64,11 @@ class _Lifecycle:
|
|
|
64
64
|
|
|
65
65
|
**The states.** A process is in exactly one of these, and the field that decides is named:
|
|
66
66
|
|
|
67
|
-
- **cold** — ``_worker is None`` and ``
|
|
67
|
+
- **cold** — ``_worker is None`` and ``_orphan_owed`` is empty. Nothing has been logged, no
|
|
68
68
|
thread exists, no sink is owed a close. ``configure()`` alone does not leave this state:
|
|
69
69
|
it runs ``_ensure_sink()`` unconditionally, so a resolved sink is not evidence anything
|
|
70
70
|
was written to it (SPEC-031 FR-006).
|
|
71
|
-
- **orphan-only** — ``
|
|
71
|
+
- **orphan-only** — ``_orphan_owed`` names every sink a level call with no span actually
|
|
72
72
|
reached, and ``_worker`` is still ``None``. The close is owed to this path, and the
|
|
73
73
|
``atexit`` handler is armed by the emit that landed rather than by the sink existing.
|
|
74
74
|
- **worker-backed** — ``_worker`` holds the process worker. It owns the drain, the sink's
|
|
@@ -149,13 +149,35 @@ class _Lifecycle:
|
|
|
149
149
|
self._worker: Worker | None = None
|
|
150
150
|
self._lock = threading.Lock()
|
|
151
151
|
self._atexit_registered = False
|
|
152
|
-
self.
|
|
152
|
+
self._orphan_owed: dict[int, Sink] = {}
|
|
153
153
|
self._orphan_closed_sink: Sink | None = None
|
|
154
154
|
self._orphan_stop = threading.Event()
|
|
155
155
|
self._orphan_retired = False
|
|
156
156
|
self._shutdown_running = 0
|
|
157
157
|
self._late_worker: Worker | None = None
|
|
158
158
|
|
|
159
|
+
def take_orphan_owed(self) -> list[Sink]:
|
|
160
|
+
"""Empties the owed-close record and returns what it held, in arming order.
|
|
161
|
+
|
|
162
|
+
A **transition**, not one of the four questions (arch §9.2): every caller that clears the
|
|
163
|
+
record is deciding who performs the closes it was holding, and reading-and-clearing in
|
|
164
|
+
one step is what stops two of them deciding the same sink is theirs.
|
|
165
|
+
|
|
166
|
+
Callers hold ``_lock``.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
None.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The sinks that were owed a close, oldest first.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
None.
|
|
176
|
+
"""
|
|
177
|
+
owed = list(self._orphan_owed.values())
|
|
178
|
+
self._orphan_owed.clear()
|
|
179
|
+
return owed
|
|
180
|
+
|
|
159
181
|
def worker_exists(self) -> Worker | None:
|
|
160
182
|
"""Existence — is there a worker at all, and therefore anything to do (arch §9.2).
|
|
161
183
|
|
|
@@ -676,7 +698,7 @@ def _inheritance_roots() -> list[object]:
|
|
|
676
698
|
candidates = (
|
|
677
699
|
config._live_config().sink,
|
|
678
700
|
None if worker is None else worker.sink,
|
|
679
|
-
_state.
|
|
701
|
+
*_state._orphan_owed.values(),
|
|
680
702
|
_state._orphan_closed_sink,
|
|
681
703
|
)
|
|
682
704
|
roots: list[object] = [found for found in candidates if found is not None]
|
|
@@ -1180,11 +1202,11 @@ def _get_worker() -> Worker:
|
|
|
1180
1202
|
_state._worker = worker
|
|
1181
1203
|
if _state._shutdown_running > 0:
|
|
1182
1204
|
_state._late_worker = worker
|
|
1183
|
-
owed = _state.
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1205
|
+
owed = _state.take_orphan_owed()
|
|
1206
|
+
for stale in owed:
|
|
1207
|
+
if stale is not worker.sink:
|
|
1208
|
+
_state._orphan_closed_sink = stale
|
|
1209
|
+
release(stale, detached=True)
|
|
1188
1210
|
return worker
|
|
1189
1211
|
def _register_exit_handler() -> None:
|
|
1190
1212
|
"""Registers the one ``atexit`` handler that covers both delivery paths (SPEC-031 FR-006).
|
|
@@ -1241,14 +1263,16 @@ def _note_orphan_emit(sink: Sink) -> None:
|
|
|
1241
1263
|
Raises:
|
|
1242
1264
|
None.
|
|
1243
1265
|
"""
|
|
1244
|
-
if (
|
|
1266
|
+
if (
|
|
1267
|
+
id(sink) in _state._orphan_owed or sink is _state._orphan_closed_sink
|
|
1268
|
+
) and not _state._orphan_stop.is_set():
|
|
1245
1269
|
return
|
|
1246
1270
|
with _state._lock:
|
|
1247
1271
|
_offer_orphan_signal(sink)
|
|
1248
|
-
if sink
|
|
1272
|
+
if id(sink) in _state._orphan_owed or sink is _state._orphan_closed_sink:
|
|
1249
1273
|
return
|
|
1250
1274
|
_register_exit_handler()
|
|
1251
|
-
_state.
|
|
1275
|
+
_state._orphan_owed[id(sink)] = sink
|
|
1252
1276
|
def _rebuild_worker_after_fork() -> None:
|
|
1253
1277
|
"""Gives a forked child a drain thread of its own, or a retired worker (SPEC-039 FR-002).
|
|
1254
1278
|
|
|
@@ -1382,17 +1406,17 @@ def _close_orphan_sink() -> None:
|
|
|
1382
1406
|
itself. ``Exception``, never ``BaseException`` (SPEC-025 FR-004).
|
|
1383
1407
|
"""
|
|
1384
1408
|
with _state._lock:
|
|
1385
|
-
owed =
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1409
|
+
owed: list[Sink] = [
|
|
1410
|
+
sink for sink in _state._orphan_owed.values() if not _state.worker_owns(sink)
|
|
1411
|
+
]
|
|
1412
|
+
for sink in owed:
|
|
1413
|
+
del _state._orphan_owed[id(sink)]
|
|
1414
|
+
_state._orphan_closed_sink = sink
|
|
1415
|
+
for sink in owed:
|
|
1416
|
+
try:
|
|
1417
|
+
release(sink)
|
|
1418
|
+
except Exception as exc:
|
|
1419
|
+
_diag.absorbed("closing the sink", exc, "it may still hold its resources")
|
|
1396
1420
|
def _shutdown_worker(timeout: float | None = DEFAULT_SHUTDOWN_TIMEOUT) -> None:
|
|
1397
1421
|
"""Drains and closes the process worker, or closes an orphan-only sink, backing ``shutdown()``.
|
|
1398
1422
|
|
|
@@ -1525,7 +1549,7 @@ def _swap_sink(new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) ->
|
|
|
1525
1549
|
reports nothing at all, by the decision that made the bounded close available.
|
|
1526
1550
|
|
|
1527
1551
|
**The worker branch latches what it hands over, and decides the close before clearing**
|
|
1528
|
-
(SPEC-044 FR-004, FR-002). It used to clear
|
|
1552
|
+
(SPEC-044 FR-004, FR-002). It used to clear the orphan record and record nothing: an
|
|
1529
1553
|
orphan emit that resolved the old sink before the swap and resumed after it then re-armed a
|
|
1530
1554
|
sink :meth:`Worker.swap_sink` had already closed, and the exit close performed a second
|
|
1531
1555
|
``close()`` on it — measured ``A.closed == 2`` with a preemption point injected at
|
|
@@ -1563,27 +1587,33 @@ def _swap_sink(new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) ->
|
|
|
1563
1587
|
rejected ceiling, and a sink swap that fails must not become the reason an application
|
|
1564
1588
|
cannot start.
|
|
1565
1589
|
"""
|
|
1566
|
-
|
|
1590
|
+
closers: list[threading.Thread] = []
|
|
1567
1591
|
deadline = None if timeout is None else monotonic() + timeout
|
|
1568
1592
|
with _state._lock:
|
|
1569
1593
|
worker = _state.live_worker()
|
|
1570
1594
|
if worker is not None:
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1595
|
+
for stale in _state.take_orphan_owed():
|
|
1596
|
+
if stale is not new_sink and stale is not worker.sink:
|
|
1597
|
+
_state._orphan_closed_sink = stale
|
|
1598
|
+
closer = release(stale, detached=True)
|
|
1599
|
+
if closer is not None:
|
|
1600
|
+
closers.append(closer)
|
|
1576
1601
|
if worker.sink is not new_sink:
|
|
1577
1602
|
_state._orphan_closed_sink = worker.sink
|
|
1578
1603
|
else:
|
|
1579
|
-
|
|
1580
|
-
if old is None or old is new_sink:
|
|
1604
|
+
if not _state._orphan_owed:
|
|
1581
1605
|
return
|
|
1582
|
-
|
|
1606
|
+
superseded = [
|
|
1607
|
+
sink for sink in _state.take_orphan_owed() if sink is not new_sink
|
|
1608
|
+
]
|
|
1609
|
+
_state._orphan_owed[id(new_sink)] = new_sink
|
|
1583
1610
|
_offer_orphan_signal(new_sink)
|
|
1584
|
-
|
|
1585
|
-
_state.
|
|
1586
|
-
|
|
1611
|
+
for stale in superseded:
|
|
1612
|
+
if not _state.worker_owns(stale):
|
|
1613
|
+
_state._orphan_closed_sink = stale
|
|
1614
|
+
closer = release(stale, detached=True)
|
|
1615
|
+
if closer is not None:
|
|
1616
|
+
closers.append(closer)
|
|
1587
1617
|
if worker is not None:
|
|
1588
1618
|
try:
|
|
1589
1619
|
worker_holds_sink = worker.swap_sink(new_sink, timeout)
|
|
@@ -1594,13 +1624,13 @@ def _swap_sink(new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) ->
|
|
|
1594
1624
|
else:
|
|
1595
1625
|
if not worker_holds_sink:
|
|
1596
1626
|
_adopt_declined_swap(new_sink)
|
|
1597
|
-
|
|
1627
|
+
for closer in closers:
|
|
1598
1628
|
closer.join(None if deadline is None else max(0.0, deadline - monotonic()))
|
|
1599
1629
|
def _adopt_declined_swap(new_sink: Sink) -> None:
|
|
1600
1630
|
"""Takes ownership of a sink a worker refused mid-swap (SPEC-035 FR-003).
|
|
1601
1631
|
|
|
1602
1632
|
``Worker.swap_sink`` re-checks retirement after its first ``flush()`` and returns early once
|
|
1603
|
-
``_shutdown_done`` latched, but :func:`_swap_sink` had already
|
|
1633
|
+
``_shutdown_done`` latched, but :func:`_swap_sink` had already taken the orphan record on the
|
|
1604
1634
|
strength of a worker being live a few instructions earlier. The new sink then sat in the
|
|
1605
1635
|
config, installed nowhere and recorded nowhere: measured, ``config.sink is B`` was ``True``,
|
|
1606
1636
|
``B`` was never closed, and ``health()`` read entirely clean — for a sink whose ``close()``
|
|
@@ -1616,7 +1646,7 @@ def _adopt_declined_swap(new_sink: Sink) -> None:
|
|
|
1616
1646
|
|
|
1617
1647
|
**A sink already recorded as closed is refused re-arming**, the guard
|
|
1618
1648
|
:func:`_note_orphan_emit` carries and for its reason. Without it, an orphan log arming
|
|
1619
|
-
``_state.
|
|
1649
|
+
``_state._orphan_owed`` while this thread is inside ``swap_sink``'s first drain, followed by a
|
|
1620
1650
|
``shutdown()`` that closes it, lets this re-arm a closed sink for a second ``close()`` at
|
|
1621
1651
|
exit — reproduced. ``sinks/base.py`` asks an implementation to make its release idempotent,
|
|
1622
1652
|
but the library does not rely on it, for the reason :func:`_swap_sink` states.
|
|
@@ -1636,10 +1666,10 @@ def _adopt_declined_swap(new_sink: Sink) -> None:
|
|
|
1636
1666
|
"""
|
|
1637
1667
|
with _state._lock:
|
|
1638
1668
|
_offer_orphan_signal(new_sink)
|
|
1639
|
-
if new_sink
|
|
1669
|
+
if id(new_sink) in _state._orphan_owed or new_sink is _state._orphan_closed_sink:
|
|
1640
1670
|
return
|
|
1641
1671
|
_register_exit_handler()
|
|
1642
|
-
_state.
|
|
1672
|
+
_state._orphan_owed[id(new_sink)] = new_sink
|
|
1643
1673
|
def _flush_live_sink() -> bool:
|
|
1644
1674
|
"""Drains whatever the delivering sink holds in its own client (SPEC-036 FR-002).
|
|
1645
1675
|
|
|
@@ -1669,15 +1699,21 @@ def _flush_live_sink() -> bool:
|
|
|
1669
1699
|
from log_foundry.sinks.base import flush_sink
|
|
1670
1700
|
|
|
1671
1701
|
worker = _state.live_worker()
|
|
1672
|
-
|
|
1673
|
-
|
|
1702
|
+
if worker is not None:
|
|
1703
|
+
pending = [worker.sink]
|
|
1704
|
+
else:
|
|
1705
|
+
with _state._lock:
|
|
1706
|
+
pending = list(_state._orphan_owed.values())
|
|
1707
|
+
if not pending:
|
|
1674
1708
|
return True
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1709
|
+
drained = True
|
|
1710
|
+
for sink in pending:
|
|
1711
|
+
try:
|
|
1712
|
+
flush_sink(sink)
|
|
1713
|
+
except Exception as exc:
|
|
1714
|
+
_diag.absorbed("flushing the sink's own buffer", exc, "its client still holds events")
|
|
1715
|
+
drained = False
|
|
1716
|
+
return drained
|
|
1681
1717
|
def _flush_worker(timeout: float | None = 5.0) -> FlushResult:
|
|
1682
1718
|
"""Drains the process worker without retiring it, backing ``flush()`` (SPEC-013 FR-003).
|
|
1683
1719
|
|
|
@@ -1745,16 +1781,18 @@ def _delivering_to_an_inherited_sink() -> bool:
|
|
|
1745
1781
|
and for the same reason: standing up a thread to answer ``health()`` is forbidden.
|
|
1746
1782
|
|
|
1747
1783
|
The three candidates are asked in delivery order: the worker's sink if a worker exists,
|
|
1748
|
-
else the sink an orphan emit reached, else the configured one. SPEC-033's
|
|
1749
|
-
disagreement is worker-versus-config, which the **first** term already covers
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1784
|
+
else the sink an orphan emit reached most recently, else the configured one. SPEC-033's
|
|
1785
|
+
measured disagreement is worker-versus-config, which the **first** term already covers.
|
|
1786
|
+
|
|
1787
|
+
The middle term takes the **last** entry of ``_state._orphan_owed``, and that is continuity
|
|
1788
|
+
with the single slot it replaced rather than a claim that the last entry is the sink being
|
|
1789
|
+
delivered to. It is not: ``_swap_sink`` inserts the new sink into a freshly emptied record and
|
|
1790
|
+
a preempted emit then appends the *superseded* one, so in this spec's own primary scenario the
|
|
1791
|
+
order is ``[live, superseded]``. Neither end of the record is authoritative for "installed" —
|
|
1792
|
+
arming order is emit order, which is a different question — and the config is. The answer is
|
|
1793
|
+
therefore unchanged from before SPEC-045 and can still name a superseded sink; that limit is
|
|
1794
|
+
recorded in ``architecture.md`` §13 rather than quietly fixed here, because correcting it
|
|
1795
|
+
changes a documented ``Health`` field on a path this spec does not otherwise touch.
|
|
1758
1796
|
|
|
1759
1797
|
With no sink resolved at all there is nothing installed and nothing inherited, so the answer
|
|
1760
1798
|
is ``False`` rather than a guess.
|
|
@@ -1774,7 +1812,8 @@ def _delivering_to_an_inherited_sink() -> bool:
|
|
|
1774
1812
|
|
|
1775
1813
|
try:
|
|
1776
1814
|
worker = _state.worker_exists()
|
|
1777
|
-
|
|
1815
|
+
owed = next(reversed(_state._orphan_owed.values()), None)
|
|
1816
|
+
sink = worker.sink if worker is not None else (owed or _live_config().sink)
|
|
1778
1817
|
return sink is not None and not releasable(sink)
|
|
1779
1818
|
except Exception:
|
|
1780
1819
|
return False
|
|
@@ -110,8 +110,14 @@ def configure(
|
|
|
110
110
|
(arch §7). It **swaps the live delivery target**: the previous sink is closed and subsequent
|
|
111
111
|
events go to the new one, so "repeated calls compose rather than reset" holds for the sink
|
|
112
112
|
too, at the cost of one bounded wait. Passing the sink that is already live is a no-op: no
|
|
113
|
-
drain, no close.
|
|
114
|
-
|
|
113
|
+
drain, no close.
|
|
114
|
+
|
|
115
|
+
**Every sink this has been given is closed** (SPEC-045), whatever races the call and however
|
|
116
|
+
many are outstanding. Handing the previous sink back to a later call is therefore supported
|
|
117
|
+
rather than forbidden: it is closed again, because it took events again, and what it took
|
|
118
|
+
after the hand-back is flushed by that second close. The record of which sinks are owed a
|
|
119
|
+
close used to be a single slot, so an ordinary ``info()`` on another thread could leave the
|
|
120
|
+
**live** sink closed by nobody and its buffer undelivered.
|
|
115
121
|
|
|
116
122
|
**What the swap promises differs by delivery path**, because the two have different work to
|
|
117
123
|
do. With a background worker — anything using ``@trace`` — everything submitted so far is
|
|
@@ -129,8 +135,10 @@ def configure(
|
|
|
129
135
|
on in the background. An expired join reports nothing, since a slow close and a stuck one
|
|
130
136
|
cannot be told apart at that moment; ``health().closing_sinks`` reports the live fact instead.
|
|
131
137
|
|
|
132
|
-
This is still a startup call
|
|
133
|
-
during a swap may land on either sink
|
|
138
|
+
This is still a startup call, and it is **not thread-safe**. A span finishing on another
|
|
139
|
+
thread during a swap may land on either sink, and two threads calling this at once can leave
|
|
140
|
+
the config naming one sink while delivery goes to another. What is unconditional is that
|
|
141
|
+
every sink involved is closed; where its events went is not.
|
|
134
142
|
|
|
135
143
|
Args:
|
|
136
144
|
service: The service name stamped onto every event.
|
|
@@ -1020,7 +1020,7 @@ class Worker:
|
|
|
1020
1020
|
after ``_run`` has returned, so the sink is provably out of use *by the worker*.
|
|
1021
1021
|
|
|
1022
1022
|
**It records nothing in the closed-sink latch, and does not need to** (SPEC-044 FR-004):
|
|
1023
|
-
``_lifecycle.
|
|
1023
|
+
``_lifecycle._orphan_owed`` still names this sink where anything named it, and
|
|
1024
1024
|
``worker_owns`` answers ``True``, so ``_close_orphan_sink`` declines rather than re-arming
|
|
1025
1025
|
it. The latch exists for a sink this worker has *stopped* holding.
|
|
1026
1026
|
|
|
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
|
{log_foundry-0.10.2.dev80 → log_foundry-0.10.2.dev82}/src/log_foundry/sinks/elasticsearch.py
RENAMED
|
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
|