log-foundry 0.5.1.dev9__tar.gz → 0.5.1.dev10__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.dev9 → log_foundry-0.5.1.dev10}/PKG-INFO +1 -1
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/pyproject.toml +1 -1
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/model.py +10 -4
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sanitize.py +8 -3
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/multi.py +5 -1
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/worker.py +9 -3
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/LICENSE +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/README.md +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/api.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/config.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/console.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/context.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.5.1.dev9 → log_foundry-0.5.1.dev10}/src/log_foundry/sinks/util.py +0 -0
|
@@ -70,8 +70,13 @@ def build_event(
|
|
|
70
70
|
→ per-call ``fields`` (arch §5.1). Later sources win on a key conflict.
|
|
71
71
|
|
|
72
72
|
The merged mapping is coerced and size-bounded here (SPEC-017 FR-001/FR-002), which is what
|
|
73
|
-
makes every sink's bare ``json.dumps`` safe without any of them changing.
|
|
74
|
-
|
|
73
|
+
makes every sink's bare ``json.dumps`` safe without any of them changing.
|
|
74
|
+
|
|
75
|
+
``message`` and ``function`` are bounded too. They are base fields, but unlike the other ten
|
|
76
|
+
both are caller-supplied text: ``message`` directly, and ``function`` via ``@trace(name=...)``
|
|
77
|
+
or — on the orphan path, where ``api`` names the standalone span after the message itself —
|
|
78
|
+
from the same unbounded string. Leaving either out would keep ``info(huge_string)`` unbounded,
|
|
79
|
+
which is the hole this ceiling exists to close.
|
|
75
80
|
"""
|
|
76
81
|
from log_foundry.config import get_config
|
|
77
82
|
from log_foundry.ids import new_log_id
|
|
@@ -80,6 +85,7 @@ def build_event(
|
|
|
80
85
|
merged: dict[str, object] = {**cfg.defaults, **span.defaults, **baggage, **fields}
|
|
81
86
|
safe, clipped = sanitize_fields(merged, cfg=cfg)
|
|
82
87
|
bounded_message, message_clipped = truncate_str(message, cfg.max_value_bytes)
|
|
88
|
+
bounded_function, function_clipped = truncate_str(span.name, cfg.max_value_bytes)
|
|
83
89
|
event: dict[str, object] = {
|
|
84
90
|
"timestamp": _iso_now(),
|
|
85
91
|
"level": level,
|
|
@@ -88,13 +94,13 @@ def build_event(
|
|
|
88
94
|
"span_id": span.span_id,
|
|
89
95
|
"parent_span_id": span.parent_span_id,
|
|
90
96
|
"log_id": new_log_id(),
|
|
91
|
-
"function":
|
|
97
|
+
"function": bounded_function,
|
|
92
98
|
"service": cfg.service,
|
|
93
99
|
"version": cfg.version,
|
|
94
100
|
"env": cfg.env,
|
|
95
101
|
}
|
|
96
102
|
# Before ``fields`` so it reads ahead of the payload blob in a rendered log line.
|
|
97
|
-
if clipped or message_clipped:
|
|
103
|
+
if clipped or message_clipped or function_clipped:
|
|
98
104
|
event[_TRUNCATED] = True
|
|
99
105
|
event["fields"] = safe
|
|
100
106
|
return event
|
|
@@ -254,8 +254,9 @@ class _Coercer:
|
|
|
254
254
|
def coerce(value: object, *, cfg: Config) -> object:
|
|
255
255
|
"""Return a JSON-serializable, size-bounded equivalent of ``value``. Never raises.
|
|
256
256
|
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
``value`` is treated as a field value, i.e. depth 0 — the same level it would occupy inside
|
|
258
|
+
an event's ``fields``. Use :func:`sanitize_fields` for a whole event mapping; it reports
|
|
259
|
+
whether a ceiling fired, which this cannot.
|
|
259
260
|
"""
|
|
260
261
|
return _Coercer(cfg).value(value, 0)
|
|
261
262
|
|
|
@@ -271,7 +272,11 @@ def sanitize_fields(
|
|
|
271
272
|
"""
|
|
272
273
|
coercer = _Coercer(cfg)
|
|
273
274
|
try:
|
|
274
|
-
|
|
275
|
+
# ``-1`` so the *values* of the top-level mapping sit at depth 0: ``fields`` is the event's
|
|
276
|
+
# payload container, not a level of nesting the caller chose. Otherwise ``max_depth=1``
|
|
277
|
+
# would replace every field value with ``<depth limit>`` and emit a uniformly empty event,
|
|
278
|
+
# which is exactly what FR-006's validation exists to prevent.
|
|
279
|
+
result = coercer.mapping(fields, -1)
|
|
275
280
|
except Exception: # noqa: BLE001 — belt and braces: `_Coercer.value` is already total, but
|
|
276
281
|
return {}, True # a hostile top-level mapping must not reach the caller either.
|
|
277
282
|
if not isinstance(result, dict):
|
|
@@ -27,7 +27,11 @@ class MultiSink:
|
|
|
27
27
|
"""A :class:`~log_foundry.sinks.base.Sink` that forwards each batch to every child sink.
|
|
28
28
|
|
|
29
29
|
Attributes:
|
|
30
|
-
failed: Count of child ``emit``/``close`` calls that raised
|
|
30
|
+
failed: Count of child ``emit``/``close`` **calls** that raised. Note this counts calls,
|
|
31
|
+
not batches: since total failure re-raises (SPEC-017 FR-004), the worker retries the
|
|
32
|
+
same batch, so one batch against an all-down fan-out of *n* children increments this
|
|
33
|
+
by *n* per attempt — ``n * (max_retries + 1)`` in total, with a stderr line each.
|
|
34
|
+
That is the visible cost of the loss no longer being silent.
|
|
31
35
|
"""
|
|
32
36
|
|
|
33
37
|
def __init__(self, *sinks: Sink) -> None:
|
|
@@ -121,9 +121,15 @@ class Worker:
|
|
|
121
121
|
# submitters. Lines may therefore interleave out of order under concurrency; the
|
|
122
122
|
# counts they carry are still exact.
|
|
123
123
|
if total == 1 or total % _DROP_WARN_EVERY == 0:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
try:
|
|
125
|
+
sys.stderr.write(
|
|
126
|
+
f"log-foundry: log queue full, dropped {total} submission(s) so far\n"
|
|
127
|
+
)
|
|
128
|
+
except Exception: # noqa: BLE001 — submit() runs on the *caller's* thread, so an
|
|
129
|
+
# unwritable stderr (closed fd, broken pipe, daemonized process) would raise
|
|
130
|
+
# straight into the app. A diagnostic about dropped logs must never itself be
|
|
131
|
+
# the reason a decorated function fails. The counter is already recorded.
|
|
132
|
+
pass
|
|
127
133
|
|
|
128
134
|
def health(self) -> Health:
|
|
129
135
|
"""Snapshot the delivery counters (SPEC-017 FR-005). Never raises.
|
|
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
|