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.
Files changed (53) hide show
  1. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/PKG-INFO +1 -1
  2. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/pyproject.toml +27 -3
  3. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/http.py +12 -1
  4. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sqlite.py +3 -8
  5. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/LICENSE +0 -0
  6. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/README.md +0 -0
  7. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/__init__.py +0 -0
  8. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/api.py +0 -0
  9. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/config.py +0 -0
  10. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/console.py +0 -0
  11. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/context.py +0 -0
  12. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/decorator.py +0 -0
  13. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/ids.py +0 -0
  14. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/model.py +0 -0
  15. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/py.typed +0 -0
  16. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sanitize.py +0 -0
  17. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/__init__.py +0 -0
  18. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_batch.py +0 -0
  19. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_chunk.py +0 -0
  20. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_socket.py +0 -0
  21. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/_time.py +0 -0
  22. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/base.py +0 -0
  23. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/callback.py +0 -0
  24. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/clickhouse.py +0 -0
  25. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/datadog.py +0 -0
  26. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/elasticsearch.py +0 -0
  27. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/eventhubs.py +0 -0
  28. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/file.py +0 -0
  29. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/filtering.py +0 -0
  30. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/firehose.py +0 -0
  31. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/honeycomb.py +0 -0
  32. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/kafka.py +0 -0
  33. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/kinesis.py +0 -0
  34. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/logging_sink.py +0 -0
  35. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/logstash.py +0 -0
  36. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/loki.py +0 -0
  37. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/mongodb.py +0 -0
  38. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/multi.py +0 -0
  39. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/nats.py +0 -0
  40. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/newrelic.py +0 -0
  41. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/postgres.py +0 -0
  42. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/pubsub.py +0 -0
  43. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/rabbitmq.py +0 -0
  44. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/redis.py +0 -0
  45. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sentry.py +0 -0
  46. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sns.py +0 -0
  47. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/splunk.py +0 -0
  48. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/sqs.py +0 -0
  49. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/stdout.py +0 -0
  50. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/syslog.py +0 -0
  51. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/transform.py +0 -0
  52. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/sinks/util.py +0 -0
  53. {log_foundry-0.9.1.dev1 → log_foundry-0.9.1.dev3}/src/log_foundry/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: log-foundry
3
- Version: 0.9.1.dev1
3
+ Version: 0.9.1.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
@@ -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.dev1"
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
- if not _IDENTIFIER.match(table):
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