log-foundry 0.9.1.dev1__tar.gz → 0.9.1.dev2__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.dev2}/PKG-INFO +1 -1
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/pyproject.toml +8 -3
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/http.py +12 -1
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/sqlite.py +3 -8
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/LICENSE +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/README.md +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/api.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/config.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/console.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/context.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/decorator.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/model.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/base.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/file.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/filtering.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/logstash.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/multi.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/sentry.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/syslog.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/transform.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/src/log_foundry/sinks/util.py +0 -0
- {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev2}/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.dev2"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
aws = ["boto3>=1.43.61"] # SQSSink, SNSSink, KinesisSink, FirehoseSink
|
|
@@ -138,8 +138,6 @@ ignore = [
|
|
|
138
138
|
# behind an import.
|
|
139
139
|
"PERF203", # try/except inside a loop is deliberate in the worker's drain: one bad batch
|
|
140
140
|
# 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
141
|
"UP047", # PEP 695 `def f[T](...)`. Valid on the 3.12 floor, but rewriting the public
|
|
144
142
|
# `@trace` decorator's generics is a change to the signature users read, and
|
|
145
143
|
# the TypeVar form is not deprecated. Its own PR if ever.
|
|
@@ -154,6 +152,13 @@ ignore = [
|
|
|
154
152
|
# FileSink and RotatingFileSink hold a handle open ACROSS calls by design — that is what makes
|
|
155
153
|
# them a sink rather than a per-event open(). A context manager would close it after one emit.
|
|
156
154
|
"src/log_foundry/sinks/file.py" = ["SIM115"]
|
|
155
|
+
# These two build an INSERT around a table name because SQL cannot parameterize an identifier.
|
|
156
|
+
# The name is run through `valid_identifier` at construction, so it is a plain SQL identifier by
|
|
157
|
+
# the time it reaches the f-string, and every VALUE is parameterised. Scoped to these two files
|
|
158
|
+
# rather than ignored repository-wide: a future sink that builds SQL less carefully should still
|
|
159
|
+
# trip this rule.
|
|
160
|
+
"src/log_foundry/sinks/postgres.py" = ["S608"]
|
|
161
|
+
"src/log_foundry/sinks/sqlite.py" = ["S608"]
|
|
157
162
|
|
|
158
163
|
[tool.mypy]
|
|
159
164
|
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
|