log-foundry 0.9.1.dev1__tar.gz → 0.9.1.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.9.1.dev1 → log_foundry-0.9.1.dev3}/PKG-INFO +1 -1
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/pyproject.toml +27 -3
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/http.py +12 -1
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sqlite.py +3 -8
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/LICENSE +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/README.md +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/api.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/config.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/console.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/context.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/model.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/worker.py +0 -0
|
@@ -20,7 +20,7 @@ dependencies = [
|
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
# Optional features. Install with: pip install log-foundry[aws]
|
|
23
|
-
version = "0.9.1.
|
|
23
|
+
version = "0.9.1.dev3"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
aws = ["boto3>=1.43.61"] # SQSSink, SNSSink, KinesisSink, FirehoseSink
|
|
@@ -70,6 +70,25 @@ pytest-cov = "^7.1.0"
|
|
|
70
70
|
ruff = ">=0.16,<0.17"
|
|
71
71
|
mypy = "^2.3"
|
|
72
72
|
|
|
73
|
+
# Supply-chain tooling (SPEC-023 FR-007). `optional = true` keeps it OUT of the
|
|
74
|
+
# `poetry install --with dev` that every CI matrix leg runs — neither tool is needed to lint,
|
|
75
|
+
# typecheck or test, and both drag in dependency trees of their own. The two security workflows
|
|
76
|
+
# ask for it explicitly with `--with security`.
|
|
77
|
+
#
|
|
78
|
+
# In the lockfile rather than fetched ad hoc so the versions are pinned and Dependabot's existing
|
|
79
|
+
# `pip` ecosystem maintains them under its cooldown, like every other dependency here.
|
|
80
|
+
[tool.poetry.group.security]
|
|
81
|
+
optional = true
|
|
82
|
+
|
|
83
|
+
#
|
|
84
|
+
# Both carry a `python = "<4.0"` marker for the same reason `clickhouse-connect` carries one:
|
|
85
|
+
# `requires-python = ">=3.12"` has no upper bound, so Poetry solves against a range that runs past
|
|
86
|
+
# 4.0, and a dependency capped at `<4.0` makes resolution fail outright. The marker scopes the tool
|
|
87
|
+
# instead of narrowing the library's own declared range.
|
|
88
|
+
[tool.poetry.group.security.dependencies]
|
|
89
|
+
cyclonedx-bom = { version = "^7.3", python = "<4.0" } # the `cyclonedx-py` CLI (SBOM generation)
|
|
90
|
+
pip-audit = { version = "^2.10", python = "<4.0" } # OSV/PyPI advisory audit of the env
|
|
91
|
+
|
|
73
92
|
|
|
74
93
|
[tool.poetry-dynamic-versioning]
|
|
75
94
|
enable = false
|
|
@@ -138,8 +157,6 @@ ignore = [
|
|
|
138
157
|
# behind an import.
|
|
139
158
|
"PERF203", # try/except inside a loop is deliberate in the worker's drain: one bad batch
|
|
140
159
|
# must not abort the rest.
|
|
141
|
-
"S608", # postgres.py and sqlite.py build INSERTs around a table name they validate
|
|
142
|
-
# themselves; the values are always parameterised.
|
|
143
160
|
"UP047", # PEP 695 `def f[T](...)`. Valid on the 3.12 floor, but rewriting the public
|
|
144
161
|
# `@trace` decorator's generics is a change to the signature users read, and
|
|
145
162
|
# the TypeVar form is not deprecated. Its own PR if ever.
|
|
@@ -154,6 +171,13 @@ ignore = [
|
|
|
154
171
|
# FileSink and RotatingFileSink hold a handle open ACROSS calls by design — that is what makes
|
|
155
172
|
# them a sink rather than a per-event open(). A context manager would close it after one emit.
|
|
156
173
|
"src/log_foundry/sinks/file.py" = ["SIM115"]
|
|
174
|
+
# These two build an INSERT around a table name because SQL cannot parameterize an identifier.
|
|
175
|
+
# The name is run through `valid_identifier` at construction, so it is a plain SQL identifier by
|
|
176
|
+
# the time it reaches the f-string, and every VALUE is parameterised. Scoped to these two files
|
|
177
|
+
# rather than ignored repository-wide: a future sink that builds SQL less carefully should still
|
|
178
|
+
# trip this rule.
|
|
179
|
+
"src/log_foundry/sinks/postgres.py" = ["S608"]
|
|
180
|
+
"src/log_foundry/sinks/sqlite.py" = ["S608"]
|
|
157
181
|
|
|
158
182
|
[tool.mypy]
|
|
159
183
|
files = ["src"]
|
|
@@ -46,6 +46,10 @@ def merge_headers(base: dict[str, str], http_kwargs: dict[str, object]) -> dict[
|
|
|
46
46
|
class HTTPSink:
|
|
47
47
|
"""A :class:`~log_foundry.sinks.base.Sink` that POSTs each batch to an HTTP endpoint.
|
|
48
48
|
|
|
49
|
+
Credentials passed as ``auth`` are sent on every request to ``url`` as given. Nothing here
|
|
50
|
+
requires ``https://`` — over a plaintext endpoint a bearer token, and a basic-auth pair (which
|
|
51
|
+
is base64, not encryption), travel in the clear. Use ``https://`` for anything off the host.
|
|
52
|
+
|
|
49
53
|
Attributes:
|
|
50
54
|
failed: Requests abandoned past the retry bound.
|
|
51
55
|
dropped_oversized: Events dropped for exceeding a destination's hard size limit (used by
|
|
@@ -154,7 +158,14 @@ class HTTPSink:
|
|
|
154
158
|
return headers, data
|
|
155
159
|
|
|
156
160
|
def _apply_auth(self, headers: dict[str, str]) -> None:
|
|
157
|
-
"""Apply bearer-token (str) or basic-auth (user, pass) credentials, if not already set.
|
|
161
|
+
"""Apply bearer-token (str) or basic-auth (user, pass) credentials, if not already set.
|
|
162
|
+
|
|
163
|
+
The scheme of ``url`` is NOT checked here: an ``http://`` endpoint sends these credentials
|
|
164
|
+
in cleartext, and basic auth is base64, not encryption. That is deliberate — a plaintext
|
|
165
|
+
endpoint is a legitimate configuration (a sidecar collector on loopback, an in-cluster
|
|
166
|
+
aggregator), and this library does not get to overrule the application's own deployment.
|
|
167
|
+
The caller owns the choice; see the class docstring.
|
|
168
|
+
"""
|
|
158
169
|
if self._auth is None or "Authorization" in headers:
|
|
159
170
|
return
|
|
160
171
|
if isinstance(self._auth, str):
|
|
@@ -13,19 +13,16 @@ ClickHouse, …) are out of scope here (the latter is SPEC-011).
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
|
-
import re
|
|
17
16
|
import sqlite3
|
|
18
17
|
|
|
18
|
+
from log_foundry.sinks._chunk import valid_identifier
|
|
19
|
+
|
|
19
20
|
__all__ = ["SQLiteSink"]
|
|
20
21
|
|
|
21
22
|
# Columns projected out of each event for cheap SQL filtering. ``event`` (full JSON) is stored
|
|
22
23
|
# alongside these and is the source of truth; these are convenience projections (NULL if absent).
|
|
23
24
|
_COLUMNS = ("log_id", "trace_id", "span_id", "timestamp", "level", "function")
|
|
24
25
|
|
|
25
|
-
# A table name is a config value, not untrusted input, but it is interpolated into DDL/DML (SQLite
|
|
26
|
-
# cannot parameterize identifiers), so restrict it to a plain SQL identifier to foreclose injection.
|
|
27
|
-
_IDENTIFIER = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
28
|
-
|
|
29
26
|
|
|
30
27
|
class SQLiteSink:
|
|
31
28
|
"""A :class:`~log_foundry.sinks.base.Sink` that batch-inserts events into a SQLite table.
|
|
@@ -49,9 +46,7 @@ class SQLiteSink:
|
|
|
49
46
|
connection: sqlite3.Connection | None = None,
|
|
50
47
|
create_table: bool = True,
|
|
51
48
|
) -> None:
|
|
52
|
-
|
|
53
|
-
raise ValueError(f"invalid table name {table!r}; expected a plain SQL identifier")
|
|
54
|
-
self._table = table
|
|
49
|
+
self._table = valid_identifier(table)
|
|
55
50
|
self._owns_connection = connection is None
|
|
56
51
|
# check_same_thread=False: the background worker (a different thread than the one that ran
|
|
57
52
|
# configure()) is the sole writer, so SQLite's same-thread guard would only get in the way
|
|
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
|