log-foundry 0.10.2.dev113__tar.gz → 0.10.2.dev115__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.dev113 → log_foundry-0.10.2.dev115}/PKG-INFO +2 -2
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/README.md +1 -1
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/results.py +5 -2
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/worker.py +153 -15
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/LICENSE +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_diag.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_fork.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_lifecycle.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/config.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/context.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_retry.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/memory.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/null.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/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.dev115
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -1208,7 +1208,7 @@ Falsy carries a `reason` saying which of these happened, because they need diffe
|
|
|
1208
1208
|
| `"retired"` | `shutdown()` was already called. Your lifecycle is wrong, not the sink. |
|
|
1209
1209
|
| `"thread-died"` | The drain thread is gone; see `health().stopped_reason`. |
|
|
1210
1210
|
| `"queue-full"` | Backpressure — the queue could not even accept the marker. |
|
|
1211
|
-
| `"abandoned"` |
|
|
1211
|
+
| `"abandoned"` | The drain that would have carried those events did not confirm delivery of them. A broken destination is one cause, but not the only one — an expiring bounded `shutdown()` answers waiting calls pessimistically, and the drain may still deliver afterwards. So this is not by itself evidence that anything was lost: `health()` is what says whether it was. |
|
|
1212
1212
|
| `"sink-flush"` | Everything queued reached the sink, but the sink's own `flush()` raised — a client-side buffer that did not go out. Distinct from `"abandoned"`: the library delivered, the sink did not. |
|
|
1213
1213
|
|
|
1214
1214
|
```python
|
|
@@ -1157,7 +1157,7 @@ Falsy carries a `reason` saying which of these happened, because they need diffe
|
|
|
1157
1157
|
| `"retired"` | `shutdown()` was already called. Your lifecycle is wrong, not the sink. |
|
|
1158
1158
|
| `"thread-died"` | The drain thread is gone; see `health().stopped_reason`. |
|
|
1159
1159
|
| `"queue-full"` | Backpressure — the queue could not even accept the marker. |
|
|
1160
|
-
| `"abandoned"` |
|
|
1160
|
+
| `"abandoned"` | The drain that would have carried those events did not confirm delivery of them. A broken destination is one cause, but not the only one — an expiring bounded `shutdown()` answers waiting calls pessimistically, and the drain may still deliver afterwards. So this is not by itself evidence that anything was lost: `health()` is what says whether it was. |
|
|
1161
1161
|
| `"sink-flush"` | Everything queued reached the sink, but the sink's own `flush()` raised — a client-side buffer that did not go out. Distinct from `"abandoned"`: the library delivered, the sink did not. |
|
|
1162
1162
|
|
|
1163
1163
|
```python
|
|
@@ -74,7 +74,7 @@ keywords = [
|
|
|
74
74
|
# vulnerability-reporting channel. The repository is still named `log-forge` — the ORIGINAL name,
|
|
75
75
|
# which PyPI rejected for the distribution — so these URLs deliberately do not match the package
|
|
76
76
|
# name. See the note on `name` above before "correcting" them.
|
|
77
|
-
version = "0.10.2.
|
|
77
|
+
version = "0.10.2.dev115"
|
|
78
78
|
|
|
79
79
|
[project.urls]
|
|
80
80
|
Homepage = "https://github.com/agriffi10/log-forge"
|
|
@@ -55,8 +55,11 @@ class FlushResult(_Result):
|
|
|
55
55
|
``"sink-flush"`` is the one SPEC-036 added (FR-002 AC-8) and it is worth distinguishing: the
|
|
56
56
|
queue drained cleanly and the **sink's own client buffer** did not, so the events are past
|
|
57
57
|
this library and inside a driver. ``"abandoned"`` is the neighbouring case where this call
|
|
58
|
-
could not
|
|
59
|
-
|
|
58
|
+
could not confirm they were handed over. It has **four** producers — a drain that spent its
|
|
59
|
+
retries, an expiring ``shutdown()`` answering pessimistically, a marker that arrived after
|
|
60
|
+
that, and one that never reached the queue at all because it was full — so it means "not
|
|
61
|
+
confirmed delivered" and never "confirmed lost" (SPEC-050 FR-001). ``ok=True`` carries the guarantee instead: the sink took them. New tokens may appear
|
|
62
|
+
in any release, which is what this type exists for — branch on ``bool()``.
|
|
60
63
|
"""
|
|
61
64
|
|
|
62
65
|
|
|
@@ -29,6 +29,13 @@ SPEC-034 froze — keeps naming the same object.
|
|
|
29
29
|
|
|
30
30
|
_DROP_WARN_EVERY = 1000
|
|
31
31
|
|
|
32
|
+
_PUT_POLL_SECONDS = 0.05
|
|
33
|
+
"""How long a full-queue ``flush()`` waits before re-asking whether the drain was abandoned.
|
|
34
|
+
|
|
35
|
+
Small enough that an unbounded caller notices promptly, large enough that a full queue does not
|
|
36
|
+
become a spin. It bounds only the *slice*, never the caller's own deadline (SPEC-050 FR-001).
|
|
37
|
+
"""
|
|
38
|
+
|
|
32
39
|
DEFAULT_SWAP_TIMEOUT = _lifecycle.DEFAULT_SWAP_TIMEOUT
|
|
33
40
|
"""Re-exported from ``_lifecycle``, which owns it (SPEC-040 FR-001).
|
|
34
41
|
|
|
@@ -755,6 +762,30 @@ class Worker:
|
|
|
755
762
|
``put`` and the sweep's snapshot both take the queue's own mutex, so a marker either
|
|
756
763
|
lands before the snapshot and is answered, or lands after it and finds the flag set.
|
|
757
764
|
|
|
765
|
+
**``_drain_settled`` is the third flag, and an expired ``shutdown()`` sets only that
|
|
766
|
+
one** (SPEC-050 FR-001). On that path the drain is still alive and still inside ``emit``,
|
|
767
|
+
so ``_drain_finished`` is clear and ``is_alive()`` is true — a marker landing after that
|
|
768
|
+
sweep found every existing condition false and waited on a drain the process had already
|
|
769
|
+
given up on. Measured: the flusher still waiting three seconds later, released only by
|
|
770
|
+
re-running the sweep by hand. ``timeout=None`` makes it permanent, and in an application
|
|
771
|
+
that is a non-daemon thread the interpreter joins at exit, so the process does not exit.
|
|
772
|
+
``_drain_settled`` set with ``_drain_finished`` clear is *uniquely* that branch — every
|
|
773
|
+
other setter sets both, and :meth:`_run` sets ``_drain_finished`` **first**, so a true
|
|
774
|
+
read of settled implies finished was already set — which is what lets this report
|
|
775
|
+
``"abandoned"`` rather than ``"thread-died"`` for a thread that is demonstrably alive.
|
|
776
|
+
Inverting that pair in :meth:`_run` survives the whole suite and would produce the
|
|
777
|
+
mislabel; it is held by :meth:`_run`'s docstring naming the order rather than by a test,
|
|
778
|
+
because the consequence is a wrong *reason* on two falsy results and not a wrong verdict.
|
|
779
|
+
|
|
780
|
+
**The delivered test comes first, and a test holds it there.** A marker the drain answered
|
|
781
|
+
``delivered=True`` between the sweep and ``_drain_finished`` being set must report
|
|
782
|
+
``ok=True``, not ``"abandoned"`` — the drain adjudicated it, and a false negative here is
|
|
783
|
+
what makes :meth:`swap_sink` count an ``incomplete_swaps`` and write a loss line for a swap
|
|
784
|
+
that completed. A wrong verdict, not a wrong reason. An earlier draft of this docstring
|
|
785
|
+
called the window unreachable without interposing inside this method; that was wrong twice
|
|
786
|
+
over — parking after the put reaches it, and holding ``_release_marker`` widens it — and a
|
|
787
|
+
reviewer built both.
|
|
788
|
+
|
|
758
789
|
Reporting is by the **marker**, never by the check alone. A drain that answered this
|
|
759
790
|
marker and then exited has delivered, and saying otherwise would be a false failure —
|
|
760
791
|
one ``swap_sink`` reads as an unconfirmed drain, counting ``incomplete_swaps``, leaving
|
|
@@ -767,9 +798,17 @@ class Worker:
|
|
|
767
798
|
Returns:
|
|
768
799
|
A :class:`FlushResult`, truthy once the worker has delivered them and otherwise
|
|
769
800
|
falsy with a ``reason``: ``"timed-out"``, ``"retired"``, ``"thread-died"``,
|
|
770
|
-
``"queue-full"``, or ``"abandoned"
|
|
771
|
-
after exhausting retries (SPEC-021 FR-001
|
|
772
|
-
false success exactly where ``flush()``
|
|
801
|
+
``"queue-full"``, or ``"abandoned"``. That last one has **four** producers, not one:
|
|
802
|
+
the drain carrying those events gave up after exhausting retries (SPEC-021 FR-001,
|
|
803
|
+
the original — and it used to return True, a false success exactly where ``flush()``
|
|
804
|
+
matters most); an expiring ``shutdown()`` answered the marker pessimistically rather
|
|
805
|
+
than leave it on a drain it had abandoned (SPEC-050 FR-001); this call's own marker
|
|
806
|
+
arrived after that had already happened; or it never reached the queue, because that
|
|
807
|
+
was full and the drain had already been given up on. Only the first says the drain
|
|
808
|
+
adjudicated the batch, so ``"abandoned"`` means "not confirmed delivered", never
|
|
809
|
+
"confirmed lost". ``ok=True`` is unaffected and still means the sink took them:
|
|
810
|
+
``delivered`` starts ``False`` and is written only by the drain, only after its emit
|
|
811
|
+
returned. **The inner call carries the
|
|
773
812
|
type too, not only the public ``log_foundry.flush``** (SPEC-034 FR-007 AC-1b): the
|
|
774
813
|
five outcomes are distinguishable only here, so a public wrapper over a bare ``bool``
|
|
775
814
|
could name none of them without guessing.
|
|
@@ -785,16 +824,20 @@ class Worker:
|
|
|
785
824
|
with self._lock:
|
|
786
825
|
marker = _FlushMarker(self.failed_batches)
|
|
787
826
|
deadline = None if timeout is None else time.monotonic() + timeout
|
|
788
|
-
|
|
789
|
-
self.
|
|
790
|
-
|
|
827
|
+
if not self._put_marker(marker, deadline):
|
|
828
|
+
if self._given_up():
|
|
829
|
+
return FlushResult(ok=False, reason="abandoned")
|
|
830
|
+
if not self._thread.is_alive() or self._drain_finished.is_set():
|
|
831
|
+
return FlushResult(ok=False, reason="thread-died")
|
|
791
832
|
return FlushResult(ok=False, reason="queue-full")
|
|
792
|
-
|
|
833
|
+
given_up = self._given_up()
|
|
834
|
+
if self._settled():
|
|
793
835
|
answered = marker.event.is_set()
|
|
794
|
-
|
|
795
|
-
if delivered:
|
|
836
|
+
if answered and marker.delivered:
|
|
796
837
|
return FlushResult(ok=True)
|
|
797
|
-
|
|
838
|
+
if answered or given_up:
|
|
839
|
+
return FlushResult(ok=False, reason="abandoned")
|
|
840
|
+
return FlushResult(ok=False, reason="thread-died")
|
|
798
841
|
remaining = None if deadline is None else max(0.0, deadline - time.monotonic())
|
|
799
842
|
if not marker.event.wait(remaining):
|
|
800
843
|
return FlushResult(ok=False, reason="timed-out")
|
|
@@ -802,6 +845,92 @@ class Worker:
|
|
|
802
845
|
return FlushResult(ok=False, reason="abandoned")
|
|
803
846
|
return FlushResult(ok=True)
|
|
804
847
|
|
|
848
|
+
def _given_up(self) -> bool:
|
|
849
|
+
"""Whether a bounded ``shutdown()`` expired on a drain that is still running.
|
|
850
|
+
|
|
851
|
+
``_drain_settled`` set with ``_drain_finished`` clear is **uniquely** that branch: every
|
|
852
|
+
other setter sets both, and :meth:`_run` sets ``_drain_finished`` first, so a true read of
|
|
853
|
+
settled implies finished was already set. That ordering is what makes this predicate
|
|
854
|
+
precise rather than merely suggestive, and :meth:`_run` names it in turn.
|
|
855
|
+
|
|
856
|
+
Args:
|
|
857
|
+
None.
|
|
858
|
+
|
|
859
|
+
Returns:
|
|
860
|
+
Whether the drain has been abandoned while still alive.
|
|
861
|
+
|
|
862
|
+
Raises:
|
|
863
|
+
None.
|
|
864
|
+
"""
|
|
865
|
+
return self._drain_settled.is_set() and not self._drain_finished.is_set()
|
|
866
|
+
|
|
867
|
+
def _settled(self) -> bool:
|
|
868
|
+
"""Whether nothing will drain this queue again, for any of the three reasons.
|
|
869
|
+
|
|
870
|
+
The disjunction :meth:`flush`'s post-put re-check tests, named once so the pre-put wait
|
|
871
|
+
and the post-put check cannot drift apart: the drain finished, a bounded ``shutdown()``
|
|
872
|
+
gave up on it, or the thread is gone. :meth:`_given_up` is the narrower question of *which*
|
|
873
|
+
of those it was, which only the reason mapping needs.
|
|
874
|
+
|
|
875
|
+
Args:
|
|
876
|
+
None.
|
|
877
|
+
|
|
878
|
+
Returns:
|
|
879
|
+
Whether the drain has stopped, been abandoned, or died.
|
|
880
|
+
|
|
881
|
+
Raises:
|
|
882
|
+
None.
|
|
883
|
+
"""
|
|
884
|
+
return self._drain_finished.is_set() or self._given_up() or not self._thread.is_alive()
|
|
885
|
+
|
|
886
|
+
def _put_marker(self, marker: _FlushMarker, deadline: float | None) -> bool:
|
|
887
|
+
"""Queues a flush marker, giving up if the drain is abandoned while the queue is full.
|
|
888
|
+
|
|
889
|
+
A plain ``put`` with ``timeout=None`` blocks until the queue has room, and on a full queue
|
|
890
|
+
behind a permanently wedged sink there is never any — so the caller waited forever one
|
|
891
|
+
line *before* the post-put re-check that exists to prevent exactly that. Measured at
|
|
892
|
+
process level: a non-daemon flusher parked in ``Queue.put`` for the whole run, and an
|
|
893
|
+
interpreter that could not exit because it joins that thread. The re-check cannot help,
|
|
894
|
+
because ``_release_waiters`` can only answer a marker that is already in the queue.
|
|
895
|
+
|
|
896
|
+
So the wait is taken in slices and :meth:`_settled` is consulted between them — the
|
|
897
|
+
re-check's own disjunction, not merely :meth:`_given_up`, because a *terminally dead*
|
|
898
|
+
drain sets both flags and would leave an unbounded caller parked forever on the narrower
|
|
899
|
+
test. The deadline is consulted **after** a put has been attempted, never before: a
|
|
900
|
+
``flush(timeout=0)`` is "enqueue and do not wait", and testing the deadline first turned
|
|
901
|
+
it into a call that never enqueued at all and reported backpressure that did not exist —
|
|
902
|
+
which is the one outcome SPEC-034 FR-007 named ``reason`` to tell apart. A bounded caller
|
|
903
|
+
is otherwise unaffected: the deadline still ends it, with the same ``"queue-full"``.
|
|
904
|
+
The slice is a polling granularity on a queue that is *already* full, which is a degraded
|
|
905
|
+
state the caller is being told about either way, and it bounds only the slice — a negative
|
|
906
|
+
or zero remainder clamps to an immediate attempt rather than raising, where ``Queue.put``
|
|
907
|
+
rejects a negative timeout before it even looks at capacity. That makes the **put** total,
|
|
908
|
+
which is what ``Raises: None`` said all along; the *wait* below is unchanged and a timeout
|
|
909
|
+
large enough to overflow ``time_t`` still raises out of it, on this tree and on every
|
|
910
|
+
earlier one.
|
|
911
|
+
|
|
912
|
+
Args:
|
|
913
|
+
marker: The marker to enqueue.
|
|
914
|
+
deadline: The caller's monotonic deadline, or ``None`` for an unbounded caller.
|
|
915
|
+
|
|
916
|
+
Returns:
|
|
917
|
+
Whether the marker reached the queue.
|
|
918
|
+
|
|
919
|
+
Raises:
|
|
920
|
+
None.
|
|
921
|
+
"""
|
|
922
|
+
while True:
|
|
923
|
+
slice_seconds = _PUT_POLL_SECONDS
|
|
924
|
+
if deadline is not None:
|
|
925
|
+
slice_seconds = min(slice_seconds, max(0.0, deadline - time.monotonic()))
|
|
926
|
+
try:
|
|
927
|
+
self._queue.put(marker, timeout=slice_seconds)
|
|
928
|
+
except queue.Full:
|
|
929
|
+
if self._settled() or (deadline is not None and time.monotonic() >= deadline):
|
|
930
|
+
return False
|
|
931
|
+
else:
|
|
932
|
+
return True
|
|
933
|
+
|
|
805
934
|
def swap_sink(self, new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) -> bool:
|
|
806
935
|
"""Retargets delivery at a new sink, draining and closing the previous one (FR-003).
|
|
807
936
|
|
|
@@ -1321,7 +1450,13 @@ class Worker:
|
|
|
1321
1450
|
would be a worse failure than the one this prevents.
|
|
1322
1451
|
|
|
1323
1452
|
The two ``finally`` blocks are nested rather than merged, and the order they impose is
|
|
1324
|
-
load-bearing
|
|
1453
|
+
load-bearing three times over. ``_drain_finished`` is set **before** ``_drain_settled``,
|
|
1454
|
+
and :meth:`_given_up` reads the pair in the opposite order to conclude that a shutdown
|
|
1455
|
+
expired on a live drain — so inverting these two lines would make an ordinary terminal
|
|
1456
|
+
exit indistinguishable from an abandoned one for a moment, and a ``flush()`` landing there
|
|
1457
|
+
would be told ``"abandoned"`` where ``"thread-died"`` is the truth. Both are falsy, so the
|
|
1458
|
+
cost is a wrong reason rather than a wrong verdict, which is why it is recorded here
|
|
1459
|
+
rather than pinned by a test. ``_drain_finished`` is set the instant the loop stops reading
|
|
1325
1460
|
the queue — *before* :meth:`_terminal_failure`, which writes to stderr and can block on
|
|
1326
1461
|
a slow reader — so a ``shutdown()`` arriving during that window sees a drain that is
|
|
1327
1462
|
already finished and declines to queue a sentinel nothing would consume. And it is set
|
|
@@ -1403,7 +1538,7 @@ class Worker:
|
|
|
1403
1538
|
self._taken_markers = [m for m in self._taken_markers if m is not marker]
|
|
1404
1539
|
|
|
1405
1540
|
def _release_waiters(self) -> None:
|
|
1406
|
-
"""Answers
|
|
1541
|
+
"""Answers the ``flush()`` markers outstanding at the moment it runs.
|
|
1407
1542
|
|
|
1408
1543
|
**Two populations, and the second is not optional** (SPEC-050 FR-001). A marker still in
|
|
1409
1544
|
the queue is read from it; a marker the drain thread has already **taken** is read from
|
|
@@ -1430,9 +1565,12 @@ class Worker:
|
|
|
1430
1565
|
than too long. The marker keeps its pessimistic ``delivered``, which is the honest
|
|
1431
1566
|
answer: the drain that would have carried it is gone.
|
|
1432
1567
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1568
|
+
**It answers a snapshot, so "every" would be the wrong word** and a caller arriving
|
|
1569
|
+
after it is not covered here. A ``flush()`` that passed its guards microseconds before
|
|
1570
|
+
this sweep can still enqueue its marker after it. That caller is answered by
|
|
1571
|
+
:meth:`flush`'s own post-put re-check instead, which consults the same
|
|
1572
|
+
``_drain_settled`` this path sets — before SPEC-050 FR-001 added it there, such a caller
|
|
1573
|
+
waited out its timeout, and a ``timeout=None`` one waited forever.
|
|
1436
1574
|
A marker left queued is also still counted by ``health().queued``, which describes
|
|
1437
1575
|
submissions; removing it would mean deleting a specific item, which ``Queue`` has no
|
|
1438
1576
|
public way to do, and the read above is the access ``architecture.md`` §13 sanctions.
|
|
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
|
{log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/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
|
{log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/logging_sink.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
|