log-foundry 0.10.2.dev74__tar.gz → 0.10.2.dev76__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.dev74 → log_foundry-0.10.2.dev76}/PKG-INFO +1 -1
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/decorator.py +119 -17
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/LICENSE +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/README.md +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/_diag.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/_fork.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/_lifecycle.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/config.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/context.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/results.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/_retry.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/memory.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/null.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/src/log_foundry/worker.py +0 -0
|
@@ -309,13 +309,16 @@ def _get_worker() -> Worker:
|
|
|
309
309
|
Exception: Whatever constructing the sink or worker raises.
|
|
310
310
|
"""
|
|
311
311
|
global _worker, _orphan_sink
|
|
312
|
-
|
|
312
|
+
worker = _worker_exists()
|
|
313
|
+
if worker is None:
|
|
313
314
|
with _worker_lock:
|
|
314
|
-
|
|
315
|
+
worker = _worker_exists()
|
|
316
|
+
if worker is None:
|
|
315
317
|
_register_exit_handler()
|
|
316
|
-
|
|
318
|
+
worker = Worker(_ensure_sink())
|
|
319
|
+
_worker = worker
|
|
317
320
|
_orphan_sink = None
|
|
318
|
-
return
|
|
321
|
+
return worker
|
|
319
322
|
|
|
320
323
|
|
|
321
324
|
def _register_exit_handler() -> None:
|
|
@@ -414,7 +417,7 @@ def _rebuild_worker_after_fork() -> None:
|
|
|
414
417
|
None. ``_fork`` absorbs and announces a handler's failure, so a child whose worker cannot
|
|
415
418
|
be rebuilt still has working locks.
|
|
416
419
|
"""
|
|
417
|
-
worker =
|
|
420
|
+
worker = _worker_exists()
|
|
418
421
|
if worker is None:
|
|
419
422
|
return
|
|
420
423
|
resume = not worker.retired
|
|
@@ -424,8 +427,103 @@ def _rebuild_worker_after_fork() -> None:
|
|
|
424
427
|
_fork.register_child_handler(_rebuild_worker_after_fork)
|
|
425
428
|
|
|
426
429
|
|
|
430
|
+
def _worker_exists() -> Worker | None:
|
|
431
|
+
"""Existence — is there a worker at all, and therefore anything to do (arch §9.2).
|
|
432
|
+
|
|
433
|
+
The first of the four questions a guard may ask, and the weakest: it says only that this
|
|
434
|
+
process built a worker, never that the worker still delivers or that it owns any particular
|
|
435
|
+
sink. A retired worker is still the process worker, which is why rebuilding one here would
|
|
436
|
+
fight a process trying to exit (SPEC-019) and why :func:`_shutdown_worker`,
|
|
437
|
+
:func:`_flush_worker` and :func:`_worker_health` all answer from it rather than from
|
|
438
|
+
:func:`_live_worker`.
|
|
439
|
+
|
|
440
|
+
**No lock is taken**, and callers must not assume one. Four of this module's guards ask a
|
|
441
|
+
question with ``_worker_lock`` already held (:func:`_get_worker`'s inner check,
|
|
442
|
+
:func:`_close_orphan_sink`, :func:`_swap_sink`, and :func:`_offer_orphan_signal` through all
|
|
443
|
+
three of its callers), so acquiring a non-reentrant lock here would deadlock them; and
|
|
444
|
+
:func:`_get_worker`'s **outer** check is deliberately unlocked on the ``@trace`` hot path,
|
|
445
|
+
where a lock would serialize every span flush in the process. The read is a single reference
|
|
446
|
+
load, which is atomic — a caller needing consistency across two reads takes ``_worker_lock``
|
|
447
|
+
itself, exactly as it does today.
|
|
448
|
+
|
|
449
|
+
Its own definition is the one of the four the roster does not file, because the body is a
|
|
450
|
+
bare ``return _worker`` and ``_boolean_positions`` excludes a plain ``Name`` return by
|
|
451
|
+
design. That is not a hole: every rewrite that would change the category introduces a
|
|
452
|
+
boolean the walker does see — an ``IfExp`` test, or a call to :func:`_live_worker`.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
None.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
The process worker if one was ever built, retired or not, otherwise ``None``.
|
|
459
|
+
|
|
460
|
+
Raises:
|
|
461
|
+
None.
|
|
462
|
+
"""
|
|
463
|
+
return _worker
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _worker_owns(sink: Sink) -> bool:
|
|
467
|
+
"""Ownership — who *owns* a close, which a retired worker still does (arch §9.2).
|
|
468
|
+
|
|
469
|
+
The third question, and the one three reviewers each named a different site for. It is not
|
|
470
|
+
liveness: :meth:`Worker.swap_sink` returns early once ``_shutdown_done``, so a retired worker
|
|
471
|
+
keeps its old sink forever, and answering "who closes this" with :func:`_live_worker` closes
|
|
472
|
+
it a second time on a clean shutdown and closes it **under a live writer** on an expired one
|
|
473
|
+
— both measured (SPEC-033 FR-002).
|
|
474
|
+
|
|
475
|
+
Takes no lock, for the reason :func:`_worker_exists` states; :func:`_close_orphan_sink` and
|
|
476
|
+
:func:`_swap_sink` both ask it under ``_worker_lock``, which is where the consistency they
|
|
477
|
+
need comes from.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
sink: The sink whose owner is in question.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
Whether the process worker holds that sink, retired or not.
|
|
484
|
+
|
|
485
|
+
Raises:
|
|
486
|
+
None.
|
|
487
|
+
"""
|
|
488
|
+
worker = _worker
|
|
489
|
+
return worker is not None and worker.sink is sink
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def _worker_owns_now(sink: Sink) -> bool:
|
|
493
|
+
"""Ownership ∧ moment — whose stop event the sink should be holding *now* (arch §9.2).
|
|
494
|
+
|
|
495
|
+
The fourth question, and a **conjunction** rather than a new subject, which is why it is named
|
|
496
|
+
for both terms. It exists because neither of its halves is right alone at
|
|
497
|
+
:func:`_offer_orphan_signal`, the one site that asks it: bare ownership skips the offer for a
|
|
498
|
+
worker whose shutdown has *finished*, leaving a live sink holding a set event that can never
|
|
499
|
+
clear, while liveness alone un-skips for the whole drain and hands the drain thread a fresh
|
|
500
|
+
event nobody will set — ``retired`` latches on **entry** to :meth:`Worker.shutdown`, not at
|
|
501
|
+
its completion. Both were measured (SPEC-035 FR-001), and the identity term is what stops an
|
|
502
|
+
orphan log to sink Y being skipped merely because a live worker is draining into sink X.
|
|
503
|
+
|
|
504
|
+
Takes no lock. Its one caller is :func:`_offer_orphan_signal`, which does not acquire
|
|
505
|
+
``_worker_lock`` itself — all three of *its* callers hold it, and that is the obligation a
|
|
506
|
+
fourth caller would have to satisfy.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
sink: The sink about to be offered a stop signal.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
Whether the process worker holds that sink *and* is still draining into it.
|
|
513
|
+
|
|
514
|
+
Raises:
|
|
515
|
+
None.
|
|
516
|
+
"""
|
|
517
|
+
worker = _worker
|
|
518
|
+
return worker is not None and worker.sink is sink and worker.draining
|
|
519
|
+
|
|
520
|
+
|
|
427
521
|
def _live_worker() -> Worker | None:
|
|
428
|
-
"""
|
|
522
|
+
"""Liveness — who *performs* an action, and a retired worker performs nothing (arch §9.2).
|
|
523
|
+
|
|
524
|
+
The second of the four questions (SPEC-033 FR-002). Reading ``retired`` in order to *report*
|
|
525
|
+
it — as :func:`_worker_health` does — is this same question asked for a different purpose,
|
|
526
|
+
not a fifth category.
|
|
429
527
|
|
|
430
528
|
Three guards ask who owns a sink, and two of them mean a *live* owner. A retired worker holds
|
|
431
529
|
its sink forever — :meth:`Worker.swap_sink` returns early once shut down — so keying on a
|
|
@@ -439,6 +537,10 @@ def _live_worker() -> Worker | None:
|
|
|
439
537
|
ownership is exactly what must make it decline, since an expired shutdown leaves the drain
|
|
440
538
|
thread possibly still inside that sink's ``emit``.
|
|
441
539
|
|
|
540
|
+
Takes no lock, for the reason :func:`_worker_exists` states. :func:`_swap_sink` asks it
|
|
541
|
+
under ``_worker_lock``; :func:`_flush_live_sink` asks it under no lock at all, which is
|
|
542
|
+
sound because it consumes the answer immediately and holds nothing across it.
|
|
543
|
+
|
|
442
544
|
Args:
|
|
443
545
|
None.
|
|
444
546
|
|
|
@@ -501,8 +603,7 @@ def _offer_orphan_signal(sink: Sink) -> None:
|
|
|
501
603
|
None.
|
|
502
604
|
"""
|
|
503
605
|
global _orphan_stop
|
|
504
|
-
|
|
505
|
-
if worker is not None and worker.sink is sink and worker.draining:
|
|
606
|
+
if _worker_owns_now(sink):
|
|
506
607
|
return
|
|
507
608
|
if _orphan_stop.is_set():
|
|
508
609
|
_orphan_stop = threading.Event()
|
|
@@ -554,7 +655,7 @@ def _close_orphan_sink() -> None:
|
|
|
554
655
|
owed = _orphan_sink
|
|
555
656
|
if owed is None:
|
|
556
657
|
return
|
|
557
|
-
if
|
|
658
|
+
if _worker_owns(owed):
|
|
558
659
|
return
|
|
559
660
|
_orphan_sink = None
|
|
560
661
|
_orphan_closed_sink = owed
|
|
@@ -605,8 +706,9 @@ def _shutdown_worker(timeout: float | None = DEFAULT_SHUTDOWN_TIMEOUT) -> None:
|
|
|
605
706
|
_orphan_retired = True
|
|
606
707
|
_orphan_stop.set()
|
|
607
708
|
deadline = None if timeout is None else monotonic() + timeout
|
|
608
|
-
|
|
609
|
-
|
|
709
|
+
worker = _worker_exists()
|
|
710
|
+
if worker is not None:
|
|
711
|
+
worker.shutdown(timeout)
|
|
610
712
|
_close_orphan_sink()
|
|
611
713
|
return
|
|
612
714
|
_close_orphan_sink()
|
|
@@ -690,7 +792,7 @@ def _swap_sink(new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) ->
|
|
|
690
792
|
return
|
|
691
793
|
_orphan_sink = new_sink
|
|
692
794
|
_offer_orphan_signal(new_sink)
|
|
693
|
-
if
|
|
795
|
+
if not _worker_owns(old):
|
|
694
796
|
_orphan_closed_sink = old
|
|
695
797
|
closer = _lifecycle.release(old, detached=True)
|
|
696
798
|
if worker is not None:
|
|
@@ -863,8 +965,8 @@ def _flush_live_sink() -> bool:
|
|
|
863
965
|
None. A failure is reported as a ``FlushResult`` reason by the caller, never raised: a
|
|
864
966
|
flush is the call most likely to be made in a ``finally``.
|
|
865
967
|
"""
|
|
866
|
-
worker =
|
|
867
|
-
sink = worker.sink if worker is not None
|
|
968
|
+
worker = _live_worker()
|
|
969
|
+
sink = worker.sink if worker is not None else _orphan_sink
|
|
868
970
|
if sink is None:
|
|
869
971
|
return True
|
|
870
972
|
try:
|
|
@@ -915,7 +1017,7 @@ def _flush_worker(timeout: float | None = 5.0) -> FlushResult:
|
|
|
915
1017
|
except Exception as exc:
|
|
916
1018
|
_diag.absorbed("sweeping open spans for a flush", exc, "buffered events were not swept")
|
|
917
1019
|
swept = False
|
|
918
|
-
worker =
|
|
1020
|
+
worker = _worker_exists()
|
|
919
1021
|
drained: FlushResult = FlushResult(ok=True)
|
|
920
1022
|
thread_died = False
|
|
921
1023
|
if worker is not None:
|
|
@@ -1025,7 +1127,7 @@ def _delivering_to_an_inherited_sink() -> bool:
|
|
|
1025
1127
|
forked.
|
|
1026
1128
|
"""
|
|
1027
1129
|
try:
|
|
1028
|
-
worker =
|
|
1130
|
+
worker = _worker_exists()
|
|
1029
1131
|
sink = worker.sink if worker is not None else (_orphan_sink or _live_config().sink)
|
|
1030
1132
|
return sink is not None and not _lifecycle.releasable(sink)
|
|
1031
1133
|
except Exception:
|
|
@@ -1074,7 +1176,7 @@ def _worker_health() -> Health:
|
|
|
1074
1176
|
Raises:
|
|
1075
1177
|
None.
|
|
1076
1178
|
"""
|
|
1077
|
-
worker =
|
|
1179
|
+
worker = _worker_exists()
|
|
1078
1180
|
orphan_lost, in_span_lost = _read_losses()
|
|
1079
1181
|
if worker is None:
|
|
1080
1182
|
return Health(
|
|
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
|
{log_foundry-0.10.2.dev74 → log_foundry-0.10.2.dev76}/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
|
|
File without changes
|