log-foundry 0.5.1.dev7__tar.gz → 0.5.1.dev8__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.5.1.dev7 → log_foundry-0.5.1.dev8}/PKG-INFO +1 -1
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/pyproject.toml +1 -1
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/multi.py +26 -1
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/LICENSE +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/README.md +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/api.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/config.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/console.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/context.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/model.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.5.1.dev7 → log_foundry-0.5.1.dev8}/src/log_foundry/worker.py +0 -0
|
@@ -6,6 +6,12 @@ whose ``emit`` (or ``close``) raises is isolated — the failure is counted on `
|
|
|
6
6
|
logged to stderr, and its siblings still run — so one broken destination never fails the whole
|
|
7
7
|
fan-out or the worker's retry. This mirrors the worker's own survive-a-sink-failure isolation
|
|
8
8
|
boundary (arch §9; best-practices §7 sanctions the broad catch here).
|
|
9
|
+
|
|
10
|
+
The one exception is **total** failure: if every child raised, ``emit`` re-raises rather than
|
|
11
|
+
reporting success, so the worker's retry engages (SPEC-017 FR-004). Swallowing there meant a
|
|
12
|
+
``MultiSink`` whose destinations were all down reported healthy on every batch and the loss was
|
|
13
|
+
invisible. ``close()`` keeps the unconditional isolate-and-continue behaviour — a failed close
|
|
14
|
+
has nothing to retry.
|
|
9
15
|
"""
|
|
10
16
|
|
|
11
17
|
from __future__ import annotations
|
|
@@ -29,16 +35,35 @@ class MultiSink:
|
|
|
29
35
|
self.failed = 0
|
|
30
36
|
|
|
31
37
|
def emit(self, batch: list[dict[str, object]]) -> None:
|
|
32
|
-
"""Forward ``batch`` to every child in construction order, isolating failures (FR-002).
|
|
38
|
+
"""Forward ``batch`` to every child in construction order, isolating failures (FR-002).
|
|
39
|
+
|
|
40
|
+
When **every** child failed, re-raise the first child's exception so the worker's bounded
|
|
41
|
+
retry sees the total loss (SPEC-017 FR-004). Partial success stays isolated: a retry there
|
|
42
|
+
would re-deliver the batch to the children that already took it, and duplicates are worse
|
|
43
|
+
than the one failure already counted on ``failed`` and written to stderr. Total failure
|
|
44
|
+
delivered nothing, so it has no duplicates to create and is the one case worth retrying.
|
|
45
|
+
"""
|
|
46
|
+
first_error: Exception | None = None
|
|
47
|
+
delivered = 0
|
|
33
48
|
for sink in self._sinks:
|
|
34
49
|
try:
|
|
35
50
|
sink.emit(batch)
|
|
36
51
|
except Exception as err: # isolation boundary: one child must not fail the rest
|
|
37
52
|
self.failed += 1
|
|
53
|
+
if first_error is None:
|
|
54
|
+
first_error = err
|
|
38
55
|
sys.stderr.write(
|
|
39
56
|
f"log-foundry: MultiSink child {type(sink).__name__}.emit "
|
|
40
57
|
f"failed and was skipped: {err!r}\n"
|
|
41
58
|
)
|
|
59
|
+
else:
|
|
60
|
+
delivered += 1
|
|
61
|
+
if delivered == 0 and first_error is not None:
|
|
62
|
+
# Raised outside any ``except`` block, so ``__context__`` is untouched and the
|
|
63
|
+
# exception propagates by identity with its original traceback. The ``is not None``
|
|
64
|
+
# is also what keeps an empty ``MultiSink`` a no-op rather than a raise — otherwise a
|
|
65
|
+
# misconfigured fan-out with no children would retry every batch to exhaustion.
|
|
66
|
+
raise first_error
|
|
42
67
|
|
|
43
68
|
def close(self) -> None:
|
|
44
69
|
"""Close every child, isolating a failing child so the rest still close (FR-002)."""
|
|
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
|
|
File without changes
|