log-foundry 0.7.2.dev2__tar.gz → 0.7.2.dev3__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.7.2.dev2 → log_foundry-0.7.2.dev3}/PKG-INFO +7 -1
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/README.md +6 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/pyproject.toml +1 -1
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/config.py +10 -1
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sanitize.py +23 -2
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/worker.py +17 -1
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/LICENSE +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/api.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/console.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/context.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/model.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.7.2.dev2 → log_foundry-0.7.2.dev3}/src/log_foundry/sinks/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: log-foundry
|
|
3
|
-
Version: 0.7.2.
|
|
3
|
+
Version: 0.7.2.dev3
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -783,6 +783,12 @@ the default configuration the interpreter's limit — not `max_value_bytes` —
|
|
|
783
783
|
unlikely to meet it deliberately; `int.from_bytes(blob, "big")` over a couple of kilobytes gets
|
|
784
784
|
there. Any ceiling firing sets `truncated: true` on the event.
|
|
785
785
|
|
|
786
|
+
`max_value_bytes` therefore carries two units: **UTF-8 bytes** for a string, **rendered decimal
|
|
787
|
+
length** (sign included) for an integer. They coincide for ASCII digits, and one ceiling for "how
|
|
788
|
+
big may a single value get" was preferred to a second config key. Note that all four ceilings
|
|
789
|
+
bound each *value* — an event of many bounded values can still be large; see
|
|
790
|
+
[Known constraints](docs/architecture.md#known-constraints).
|
|
791
|
+
|
|
786
792
|
## Development
|
|
787
793
|
|
|
788
794
|
```bash
|
|
@@ -747,6 +747,12 @@ the default configuration the interpreter's limit — not `max_value_bytes` —
|
|
|
747
747
|
unlikely to meet it deliberately; `int.from_bytes(blob, "big")` over a couple of kilobytes gets
|
|
748
748
|
there. Any ceiling firing sets `truncated: true` on the event.
|
|
749
749
|
|
|
750
|
+
`max_value_bytes` therefore carries two units: **UTF-8 bytes** for a string, **rendered decimal
|
|
751
|
+
length** (sign included) for an integer. They coincide for ASCII digits, and one ceiling for "how
|
|
752
|
+
big may a single value get" was preferred to a second config key. Note that all four ceilings
|
|
753
|
+
bound each *value* — an event of many bounded values can still be large; see
|
|
754
|
+
[Known constraints](docs/architecture.md#known-constraints).
|
|
755
|
+
|
|
750
756
|
## Development
|
|
751
757
|
|
|
752
758
|
```bash
|
|
@@ -24,6 +24,15 @@ class Config:
|
|
|
24
24
|
The four ``max_*`` ceilings bound every event payload (SPEC-017 FR-002). Defaults are set so
|
|
25
25
|
that the overwhelming majority of events are untouched; they exist to stop *one* pathological
|
|
26
26
|
value getting a whole event rejected by a sink's hard limit.
|
|
27
|
+
|
|
28
|
+
``max_value_bytes`` carries **two units**, deliberately (SPEC-020, recorded by SPEC-021): a
|
|
29
|
+
string is measured in UTF-8 bytes, an integer in the decimal length it renders as, sign
|
|
30
|
+
included. They coincide for ASCII digits, and one ceiling covering "how big may a single
|
|
31
|
+
value get" beat a second config key for a distinction almost no one configures. An integer is
|
|
32
|
+
also bounded by ``sys.get_int_max_str_digits()`` whenever that is lower, since a longer one
|
|
33
|
+
cannot be rendered at all.
|
|
34
|
+
|
|
35
|
+
They bound each *value*, not the event as a whole — see arch §13 Known Constraints.
|
|
27
36
|
"""
|
|
28
37
|
|
|
29
38
|
service: str = "unknown"
|
|
@@ -31,7 +40,7 @@ class Config:
|
|
|
31
40
|
env: str = "dev"
|
|
32
41
|
sink: Sink | None = None
|
|
33
42
|
defaults: dict[str, object] = field(default_factory=dict)
|
|
34
|
-
max_value_bytes: int = 8192 # per
|
|
43
|
+
max_value_bytes: int = 8192 # per value: UTF-8 bytes for a str, rendered digits for an int
|
|
35
44
|
max_stack_bytes: int = 32768 # error.stack only — legitimately long, and worth keeping
|
|
36
45
|
max_keys: int = 256 # per mapping / sequence
|
|
37
46
|
max_depth: int = 8 # nesting levels
|
|
@@ -55,6 +55,12 @@ _DEPTH_LIMIT = "<depth limit>"
|
|
|
55
55
|
_LOG10_2_NUM = 30103
|
|
56
56
|
_LOG10_2_DEN = 100000
|
|
57
57
|
|
|
58
|
+
# ``int.__lt__`` bound once, for the sign test in :meth:`_Coercer.integer` (SPEC-021 FR-003). The
|
|
59
|
+
# unbound slot cannot be diverted by an ``int`` subclass the way ``value < 0`` can, but reaching
|
|
60
|
+
# it through ``int.__lt__`` costs a global plus an attribute lookup on a per-value hot path;
|
|
61
|
+
# binding it here recovers about a third of that, the same idiom as the constants above.
|
|
62
|
+
_INT_LT = int.__lt__
|
|
63
|
+
|
|
58
64
|
# Exact-type membership, deliberately not ``isinstance``. ``IntEnum``/``StrEnum`` members *are*
|
|
59
65
|
# ``int``/``str`` instances, so an isinstance check would pass the enum member itself through and
|
|
60
66
|
# hand a sink an ``Enum`` where a plain value was promised. Exact typing lets them fall to the
|
|
@@ -74,6 +80,11 @@ def _int_digit_ceiling(max_value_bytes: int) -> int:
|
|
|
74
80
|
raises ``ValueError``, which ``json.dumps`` inherits. A configured ceiling above that cannot be
|
|
75
81
|
honoured — rendering such an integer is the very thing that raises — so the interpreter's limit
|
|
76
82
|
wins whenever it is lower. A limit of ``0`` means the interpreter imposes none.
|
|
83
|
+
|
|
84
|
+
The caller measures a negative value's rendered length, sign included, against this; the
|
|
85
|
+
interpreter's own limit counts digits only. A negative integer sitting exactly on the
|
|
86
|
+
interpreter bound is therefore replaced rather than rendered — one value at the far edge, in
|
|
87
|
+
the direction this module always errs (SPEC-021 FR-003).
|
|
77
88
|
"""
|
|
78
89
|
limit = sys.get_int_max_str_digits()
|
|
79
90
|
return max_value_bytes if limit <= 0 else min(max_value_bytes, limit)
|
|
@@ -278,7 +289,11 @@ class _Coercer:
|
|
|
278
289
|
The size test is ``bit_length()``, never ``len(str(value))``: converting an over-long
|
|
279
290
|
integer to a string raises the very ``ValueError`` this bound exists to prevent, so the
|
|
280
291
|
obvious check would move the crash rather than remove it (FR-002). ``bit_length()`` is
|
|
281
|
-
O(1), total, and ignores the sign
|
|
292
|
+
O(1), total, and ignores the sign — so the minus sign is added back explicitly, since the
|
|
293
|
+
ceiling measures what the value *renders* as and ``-10**9`` renders as eleven bytes
|
|
294
|
+
(SPEC-021 FR-003). The placeholder still names the digit count: a sign is not a digit.
|
|
295
|
+
The sign is read through an *unbound* ``int.__lt__``, which an ``int`` subclass cannot
|
|
296
|
+
divert; ``bit_length()`` stays the one trusted call, as below.
|
|
282
297
|
|
|
283
298
|
An over-long integer is *replaced*, not clipped. Dropping digits would silently change the
|
|
284
299
|
value, and a wrong number is worse than a visibly elided one — so this reuses the
|
|
@@ -291,7 +306,13 @@ class _Coercer:
|
|
|
291
306
|
``type()`` check to catch only a value engineered to lie about itself.
|
|
292
307
|
"""
|
|
293
308
|
digits = value.bit_length() * _LOG10_2_NUM // _LOG10_2_DEN + 1
|
|
294
|
-
|
|
309
|
+
# ``_INT_LT`` (unbound ``int.__lt__``), never ``value < 0``: on an ``int`` *subclass* the
|
|
310
|
+
# operator dispatches to user code, which can raise — and a raise here is caught up in
|
|
311
|
+
# :meth:`value`, replacing the whole enclosing mapping and taking every sibling key with
|
|
312
|
+
# it, which is precisely what :meth:`key` was written to prevent. The unbound call reads
|
|
313
|
+
# the underlying integer and cannot be overridden.
|
|
314
|
+
rendered = digits + 1 if _INT_LT(value, 0) else digits
|
|
315
|
+
if rendered <= _int_digit_ceiling(self._cfg.max_value_bytes):
|
|
295
316
|
return value
|
|
296
317
|
self.truncated = True
|
|
297
318
|
return f"<int: ~{digits} digits>"
|
|
@@ -308,14 +308,30 @@ class Worker:
|
|
|
308
308
|
must not be able to ride on it. The exception's *type* is reported and its message is
|
|
309
309
|
not — a sink's exception text can carry event data, and arch §6 keeps caller data out of
|
|
310
310
|
places it was not asked for (the same rule behind ``sanitize``'s type-name placeholder).
|
|
311
|
+
|
|
312
|
+
The count reports what was *in hand* and what was still *queued behind it* (SPEC-021
|
|
313
|
+
FR-002). Held alone under-reads the loss: nothing will drain the queue either, so an
|
|
314
|
+
operator reading "1 undrained event-list(s)" could conclude far less was lost than was.
|
|
315
|
+
|
|
316
|
+
The queued figure is "items", not "event-lists", and says so: like ``Health.queued`` it is
|
|
317
|
+
read without stopping the world, so it counts any internal flush/shutdown marker sitting
|
|
318
|
+
alongside real submissions, and a producer thread can add to the queue between the death
|
|
319
|
+
and the read. It is a floor on what was lost, which is the useful direction.
|
|
311
320
|
"""
|
|
312
321
|
name = type(exc).__name__
|
|
313
322
|
with self._lock:
|
|
314
323
|
self.stopped_reason = name
|
|
324
|
+
try:
|
|
325
|
+
# In its own guard, and after the record: ``qsize()`` is not guaranteed on every
|
|
326
|
+
# platform's queue, and a diagnostic must not be the reason the diagnosis is lost.
|
|
327
|
+
queued: object = self._queue.qsize()
|
|
328
|
+
except Exception:
|
|
329
|
+
queued = "?"
|
|
315
330
|
try:
|
|
316
331
|
sys.stderr.write(
|
|
317
332
|
f"log-foundry: worker thread stopped on {name}; {undrained} undrained "
|
|
318
|
-
f"event-list(s), nothing further
|
|
333
|
+
f"event-list(s) held and {queued} queued item(s) undelivered, nothing further "
|
|
334
|
+
f"will be delivered\n"
|
|
319
335
|
)
|
|
320
336
|
except Exception: # best-effort: the record above is what an operator reads.
|
|
321
337
|
pass
|
|
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
|