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