log-foundry 0.4.1.dev3__tar.gz → 0.4.1.dev4__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.4.1.dev3 → log_foundry-0.4.1.dev4}/PKG-INFO +47 -2
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/README.md +46 -1
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/pyproject.toml +1 -1
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/sqs.py +21 -2
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/LICENSE +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/api.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/config.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/console.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/context.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/model.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.4.1.dev3 → log_foundry-0.4.1.dev4}/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.4.1.
|
|
3
|
+
Version: 0.4.1.dev4
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -512,7 +512,7 @@ limits, retries partial failures, and drops any single event too large to ever f
|
|
|
512
512
|
|
|
513
513
|
| Sink | Import from | Configure |
|
|
514
514
|
|---|---|---|
|
|
515
|
-
| `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
|
|
515
|
+
| `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3, fifo=None, message_group_id=None, message_deduplication_id=None)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages. Standard **and** FIFO queues |
|
|
516
516
|
| `SNSSink` | `log_foundry.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
|
|
517
517
|
| `KinesisSink` | `log_foundry.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
|
|
518
518
|
| `FirehoseSink` | `log_foundry.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
|
|
@@ -525,6 +525,51 @@ lf.configure(service="payments",
|
|
|
525
525
|
|
|
526
526
|
Consuming from the buffer and indexing into ELK is a separate component, outside this library.
|
|
527
527
|
|
|
528
|
+
`SQSSink` does not retry a message SQS rejects as a **sender fault** — the retry would re-send it
|
|
529
|
+
byte-identical, so it can only fail the same way. Those are counted on `.failed` immediately and
|
|
530
|
+
the SQS error code is named on stderr. Throttles and internal errors are still retried up to
|
|
531
|
+
`max_retries`.
|
|
532
|
+
|
|
533
|
+
##### FIFO queues
|
|
534
|
+
|
|
535
|
+
A queue URL ending in `.fifo` switches `SQSSink` into FIFO mode automatically — AWS requires the
|
|
536
|
+
suffix on every FIFO queue, so nothing needs configuring:
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
SQSSink(queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/logs.fifo")
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Each message then carries a **`MessageGroupId`**, which defaults to the event's own `trace_id`.
|
|
543
|
+
SQS guarantees ordering *within* a group, and a trace is exactly the unit whose events should stay
|
|
544
|
+
ordered — while separate traces land in separate groups, so the queue delivers them in parallel
|
|
545
|
+
instead of serializing your whole process behind one group. (`KinesisSink` partitions on `trace_id`
|
|
546
|
+
by default for the same reason.) The **`MessageDeduplicationId`** defaults to the event's `log_id`,
|
|
547
|
+
already a per-event UUID, so SQS's five-minute deduplication window never collapses two distinct
|
|
548
|
+
records.
|
|
549
|
+
|
|
550
|
+
Override the group with a constant or a callable:
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
# One group for the whole process — strict global ordering, capped at ~300 msg/s.
|
|
554
|
+
SQSSink(queue_url=FIFO_URL, message_group_id="payments")
|
|
555
|
+
|
|
556
|
+
# Group by anything on the event. Baggage lands in `fields`, so this groups by tenant
|
|
557
|
+
# and falls back to per-trace when unset:
|
|
558
|
+
SQSSink(queue_url=FIFO_URL,
|
|
559
|
+
message_group_id=lambda e: str(e["fields"].get("tenant_id") or e["trace_id"]))
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Pass `fifo=True` or `fifo=False` to override the URL-based detection. Standard queues are entirely
|
|
563
|
+
unaffected — their messages carry neither parameter.
|
|
564
|
+
|
|
565
|
+
Two things worth knowing:
|
|
566
|
+
|
|
567
|
+
- **Ordering is best-effort across a retry.** If one message fails and a same-group message ahead
|
|
568
|
+
of it succeeded, the retry lands after it. Holding a whole group back on a single failure would
|
|
569
|
+
trade log delivery for ordering you can rebuild from `timestamp`, so the sink doesn't.
|
|
570
|
+
- **FIFO queues cap throughput** at 300 messages/second (3,000 with batching), or higher in
|
|
571
|
+
high-throughput mode. That's queue-side configuration, not something the library sets.
|
|
572
|
+
|
|
528
573
|
#### Queue & stream
|
|
529
574
|
|
|
530
575
|
Each needs its own extra (lazy-imported). All publish + retry within a bound and close cleanly.
|
|
@@ -476,7 +476,7 @@ limits, retries partial failures, and drops any single event too large to ever f
|
|
|
476
476
|
|
|
477
477
|
| Sink | Import from | Configure |
|
|
478
478
|
|---|---|---|
|
|
479
|
-
| `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
|
|
479
|
+
| `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3, fifo=None, message_group_id=None, message_deduplication_id=None)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages. Standard **and** FIFO queues |
|
|
480
480
|
| `SNSSink` | `log_foundry.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
|
|
481
481
|
| `KinesisSink` | `log_foundry.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
|
|
482
482
|
| `FirehoseSink` | `log_foundry.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
|
|
@@ -489,6 +489,51 @@ lf.configure(service="payments",
|
|
|
489
489
|
|
|
490
490
|
Consuming from the buffer and indexing into ELK is a separate component, outside this library.
|
|
491
491
|
|
|
492
|
+
`SQSSink` does not retry a message SQS rejects as a **sender fault** — the retry would re-send it
|
|
493
|
+
byte-identical, so it can only fail the same way. Those are counted on `.failed` immediately and
|
|
494
|
+
the SQS error code is named on stderr. Throttles and internal errors are still retried up to
|
|
495
|
+
`max_retries`.
|
|
496
|
+
|
|
497
|
+
##### FIFO queues
|
|
498
|
+
|
|
499
|
+
A queue URL ending in `.fifo` switches `SQSSink` into FIFO mode automatically — AWS requires the
|
|
500
|
+
suffix on every FIFO queue, so nothing needs configuring:
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
SQSSink(queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/logs.fifo")
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Each message then carries a **`MessageGroupId`**, which defaults to the event's own `trace_id`.
|
|
507
|
+
SQS guarantees ordering *within* a group, and a trace is exactly the unit whose events should stay
|
|
508
|
+
ordered — while separate traces land in separate groups, so the queue delivers them in parallel
|
|
509
|
+
instead of serializing your whole process behind one group. (`KinesisSink` partitions on `trace_id`
|
|
510
|
+
by default for the same reason.) The **`MessageDeduplicationId`** defaults to the event's `log_id`,
|
|
511
|
+
already a per-event UUID, so SQS's five-minute deduplication window never collapses two distinct
|
|
512
|
+
records.
|
|
513
|
+
|
|
514
|
+
Override the group with a constant or a callable:
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
# One group for the whole process — strict global ordering, capped at ~300 msg/s.
|
|
518
|
+
SQSSink(queue_url=FIFO_URL, message_group_id="payments")
|
|
519
|
+
|
|
520
|
+
# Group by anything on the event. Baggage lands in `fields`, so this groups by tenant
|
|
521
|
+
# and falls back to per-trace when unset:
|
|
522
|
+
SQSSink(queue_url=FIFO_URL,
|
|
523
|
+
message_group_id=lambda e: str(e["fields"].get("tenant_id") or e["trace_id"]))
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Pass `fifo=True` or `fifo=False` to override the URL-based detection. Standard queues are entirely
|
|
527
|
+
unaffected — their messages carry neither parameter.
|
|
528
|
+
|
|
529
|
+
Two things worth knowing:
|
|
530
|
+
|
|
531
|
+
- **Ordering is best-effort across a retry.** If one message fails and a same-group message ahead
|
|
532
|
+
of it succeeded, the retry lands after it. Holding a whole group back on a single failure would
|
|
533
|
+
trade log delivery for ordering you can rebuild from `timestamp`, so the sink doesn't.
|
|
534
|
+
- **FIFO queues cap throughput** at 300 messages/second (3,000 with batching), or higher in
|
|
535
|
+
high-throughput mode. That's queue-side configuration, not something the library sets.
|
|
536
|
+
|
|
492
537
|
#### Queue & stream
|
|
493
538
|
|
|
494
539
|
Each needs its own extra (lazy-imported). All publish + retry within a bound and close cleanly.
|
|
@@ -194,6 +194,11 @@ class SQSSink:
|
|
|
194
194
|
are counted (``failed``) and logged, not silently dropped (FR-003). The FIFO parameters
|
|
195
195
|
are attached only when the queue is FIFO, so a standard queue's entries are exactly
|
|
196
196
|
``Id`` + ``MessageBody`` (SPEC-016 FR-004).
|
|
197
|
+
|
|
198
|
+
Entries SQS marks ``SenderFault`` are abandoned rather than retried (SPEC-016 FR-006):
|
|
199
|
+
a retry re-sends them byte-identical, so a fault in the request itself can only fail
|
|
200
|
+
the same way. Throttles and internal errors carry ``SenderFault: false`` and are still
|
|
201
|
+
retried under the bound.
|
|
197
202
|
"""
|
|
198
203
|
entries: list[dict[str, str]] = []
|
|
199
204
|
for i, item in enumerate(prepared):
|
|
@@ -208,8 +213,22 @@ class SQSSink:
|
|
|
208
213
|
failed = response.get("Failed", [])
|
|
209
214
|
if not failed:
|
|
210
215
|
return
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
# Abandon sender faults immediately and name the code: the retry would re-send the
|
|
217
|
+
# entry byte-identical, and the code is the only thing that makes a rejection
|
|
218
|
+
# diagnosable from the log line alone. A missing flag is treated as retryable, so
|
|
219
|
+
# an unfamiliar response shape degrades to the old behaviour rather than dropping.
|
|
220
|
+
sender_faults = [item for item in failed if item.get("SenderFault")]
|
|
221
|
+
if sender_faults:
|
|
222
|
+
self.failed += len(sender_faults)
|
|
223
|
+
sys.stderr.write(
|
|
224
|
+
f"log-foundry: {len(sender_faults)} SQS message(s) rejected as invalid "
|
|
225
|
+
f"(first code: {sender_faults[0].get('Code', 'unknown')}); not retried\n"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
retryable_ids = {item["Id"] for item in failed if not item.get("SenderFault")}
|
|
229
|
+
if not retryable_ids:
|
|
230
|
+
return
|
|
231
|
+
entries = [entry for entry in entries if entry["Id"] in retryable_ids]
|
|
213
232
|
if attempt >= self.max_retries:
|
|
214
233
|
self.failed += len(entries)
|
|
215
234
|
sys.stderr.write(
|
|
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
|