log-foundry 0.1.1.dev1__tar.gz → 0.1.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 (51) hide show
  1. {log_foundry-0.1.1.dev1 → log_foundry-0.1.1.dev2}/PKG-INFO +75 -66
  2. {log_foundry-0.1.1.dev1 → log_foundry-0.1.1.dev2}/README.md +74 -65
  3. {log_foundry-0.1.1.dev1 → log_foundry-0.1.1.dev2}/pyproject.toml +7 -6
  4. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/__init__.py +6 -6
  5. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/api.py +7 -7
  6. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/config.py +3 -3
  7. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/console.py +1 -1
  8. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/context.py +4 -4
  9. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/decorator.py +5 -5
  10. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/model.py +2 -2
  11. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/_socket.py +1 -1
  12. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/callback.py +2 -2
  13. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/clickhouse.py +3 -3
  14. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/datadog.py +3 -3
  15. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/elasticsearch.py +3 -3
  16. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/eventhubs.py +3 -3
  17. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/file.py +2 -2
  18. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/filtering.py +2 -2
  19. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/firehose.py +5 -5
  20. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/honeycomb.py +2 -2
  21. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/http.py +2 -2
  22. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/kafka.py +2 -2
  23. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/kinesis.py +5 -5
  24. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/logging_sink.py +2 -2
  25. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/logstash.py +4 -4
  26. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/loki.py +3 -3
  27. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/mongodb.py +4 -4
  28. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/multi.py +4 -4
  29. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/nats.py +2 -2
  30. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/newrelic.py +2 -2
  31. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/postgres.py +3 -3
  32. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/pubsub.py +2 -2
  33. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/rabbitmq.py +2 -2
  34. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/redis.py +1 -1
  35. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/sentry.py +7 -7
  36. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/sns.py +4 -4
  37. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/splunk.py +4 -4
  38. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/sqlite.py +1 -1
  39. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/sqs.py +4 -4
  40. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/syslog.py +5 -5
  41. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/transform.py +2 -2
  42. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/util.py +4 -4
  43. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/worker.py +3 -3
  44. {log_foundry-0.1.1.dev1 → log_foundry-0.1.1.dev2}/LICENSE +0 -0
  45. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/ids.py +0 -0
  46. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/py.typed +0 -0
  47. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/__init__.py +0 -0
  48. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/_chunk.py +0 -0
  49. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/_time.py +0 -0
  50. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/base.py +0 -0
  51. {log_foundry-0.1.1.dev1/src/log_forge → log_foundry-0.1.1.dev2/src/log_foundry}/sinks/stdout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: log-foundry
3
- Version: 0.1.1.dev1
3
+ Version: 0.1.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
@@ -33,13 +33,13 @@ Requires-Dist: redis (>=5.0) ; extra == "redis"
33
33
  Requires-Dist: sentry-sdk (>=2.0) ; extra == "sentry"
34
34
  Description-Content-Type: text/markdown
35
35
 
36
- # log-forge
36
+ # log-foundry
37
37
 
38
38
  Consistent, structured (JSON) logs for every decorated function call — correlated by shared
39
39
  trace/span IDs, ready to ship to any of 30-plus built-in sinks (stdout by default; SQS → ELK is
40
40
  the headline production path).
41
41
 
42
- `log-forge` owns the **logs** pillar of observability. You decorate a function with `@trace`;
42
+ `log-foundry` owns the **logs** pillar of observability. You decorate a function with `@trace`;
43
43
  it emits one identically-shaped JSON record when the call starts and another when it ends
44
44
  (with duration and status), stitched together by W3C-compatible trace and span IDs so nested
45
45
  calls form a tree you can query later.
@@ -67,16 +67,25 @@ pip install log-foundry # core, zero dependencies
67
67
  pip install 'log-foundry[aws]' # + boto3 for the SQS/SNS/Kinesis/Firehose sinks
68
68
  ```
69
69
 
70
- > **The install name and the import name differ.** You `pip install log-foundry`, then
71
- > `import log_forge`. PyPI rejects `log-forge` as too similar to the unrelated, pre-existing
70
+ > **Renamed in 0.2.0: `log_forge` `log_foundry`.** The import package now matches the
71
+ > distribution name `pip install log-foundry`, then `import log_foundry`. If you are on
72
+ > `0.1.x`, update your imports; there is no compatibility shim. The project was originally
73
+ > called *log-forge*, but PyPI rejects that name as too similar to the unrelated, pre-existing
72
74
  > [`logforge`](https://pypi.org/project/logforge/) project — its similarity check collapses
73
- > separators, so `log-forge` and `logforge` count as the same name. Only the distribution name
74
- > moved; no module or API was renamed.
75
+ > separators, so `log-forge` and `logforge` count as the same name. Rather than keep a
76
+ > distribution and an import name that disagreed, everything is now `log-foundry` /
77
+ > `log_foundry`.
78
+ >
79
+ > Migrating from `0.1.x` is a find-and-replace on `log_forge` → `log_foundry`; no module moved
80
+ > and no public API changed. A handful of *emitted* defaults carry the name and shift with it:
81
+ > `LoggingSink`'s default logger (`logging.getLogger("log_foundry")`), `SyslogSink(app_name=…)`,
82
+ > `SplunkHECSink(source=…)`, Datadog's `ddsource`, and Sentry's client tag. Override them
83
+ > explicitly if a downstream query or dashboard pins the old string.
75
84
 
76
85
  ```python
77
- import log_forge
86
+ import log_foundry
78
87
 
79
- print(log_forge.__version__) # the installed version
88
+ print(log_foundry.__version__) # the installed version
80
89
  ```
81
90
 
82
91
  To work on the library itself, install from a clone:
@@ -111,7 +120,7 @@ Loki, Logstash, Syslog, Datadog, Splunk, New Relic, Honeycomb) — need **no** e
111
120
  ## Quickstart
112
121
 
113
122
  ```python
114
- import log_forge as lf
123
+ import log_foundry as lf
115
124
 
116
125
  # Call once at startup. These values are stamped onto every event.
117
126
  lf.configure(service="billing-api", version="1.4.2", env="prod")
@@ -143,7 +152,7 @@ with the child span pointing at its parent via `parent_span_id`:
143
152
 
144
153
  ## How it works
145
154
 
146
- ![log-forge pipeline: a traced call opens a span, gathers events, then closes and hands off to a background worker that batches events and ships them to a sink. Steps 1–4 run on your thread; the worker and sink run on a background thread. Support modules — config, ids, model, context, console — assist every step.](docs/assets/pipeline.svg)
155
+ ![log-foundry pipeline: a traced call opens a span, gathers events, then closes and hands off to a background worker that batches events and ships them to a sink. Steps 1–4 run on your thread; the worker and sink run on a background thread. Support modules — config, ids, model, context, console — assist every step.](docs/assets/pipeline.svg)
147
156
 
148
157
  A traced call travels through a small pipeline. The first four steps run on your own thread
149
158
  and are deliberately fast; the last two run on a background thread so your code never waits on
@@ -295,13 +304,13 @@ class Sink(Protocol):
295
304
 
296
305
  Wire one up by passing an instance to `configure(sink=...)`; if you never do, the first decorated
297
306
  call falls back to `StdoutSink()`. Sinks are **not** re-exported at the top level — import each
298
- from its own module, e.g. `from log_forge.sinks.sqs import SQSSink`.
307
+ from its own module, e.g. `from log_foundry.sinks.sqs import SQSSink`.
299
308
 
300
309
  A few conventions hold across every sink below:
301
310
 
302
311
  - **Extras.** The core is dependency-free. A sink built on a third-party client sits behind the
303
312
  optional extra named in its table (blank = zero-dependency, stdlib only); the client is imported
304
- lazily, so `import log_forge.sinks.<x>` never fails for a missing dependency — only *constructing*
313
+ lazily, so `import log_foundry.sinks.<x>` never fails for a missing dependency — only *constructing*
305
314
  the sink without an injected client does. See [Optional extras](#optional-extras).
306
315
  - **Injection.** Sinks backed by an external resource accept an injected client/connection/stream
307
316
  (`client=`, `connection=`, `producer=`, `stream=`, `opener=`) for testing or bespoke configuration.
@@ -315,13 +324,13 @@ A few conventions hold across every sink below:
315
324
 
316
325
  | Sink | Import from | Configure |
317
326
  |---|---|---|
318
- | `StdoutSink` | `log_forge.sinks.stdout` | `StdoutSink(stream=sys.stdout)` — one JSON line per event; the zero-config default |
319
- | `StderrSink` | `log_forge.sinks.util` | `StderrSink(stream=sys.stderr)` — same, on stderr (twelve-factor) |
320
- | `NullSink` | `log_forge.sinks.util` | `NullSink()` — discard everything; `.dropped` counts events |
321
- | `MemorySink` | `log_forge.sinks.util` | `MemorySink(maxlen=None)` — collect into `.events` (a bounded ring when `maxlen` is set) |
327
+ | `StdoutSink` | `log_foundry.sinks.stdout` | `StdoutSink(stream=sys.stdout)` — one JSON line per event; the zero-config default |
328
+ | `StderrSink` | `log_foundry.sinks.util` | `StderrSink(stream=sys.stderr)` — same, on stderr (twelve-factor) |
329
+ | `NullSink` | `log_foundry.sinks.util` | `NullSink()` — discard everything; `.dropped` counts events |
330
+ | `MemorySink` | `log_foundry.sinks.util` | `MemorySink(maxlen=None)` — collect into `.events` (a bounded ring when `maxlen` is set) |
322
331
 
323
332
  ```python
324
- from log_forge.sinks.stdout import StdoutSink
333
+ from log_foundry.sinks.stdout import StdoutSink
325
334
  lf.configure(sink=StdoutSink()) # explicit; also the zero-config default
326
335
  ```
327
336
 
@@ -332,16 +341,16 @@ to a plain callable.
332
341
 
333
342
  | Sink | Import from | Configure |
334
343
  |---|---|---|
335
- | `MultiSink` | `log_forge.sinks.multi` | `MultiSink(*sinks)` — forward each batch to every child; a failing child is isolated and counted on `.failed` |
336
- | `FilteringSink` | `log_forge.sinks.filtering` | `FilteringSink(inner, *, predicate=None, min_level=None)` — forward only events passing `predicate` and/or at/above `min_level` |
337
- | `TransformSink` | `log_forge.sinks.transform` | `TransformSink(inner, fn)` — map each event through `fn` before forwarding; return `None` to drop one |
338
- | `CallbackSink` | `log_forge.sinks.callback` | `CallbackSink(fn, *, on_close=None)` — hand each batch to any callable |
344
+ | `MultiSink` | `log_foundry.sinks.multi` | `MultiSink(*sinks)` — forward each batch to every child; a failing child is isolated and counted on `.failed` |
345
+ | `FilteringSink` | `log_foundry.sinks.filtering` | `FilteringSink(inner, *, predicate=None, min_level=None)` — forward only events passing `predicate` and/or at/above `min_level` |
346
+ | `TransformSink` | `log_foundry.sinks.transform` | `TransformSink(inner, fn)` — map each event through `fn` before forwarding; return `None` to drop one |
347
+ | `CallbackSink` | `log_foundry.sinks.callback` | `CallbackSink(fn, *, on_close=None)` — hand each batch to any callable |
339
348
 
340
349
  ```python
341
- from log_forge.sinks.multi import MultiSink
342
- from log_forge.sinks.filtering import FilteringSink
343
- from log_forge.sinks.stdout import StdoutSink
344
- from log_forge.sinks.sqs import SQSSink
350
+ from log_foundry.sinks.multi import MultiSink
351
+ from log_foundry.sinks.filtering import FilteringSink
352
+ from log_foundry.sinks.stdout import StdoutSink
353
+ from log_foundry.sinks.sqs import SQSSink
345
354
 
346
355
  lf.configure(sink=MultiSink(
347
356
  StdoutSink(), # echo everything locally
@@ -356,9 +365,9 @@ level is unknown or missing fails open (is forwarded).
356
365
 
357
366
  | Sink | Import from | Configure |
358
367
  |---|---|---|
359
- | `LoggingSink` | `log_forge.sinks.logging_sink` | `LoggingSink(logger=None, *, default_level="INFO")` — emit each event as a `logging.LogRecord` |
368
+ | `LoggingSink` | `log_foundry.sinks.logging_sink` | `LoggingSink(logger=None, *, default_level="INFO")` — emit each event as a `logging.LogRecord` |
360
369
 
361
- Hands every event to a `logging.Logger` (default `logging.getLogger("log_forge")`) so your existing
370
+ Hands every event to a `logging.Logger` (default `logging.getLogger("log_foundry")`) so your existing
362
371
  handlers, formatters, and `logging.config` apply. Identity fields and the nested `fields` are
363
372
  attached to each record; the sink never configures or tears down logging itself.
364
373
 
@@ -366,9 +375,9 @@ attached to each record; the sink never configures or tears down logging itself.
366
375
 
367
376
  | Sink | Import from | Configure |
368
377
  |---|---|---|
369
- | `FileSink` | `log_forge.sinks.file` | `FileSink(path, *, encoding="utf-8")` — append NDJSON to one file |
370
- | `RotatingFileSink` | `log_forge.sinks.file` | `RotatingFileSink(path, *, max_bytes=0, backup_count=0, when=None, interval=1)` — rotate by size and/or time, keeping `backup_count` numbered backups |
371
- | `SQLiteSink` | `log_forge.sinks.sqlite` | `SQLiteSink(database, *, table="log_events", create_table=True)` — batch-insert into an embedded SQLite DB |
378
+ | `FileSink` | `log_foundry.sinks.file` | `FileSink(path, *, encoding="utf-8")` — append NDJSON to one file |
379
+ | `RotatingFileSink` | `log_foundry.sinks.file` | `RotatingFileSink(path, *, max_bytes=0, backup_count=0, when=None, interval=1)` — rotate by size and/or time, keeping `backup_count` numbered backups |
380
+ | `SQLiteSink` | `log_foundry.sinks.sqlite` | `SQLiteSink(database, *, table="log_events", create_table=True)` — batch-insert into an embedded SQLite DB |
372
381
 
373
382
  `RotatingFileSink`'s time trigger uses a `when` unit code — `"S"`/`"M"`/`"H"`/`"D"` — times `interval`
374
383
  (either trigger, or both, can be enabled). `SQLiteSink` stores each event as full JSON plus projected
@@ -376,7 +385,7 @@ attached to each record; the sink never configures or tears down logging itself.
376
385
  you provision the table yourself.
377
386
 
378
387
  ```python
379
- from log_forge.sinks.file import RotatingFileSink
388
+ from log_foundry.sinks.file import RotatingFileSink
380
389
  lf.configure(sink=RotatingFileSink("app.log.jsonl", max_bytes=10_000_000, backup_count=5))
381
390
  ```
382
391
 
@@ -388,15 +397,15 @@ to `HTTPSink` (`headers=`, `auth=`, `gzip=`, `timeout=`, `max_retries=`).
388
397
 
389
398
  | Sink | Import from | Configure |
390
399
  |---|---|---|
391
- | `HTTPSink` | `log_forge.sinks.http` | `HTTPSink(url, *, method="POST", headers=None, auth=None, body_format="ndjson", timeout=5.0, gzip=False, max_retries=3)` — generic POST. `auth` is a bearer-token `str` or `(user, pass)` for basic; `body_format` is `"ndjson"` or `"json_array"` |
392
- | `ElasticsearchSink` | `log_forge.sinks.elasticsearch` | `ElasticsearchSink(url, *, index, auth=None, **http_kwargs)` — POST to `_bulk`, parsing per-item errors (`.item_errors`) |
393
- | `OpenSearchSink` | `log_forge.sinks.elasticsearch` | same signature as `ElasticsearchSink` (identical bulk protocol) |
394
- | `LokiSink` | `log_forge.sinks.loki` | `LokiSink(url, *, labels=("service", "env", "level"), **http_kwargs)` — Grafana Loki push API |
395
- | `LogstashSink` | `log_forge.sinks.logstash` | `LogstashSink(url=…, **http_kwargs)` for HTTP, **or** `LogstashSink(host=…, port=…, transport="tcp")` for a raw TCP/UDP socket |
396
- | `SyslogSink` | `log_forge.sinks.syslog` | `SyslogSink(host, port=514, *, transport="udp", facility="user", app_name="log-forge")` — RFC 5424 over UDP/TCP |
400
+ | `HTTPSink` | `log_foundry.sinks.http` | `HTTPSink(url, *, method="POST", headers=None, auth=None, body_format="ndjson", timeout=5.0, gzip=False, max_retries=3)` — generic POST. `auth` is a bearer-token `str` or `(user, pass)` for basic; `body_format` is `"ndjson"` or `"json_array"` |
401
+ | `ElasticsearchSink` | `log_foundry.sinks.elasticsearch` | `ElasticsearchSink(url, *, index, auth=None, **http_kwargs)` — POST to `_bulk`, parsing per-item errors (`.item_errors`) |
402
+ | `OpenSearchSink` | `log_foundry.sinks.elasticsearch` | same signature as `ElasticsearchSink` (identical bulk protocol) |
403
+ | `LokiSink` | `log_foundry.sinks.loki` | `LokiSink(url, *, labels=("service", "env", "level"), **http_kwargs)` — Grafana Loki push API |
404
+ | `LogstashSink` | `log_foundry.sinks.logstash` | `LogstashSink(url=…, **http_kwargs)` for HTTP, **or** `LogstashSink(host=…, port=…, transport="tcp")` for a raw TCP/UDP socket |
405
+ | `SyslogSink` | `log_foundry.sinks.syslog` | `SyslogSink(host, port=514, *, transport="udp", facility="user", app_name="log-foundry")` — RFC 5424 over UDP/TCP |
397
406
 
398
407
  ```python
399
- from log_forge.sinks.elasticsearch import ElasticsearchSink
408
+ from log_foundry.sinks.elasticsearch import ElasticsearchSink
400
409
  lf.configure(sink=ElasticsearchSink("https://es.internal:9200", index="app-logs",
401
410
  auth=("elastic", "…")))
402
411
  ```
@@ -408,11 +417,11 @@ Also HTTP-based. All are zero-dependency **except** `SentrySink`, which prefers
408
417
 
409
418
  | Sink | Import from | Extra | Configure |
410
419
  |---|---|---|---|
411
- | `DatadogSink` | `log_forge.sinks.datadog` | — | `DatadogSink(api_key, *, site="datadoghq.com", service=None, ddtags=None)` |
412
- | `SplunkHECSink` | `log_forge.sinks.splunk` | — | `SplunkHECSink(url, token, *, host=None, source="log-forge")` — HTTP Event Collector |
413
- | `NewRelicSink` | `log_forge.sinks.newrelic` | — | `NewRelicSink(api_key, *, region="US")` — `region` is `"US"` or `"EU"` |
414
- | `HoneycombSink` | `log_forge.sinks.honeycomb` | — | `HoneycombSink(api_key, dataset, *, url="https://api.honeycomb.io")` |
415
- | `SentrySink` | `log_forge.sinks.sentry` | `sentry` | `SentrySink(dsn=None, *, min_level="ERROR")` — sends only `min_level`+ events |
420
+ | `DatadogSink` | `log_foundry.sinks.datadog` | — | `DatadogSink(api_key, *, site="datadoghq.com", service=None, ddtags=None)` |
421
+ | `SplunkHECSink` | `log_foundry.sinks.splunk` | — | `SplunkHECSink(url, token, *, host=None, source="log-foundry")` — HTTP Event Collector |
422
+ | `NewRelicSink` | `log_foundry.sinks.newrelic` | — | `NewRelicSink(api_key, *, region="US")` — `region` is `"US"` or `"EU"` |
423
+ | `HoneycombSink` | `log_foundry.sinks.honeycomb` | — | `HoneycombSink(api_key, dataset, *, url="https://api.honeycomb.io")` |
424
+ | `SentrySink` | `log_foundry.sinks.sentry` | `sentry` | `SentrySink(dsn=None, *, min_level="ERROR")` — sends only `min_level`+ events |
416
425
 
417
426
  With the `sentry` extra installed, `SentrySink` captures via `sentry_sdk.capture_event` (initialize
418
427
  the SDK yourself with `sentry_sdk.init(...)`); without it, pass `dsn=` and events are POSTed as
@@ -421,19 +430,19 @@ Sentry envelopes over HTTP.
421
430
  #### AWS — the durable-buffer path (`aws` extra)
422
431
 
423
432
  `pip install 'log-foundry[aws]'` (pulls `boto3`). Credentials and region come from boto3's standard
424
- chain — log-forge adds none of its own. Each re-chunks every batch to the service's hard per-request
433
+ chain — log-foundry adds none of its own. Each re-chunks every batch to the service's hard per-request
425
434
  limits, retries partial failures, and drops any single event too large to ever fit (counted on
426
435
  `.dropped_oversized`).
427
436
 
428
437
  | Sink | Import from | Configure |
429
438
  |---|---|---|
430
- | `SQSSink` | `log_forge.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
431
- | `SNSSink` | `log_forge.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
432
- | `KinesisSink` | `log_forge.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
433
- | `FirehoseSink` | `log_forge.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
439
+ | `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
440
+ | `SNSSink` | `log_foundry.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
441
+ | `KinesisSink` | `log_foundry.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
442
+ | `FirehoseSink` | `log_foundry.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
434
443
 
435
444
  ```python
436
- from log_forge.sinks.sqs import SQSSink
445
+ from log_foundry.sinks.sqs import SQSSink
437
446
  lf.configure(service="payments",
438
447
  sink=SQSSink(queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/logs"))
439
448
  ```
@@ -446,16 +455,16 @@ Each needs its own extra (lazy-imported). All publish + retry within a bound and
446
455
 
447
456
  | Sink | Import from | Extra | Configure |
448
457
  |---|---|---|---|
449
- | `KafkaSink` | `log_forge.sinks.kafka` | `kafka` | `KafkaSink(topic, *, bootstrap_servers="…", key_field="trace_id")` |
450
- | `RedisStreamsSink` | `log_forge.sinks.redis` | `redis` | `RedisStreamsSink(stream, *, url=None)` — `XADD` |
451
- | `RedisListSink` | `log_forge.sinks.redis` | `redis` | `RedisListSink(key, *, url=None)` — `RPUSH` |
452
- | `RabbitMQSink` | `log_forge.sinks.rabbitmq` | `amqp` | `RabbitMQSink(*, exchange, routing_key, url=None)` — persistent messages |
453
- | `NATSSink` | `log_forge.sinks.nats` | `nats` | `NATSSink(subject, *, jetstream=False, servers=None)` |
454
- | `GooglePubSubSink` | `log_forge.sinks.pubsub` | `gcp-pubsub` | `GooglePubSubSink(topic)` |
455
- | `AzureEventHubsSink` | `log_forge.sinks.eventhubs` | `azure-eventhubs` | `AzureEventHubsSink(*, connection_str="…", eventhub=None)` |
458
+ | `KafkaSink` | `log_foundry.sinks.kafka` | `kafka` | `KafkaSink(topic, *, bootstrap_servers="…", key_field="trace_id")` |
459
+ | `RedisStreamsSink` | `log_foundry.sinks.redis` | `redis` | `RedisStreamsSink(stream, *, url=None)` — `XADD` |
460
+ | `RedisListSink` | `log_foundry.sinks.redis` | `redis` | `RedisListSink(key, *, url=None)` — `RPUSH` |
461
+ | `RabbitMQSink` | `log_foundry.sinks.rabbitmq` | `amqp` | `RabbitMQSink(*, exchange, routing_key, url=None)` — persistent messages |
462
+ | `NATSSink` | `log_foundry.sinks.nats` | `nats` | `NATSSink(subject, *, jetstream=False, servers=None)` |
463
+ | `GooglePubSubSink` | `log_foundry.sinks.pubsub` | `gcp-pubsub` | `GooglePubSubSink(topic)` |
464
+ | `AzureEventHubsSink` | `log_foundry.sinks.eventhubs` | `azure-eventhubs` | `AzureEventHubsSink(*, connection_str="…", eventhub=None)` |
456
465
 
457
466
  ```python
458
- from log_forge.sinks.kafka import KafkaSink
467
+ from log_foundry.sinks.kafka import KafkaSink
459
468
  lf.configure(sink=KafkaSink("app-logs", bootstrap_servers="broker:9092"))
460
469
  ```
461
470
 
@@ -465,9 +474,9 @@ Write-only inserts (querying is the downstream tool's job); each needs its own e
465
474
 
466
475
  | Sink | Import from | Extra | Configure |
467
476
  |---|---|---|---|
468
- | `MongoDBSink` | `log_forge.sinks.mongodb` | `mongo` | `MongoDBSink(*, uri="…", database="…", collection="…")` |
469
- | `PostgresSink` | `log_forge.sinks.postgres` | `postgres` | `PostgresSink(table, *, dsn="…", create_table=False)` — JSONB `event` column + extracted columns |
470
- | `ClickHouseSink` | `log_forge.sinks.clickhouse` | `clickhouse` | `ClickHouseSink(table, *, dsn="…", create_table=False)` — MergeTree, columnar insert |
477
+ | `MongoDBSink` | `log_foundry.sinks.mongodb` | `mongo` | `MongoDBSink(*, uri="…", database="…", collection="…")` |
478
+ | `PostgresSink` | `log_foundry.sinks.postgres` | `postgres` | `PostgresSink(table, *, dsn="…", create_table=False)` — JSONB `event` column + extracted columns |
479
+ | `ClickHouseSink` | `log_foundry.sinks.clickhouse` | `clickhouse` | `ClickHouseSink(table, *, dsn="…", create_table=False)` — MergeTree, columnar insert |
471
480
 
472
481
  `PostgresSink` / `ClickHouseSink` default `create_table=False` (you own the schema and indexes); set
473
482
  it `True` for an idempotent `CREATE TABLE IF NOT EXISTS` convenience.
@@ -487,7 +496,7 @@ them (`worker.dropped`) rather than stalling.
487
496
  Because delivery is asynchronous, drain before the process exits:
488
497
 
489
498
  ```python
490
- import log_forge as lf
499
+ import log_foundry as lf
491
500
 
492
501
  lf.shutdown() # flush buffered events and close the sink; blocks until drained
493
502
  ```
@@ -532,7 +541,7 @@ poetry run mypy # typecheck (strict, over src/)
532
541
  every pull request and on push to `main`. A second workflow
533
542
  ([`spec-lint.yml`](.github/workflows/spec-lint.yml)) lints the design specs under `docs/specs/`.
534
543
 
535
- The library uses a src layout (`src/log_forge/`) with a single concept per module: `config`,
544
+ The library uses a src layout (`src/log_foundry/`) with a single concept per module: `config`,
536
545
  `ids`, `model`, `context`, `decorator`, `api`, `console`, `worker`, and the `sinks/` package (the
537
546
  `base` protocol, `stdout`, and one module per sink family — see [Sinks](#sinks)).
538
547
  Deeper design docs live in [`docs/`](docs/) — start with [`docs/architecture.md`](docs/architecture.md).
@@ -548,8 +557,8 @@ sdist and a wheel:
548
557
 
549
558
  | Trigger | Version built | Published to PyPI as |
550
559
  |---|---|---|
551
- | merge to `main` | `0.1.1.devN` | dev pre-release |
552
- | push tag `v0.1.0` | `0.1.0` | stable release |
560
+ | merge to `main` | `X.Y.Z.devN` | dev pre-release |
561
+ | push tag `vX.Y.Z` | `X.Y.Z` | stable release |
553
562
 
554
563
  Dev pre-releases keep the upload path exercised on every merge, so a real release is never the
555
564
  first time it runs. `pip install log-foundry` still resolves to the latest **stable** version —
@@ -558,7 +567,7 @@ pip ignores pre-releases unless you pass `--pre`.
558
567
  Cutting a release is one tag:
559
568
 
560
569
  ```bash
561
- git tag -a v0.2.0 -m "log-forge 0.2.0"
570
+ git tag -a v0.2.0 -m "log-foundry 0.2.0"
562
571
  git push origin v0.2.0
563
572
  ```
564
573
 
@@ -1,10 +1,10 @@
1
- # log-forge
1
+ # log-foundry
2
2
 
3
3
  Consistent, structured (JSON) logs for every decorated function call — correlated by shared
4
4
  trace/span IDs, ready to ship to any of 30-plus built-in sinks (stdout by default; SQS → ELK is
5
5
  the headline production path).
6
6
 
7
- `log-forge` owns the **logs** pillar of observability. You decorate a function with `@trace`;
7
+ `log-foundry` owns the **logs** pillar of observability. You decorate a function with `@trace`;
8
8
  it emits one identically-shaped JSON record when the call starts and another when it ends
9
9
  (with duration and status), stitched together by W3C-compatible trace and span IDs so nested
10
10
  calls form a tree you can query later.
@@ -32,16 +32,25 @@ pip install log-foundry # core, zero dependencies
32
32
  pip install 'log-foundry[aws]' # + boto3 for the SQS/SNS/Kinesis/Firehose sinks
33
33
  ```
34
34
 
35
- > **The install name and the import name differ.** You `pip install log-foundry`, then
36
- > `import log_forge`. PyPI rejects `log-forge` as too similar to the unrelated, pre-existing
35
+ > **Renamed in 0.2.0: `log_forge` `log_foundry`.** The import package now matches the
36
+ > distribution name `pip install log-foundry`, then `import log_foundry`. If you are on
37
+ > `0.1.x`, update your imports; there is no compatibility shim. The project was originally
38
+ > called *log-forge*, but PyPI rejects that name as too similar to the unrelated, pre-existing
37
39
  > [`logforge`](https://pypi.org/project/logforge/) project — its similarity check collapses
38
- > separators, so `log-forge` and `logforge` count as the same name. Only the distribution name
39
- > moved; no module or API was renamed.
40
+ > separators, so `log-forge` and `logforge` count as the same name. Rather than keep a
41
+ > distribution and an import name that disagreed, everything is now `log-foundry` /
42
+ > `log_foundry`.
43
+ >
44
+ > Migrating from `0.1.x` is a find-and-replace on `log_forge` → `log_foundry`; no module moved
45
+ > and no public API changed. A handful of *emitted* defaults carry the name and shift with it:
46
+ > `LoggingSink`'s default logger (`logging.getLogger("log_foundry")`), `SyslogSink(app_name=…)`,
47
+ > `SplunkHECSink(source=…)`, Datadog's `ddsource`, and Sentry's client tag. Override them
48
+ > explicitly if a downstream query or dashboard pins the old string.
40
49
 
41
50
  ```python
42
- import log_forge
51
+ import log_foundry
43
52
 
44
- print(log_forge.__version__) # the installed version
53
+ print(log_foundry.__version__) # the installed version
45
54
  ```
46
55
 
47
56
  To work on the library itself, install from a clone:
@@ -76,7 +85,7 @@ Loki, Logstash, Syslog, Datadog, Splunk, New Relic, Honeycomb) — need **no** e
76
85
  ## Quickstart
77
86
 
78
87
  ```python
79
- import log_forge as lf
88
+ import log_foundry as lf
80
89
 
81
90
  # Call once at startup. These values are stamped onto every event.
82
91
  lf.configure(service="billing-api", version="1.4.2", env="prod")
@@ -108,7 +117,7 @@ with the child span pointing at its parent via `parent_span_id`:
108
117
 
109
118
  ## How it works
110
119
 
111
- ![log-forge pipeline: a traced call opens a span, gathers events, then closes and hands off to a background worker that batches events and ships them to a sink. Steps 1–4 run on your thread; the worker and sink run on a background thread. Support modules — config, ids, model, context, console — assist every step.](docs/assets/pipeline.svg)
120
+ ![log-foundry pipeline: a traced call opens a span, gathers events, then closes and hands off to a background worker that batches events and ships them to a sink. Steps 1–4 run on your thread; the worker and sink run on a background thread. Support modules — config, ids, model, context, console — assist every step.](docs/assets/pipeline.svg)
112
121
 
113
122
  A traced call travels through a small pipeline. The first four steps run on your own thread
114
123
  and are deliberately fast; the last two run on a background thread so your code never waits on
@@ -260,13 +269,13 @@ class Sink(Protocol):
260
269
 
261
270
  Wire one up by passing an instance to `configure(sink=...)`; if you never do, the first decorated
262
271
  call falls back to `StdoutSink()`. Sinks are **not** re-exported at the top level — import each
263
- from its own module, e.g. `from log_forge.sinks.sqs import SQSSink`.
272
+ from its own module, e.g. `from log_foundry.sinks.sqs import SQSSink`.
264
273
 
265
274
  A few conventions hold across every sink below:
266
275
 
267
276
  - **Extras.** The core is dependency-free. A sink built on a third-party client sits behind the
268
277
  optional extra named in its table (blank = zero-dependency, stdlib only); the client is imported
269
- lazily, so `import log_forge.sinks.<x>` never fails for a missing dependency — only *constructing*
278
+ lazily, so `import log_foundry.sinks.<x>` never fails for a missing dependency — only *constructing*
270
279
  the sink without an injected client does. See [Optional extras](#optional-extras).
271
280
  - **Injection.** Sinks backed by an external resource accept an injected client/connection/stream
272
281
  (`client=`, `connection=`, `producer=`, `stream=`, `opener=`) for testing or bespoke configuration.
@@ -280,13 +289,13 @@ A few conventions hold across every sink below:
280
289
 
281
290
  | Sink | Import from | Configure |
282
291
  |---|---|---|
283
- | `StdoutSink` | `log_forge.sinks.stdout` | `StdoutSink(stream=sys.stdout)` — one JSON line per event; the zero-config default |
284
- | `StderrSink` | `log_forge.sinks.util` | `StderrSink(stream=sys.stderr)` — same, on stderr (twelve-factor) |
285
- | `NullSink` | `log_forge.sinks.util` | `NullSink()` — discard everything; `.dropped` counts events |
286
- | `MemorySink` | `log_forge.sinks.util` | `MemorySink(maxlen=None)` — collect into `.events` (a bounded ring when `maxlen` is set) |
292
+ | `StdoutSink` | `log_foundry.sinks.stdout` | `StdoutSink(stream=sys.stdout)` — one JSON line per event; the zero-config default |
293
+ | `StderrSink` | `log_foundry.sinks.util` | `StderrSink(stream=sys.stderr)` — same, on stderr (twelve-factor) |
294
+ | `NullSink` | `log_foundry.sinks.util` | `NullSink()` — discard everything; `.dropped` counts events |
295
+ | `MemorySink` | `log_foundry.sinks.util` | `MemorySink(maxlen=None)` — collect into `.events` (a bounded ring when `maxlen` is set) |
287
296
 
288
297
  ```python
289
- from log_forge.sinks.stdout import StdoutSink
298
+ from log_foundry.sinks.stdout import StdoutSink
290
299
  lf.configure(sink=StdoutSink()) # explicit; also the zero-config default
291
300
  ```
292
301
 
@@ -297,16 +306,16 @@ to a plain callable.
297
306
 
298
307
  | Sink | Import from | Configure |
299
308
  |---|---|---|
300
- | `MultiSink` | `log_forge.sinks.multi` | `MultiSink(*sinks)` — forward each batch to every child; a failing child is isolated and counted on `.failed` |
301
- | `FilteringSink` | `log_forge.sinks.filtering` | `FilteringSink(inner, *, predicate=None, min_level=None)` — forward only events passing `predicate` and/or at/above `min_level` |
302
- | `TransformSink` | `log_forge.sinks.transform` | `TransformSink(inner, fn)` — map each event through `fn` before forwarding; return `None` to drop one |
303
- | `CallbackSink` | `log_forge.sinks.callback` | `CallbackSink(fn, *, on_close=None)` — hand each batch to any callable |
309
+ | `MultiSink` | `log_foundry.sinks.multi` | `MultiSink(*sinks)` — forward each batch to every child; a failing child is isolated and counted on `.failed` |
310
+ | `FilteringSink` | `log_foundry.sinks.filtering` | `FilteringSink(inner, *, predicate=None, min_level=None)` — forward only events passing `predicate` and/or at/above `min_level` |
311
+ | `TransformSink` | `log_foundry.sinks.transform` | `TransformSink(inner, fn)` — map each event through `fn` before forwarding; return `None` to drop one |
312
+ | `CallbackSink` | `log_foundry.sinks.callback` | `CallbackSink(fn, *, on_close=None)` — hand each batch to any callable |
304
313
 
305
314
  ```python
306
- from log_forge.sinks.multi import MultiSink
307
- from log_forge.sinks.filtering import FilteringSink
308
- from log_forge.sinks.stdout import StdoutSink
309
- from log_forge.sinks.sqs import SQSSink
315
+ from log_foundry.sinks.multi import MultiSink
316
+ from log_foundry.sinks.filtering import FilteringSink
317
+ from log_foundry.sinks.stdout import StdoutSink
318
+ from log_foundry.sinks.sqs import SQSSink
310
319
 
311
320
  lf.configure(sink=MultiSink(
312
321
  StdoutSink(), # echo everything locally
@@ -321,9 +330,9 @@ level is unknown or missing fails open (is forwarded).
321
330
 
322
331
  | Sink | Import from | Configure |
323
332
  |---|---|---|
324
- | `LoggingSink` | `log_forge.sinks.logging_sink` | `LoggingSink(logger=None, *, default_level="INFO")` — emit each event as a `logging.LogRecord` |
333
+ | `LoggingSink` | `log_foundry.sinks.logging_sink` | `LoggingSink(logger=None, *, default_level="INFO")` — emit each event as a `logging.LogRecord` |
325
334
 
326
- Hands every event to a `logging.Logger` (default `logging.getLogger("log_forge")`) so your existing
335
+ Hands every event to a `logging.Logger` (default `logging.getLogger("log_foundry")`) so your existing
327
336
  handlers, formatters, and `logging.config` apply. Identity fields and the nested `fields` are
328
337
  attached to each record; the sink never configures or tears down logging itself.
329
338
 
@@ -331,9 +340,9 @@ attached to each record; the sink never configures or tears down logging itself.
331
340
 
332
341
  | Sink | Import from | Configure |
333
342
  |---|---|---|
334
- | `FileSink` | `log_forge.sinks.file` | `FileSink(path, *, encoding="utf-8")` — append NDJSON to one file |
335
- | `RotatingFileSink` | `log_forge.sinks.file` | `RotatingFileSink(path, *, max_bytes=0, backup_count=0, when=None, interval=1)` — rotate by size and/or time, keeping `backup_count` numbered backups |
336
- | `SQLiteSink` | `log_forge.sinks.sqlite` | `SQLiteSink(database, *, table="log_events", create_table=True)` — batch-insert into an embedded SQLite DB |
343
+ | `FileSink` | `log_foundry.sinks.file` | `FileSink(path, *, encoding="utf-8")` — append NDJSON to one file |
344
+ | `RotatingFileSink` | `log_foundry.sinks.file` | `RotatingFileSink(path, *, max_bytes=0, backup_count=0, when=None, interval=1)` — rotate by size and/or time, keeping `backup_count` numbered backups |
345
+ | `SQLiteSink` | `log_foundry.sinks.sqlite` | `SQLiteSink(database, *, table="log_events", create_table=True)` — batch-insert into an embedded SQLite DB |
337
346
 
338
347
  `RotatingFileSink`'s time trigger uses a `when` unit code — `"S"`/`"M"`/`"H"`/`"D"` — times `interval`
339
348
  (either trigger, or both, can be enabled). `SQLiteSink` stores each event as full JSON plus projected
@@ -341,7 +350,7 @@ attached to each record; the sink never configures or tears down logging itself.
341
350
  you provision the table yourself.
342
351
 
343
352
  ```python
344
- from log_forge.sinks.file import RotatingFileSink
353
+ from log_foundry.sinks.file import RotatingFileSink
345
354
  lf.configure(sink=RotatingFileSink("app.log.jsonl", max_bytes=10_000_000, backup_count=5))
346
355
  ```
347
356
 
@@ -353,15 +362,15 @@ to `HTTPSink` (`headers=`, `auth=`, `gzip=`, `timeout=`, `max_retries=`).
353
362
 
354
363
  | Sink | Import from | Configure |
355
364
  |---|---|---|
356
- | `HTTPSink` | `log_forge.sinks.http` | `HTTPSink(url, *, method="POST", headers=None, auth=None, body_format="ndjson", timeout=5.0, gzip=False, max_retries=3)` — generic POST. `auth` is a bearer-token `str` or `(user, pass)` for basic; `body_format` is `"ndjson"` or `"json_array"` |
357
- | `ElasticsearchSink` | `log_forge.sinks.elasticsearch` | `ElasticsearchSink(url, *, index, auth=None, **http_kwargs)` — POST to `_bulk`, parsing per-item errors (`.item_errors`) |
358
- | `OpenSearchSink` | `log_forge.sinks.elasticsearch` | same signature as `ElasticsearchSink` (identical bulk protocol) |
359
- | `LokiSink` | `log_forge.sinks.loki` | `LokiSink(url, *, labels=("service", "env", "level"), **http_kwargs)` — Grafana Loki push API |
360
- | `LogstashSink` | `log_forge.sinks.logstash` | `LogstashSink(url=…, **http_kwargs)` for HTTP, **or** `LogstashSink(host=…, port=…, transport="tcp")` for a raw TCP/UDP socket |
361
- | `SyslogSink` | `log_forge.sinks.syslog` | `SyslogSink(host, port=514, *, transport="udp", facility="user", app_name="log-forge")` — RFC 5424 over UDP/TCP |
365
+ | `HTTPSink` | `log_foundry.sinks.http` | `HTTPSink(url, *, method="POST", headers=None, auth=None, body_format="ndjson", timeout=5.0, gzip=False, max_retries=3)` — generic POST. `auth` is a bearer-token `str` or `(user, pass)` for basic; `body_format` is `"ndjson"` or `"json_array"` |
366
+ | `ElasticsearchSink` | `log_foundry.sinks.elasticsearch` | `ElasticsearchSink(url, *, index, auth=None, **http_kwargs)` — POST to `_bulk`, parsing per-item errors (`.item_errors`) |
367
+ | `OpenSearchSink` | `log_foundry.sinks.elasticsearch` | same signature as `ElasticsearchSink` (identical bulk protocol) |
368
+ | `LokiSink` | `log_foundry.sinks.loki` | `LokiSink(url, *, labels=("service", "env", "level"), **http_kwargs)` — Grafana Loki push API |
369
+ | `LogstashSink` | `log_foundry.sinks.logstash` | `LogstashSink(url=…, **http_kwargs)` for HTTP, **or** `LogstashSink(host=…, port=…, transport="tcp")` for a raw TCP/UDP socket |
370
+ | `SyslogSink` | `log_foundry.sinks.syslog` | `SyslogSink(host, port=514, *, transport="udp", facility="user", app_name="log-foundry")` — RFC 5424 over UDP/TCP |
362
371
 
363
372
  ```python
364
- from log_forge.sinks.elasticsearch import ElasticsearchSink
373
+ from log_foundry.sinks.elasticsearch import ElasticsearchSink
365
374
  lf.configure(sink=ElasticsearchSink("https://es.internal:9200", index="app-logs",
366
375
  auth=("elastic", "…")))
367
376
  ```
@@ -373,11 +382,11 @@ Also HTTP-based. All are zero-dependency **except** `SentrySink`, which prefers
373
382
 
374
383
  | Sink | Import from | Extra | Configure |
375
384
  |---|---|---|---|
376
- | `DatadogSink` | `log_forge.sinks.datadog` | — | `DatadogSink(api_key, *, site="datadoghq.com", service=None, ddtags=None)` |
377
- | `SplunkHECSink` | `log_forge.sinks.splunk` | — | `SplunkHECSink(url, token, *, host=None, source="log-forge")` — HTTP Event Collector |
378
- | `NewRelicSink` | `log_forge.sinks.newrelic` | — | `NewRelicSink(api_key, *, region="US")` — `region` is `"US"` or `"EU"` |
379
- | `HoneycombSink` | `log_forge.sinks.honeycomb` | — | `HoneycombSink(api_key, dataset, *, url="https://api.honeycomb.io")` |
380
- | `SentrySink` | `log_forge.sinks.sentry` | `sentry` | `SentrySink(dsn=None, *, min_level="ERROR")` — sends only `min_level`+ events |
385
+ | `DatadogSink` | `log_foundry.sinks.datadog` | — | `DatadogSink(api_key, *, site="datadoghq.com", service=None, ddtags=None)` |
386
+ | `SplunkHECSink` | `log_foundry.sinks.splunk` | — | `SplunkHECSink(url, token, *, host=None, source="log-foundry")` — HTTP Event Collector |
387
+ | `NewRelicSink` | `log_foundry.sinks.newrelic` | — | `NewRelicSink(api_key, *, region="US")` — `region` is `"US"` or `"EU"` |
388
+ | `HoneycombSink` | `log_foundry.sinks.honeycomb` | — | `HoneycombSink(api_key, dataset, *, url="https://api.honeycomb.io")` |
389
+ | `SentrySink` | `log_foundry.sinks.sentry` | `sentry` | `SentrySink(dsn=None, *, min_level="ERROR")` — sends only `min_level`+ events |
381
390
 
382
391
  With the `sentry` extra installed, `SentrySink` captures via `sentry_sdk.capture_event` (initialize
383
392
  the SDK yourself with `sentry_sdk.init(...)`); without it, pass `dsn=` and events are POSTed as
@@ -386,19 +395,19 @@ Sentry envelopes over HTTP.
386
395
  #### AWS — the durable-buffer path (`aws` extra)
387
396
 
388
397
  `pip install 'log-foundry[aws]'` (pulls `boto3`). Credentials and region come from boto3's standard
389
- chain — log-forge adds none of its own. Each re-chunks every batch to the service's hard per-request
398
+ chain — log-foundry adds none of its own. Each re-chunks every batch to the service's hard per-request
390
399
  limits, retries partial failures, and drops any single event too large to ever fit (counted on
391
400
  `.dropped_oversized`).
392
401
 
393
402
  | Sink | Import from | Configure |
394
403
  |---|---|---|
395
- | `SQSSink` | `log_forge.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
396
- | `SNSSink` | `log_forge.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
397
- | `KinesisSink` | `log_forge.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
398
- | `FirehoseSink` | `log_forge.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
404
+ | `SQSSink` | `log_foundry.sinks.sqs` | `SQSSink(queue_url, *, max_retries=3)` — the headline production path: a durable buffer in front of ELK, absorbing downstream spikes/outages |
405
+ | `SNSSink` | `log_foundry.sinks.sns` | `SNSSink(topic_arn, *, max_retries=3)` |
406
+ | `KinesisSink` | `log_foundry.sinks.kinesis` | `KinesisSink(stream_name, *, partition_key_field="trace_id", max_retries=3)` |
407
+ | `FirehoseSink` | `log_foundry.sinks.firehose` | `FirehoseSink(delivery_stream, *, max_retries=3)` |
399
408
 
400
409
  ```python
401
- from log_forge.sinks.sqs import SQSSink
410
+ from log_foundry.sinks.sqs import SQSSink
402
411
  lf.configure(service="payments",
403
412
  sink=SQSSink(queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/logs"))
404
413
  ```
@@ -411,16 +420,16 @@ Each needs its own extra (lazy-imported). All publish + retry within a bound and
411
420
 
412
421
  | Sink | Import from | Extra | Configure |
413
422
  |---|---|---|---|
414
- | `KafkaSink` | `log_forge.sinks.kafka` | `kafka` | `KafkaSink(topic, *, bootstrap_servers="…", key_field="trace_id")` |
415
- | `RedisStreamsSink` | `log_forge.sinks.redis` | `redis` | `RedisStreamsSink(stream, *, url=None)` — `XADD` |
416
- | `RedisListSink` | `log_forge.sinks.redis` | `redis` | `RedisListSink(key, *, url=None)` — `RPUSH` |
417
- | `RabbitMQSink` | `log_forge.sinks.rabbitmq` | `amqp` | `RabbitMQSink(*, exchange, routing_key, url=None)` — persistent messages |
418
- | `NATSSink` | `log_forge.sinks.nats` | `nats` | `NATSSink(subject, *, jetstream=False, servers=None)` |
419
- | `GooglePubSubSink` | `log_forge.sinks.pubsub` | `gcp-pubsub` | `GooglePubSubSink(topic)` |
420
- | `AzureEventHubsSink` | `log_forge.sinks.eventhubs` | `azure-eventhubs` | `AzureEventHubsSink(*, connection_str="…", eventhub=None)` |
423
+ | `KafkaSink` | `log_foundry.sinks.kafka` | `kafka` | `KafkaSink(topic, *, bootstrap_servers="…", key_field="trace_id")` |
424
+ | `RedisStreamsSink` | `log_foundry.sinks.redis` | `redis` | `RedisStreamsSink(stream, *, url=None)` — `XADD` |
425
+ | `RedisListSink` | `log_foundry.sinks.redis` | `redis` | `RedisListSink(key, *, url=None)` — `RPUSH` |
426
+ | `RabbitMQSink` | `log_foundry.sinks.rabbitmq` | `amqp` | `RabbitMQSink(*, exchange, routing_key, url=None)` — persistent messages |
427
+ | `NATSSink` | `log_foundry.sinks.nats` | `nats` | `NATSSink(subject, *, jetstream=False, servers=None)` |
428
+ | `GooglePubSubSink` | `log_foundry.sinks.pubsub` | `gcp-pubsub` | `GooglePubSubSink(topic)` |
429
+ | `AzureEventHubsSink` | `log_foundry.sinks.eventhubs` | `azure-eventhubs` | `AzureEventHubsSink(*, connection_str="…", eventhub=None)` |
421
430
 
422
431
  ```python
423
- from log_forge.sinks.kafka import KafkaSink
432
+ from log_foundry.sinks.kafka import KafkaSink
424
433
  lf.configure(sink=KafkaSink("app-logs", bootstrap_servers="broker:9092"))
425
434
  ```
426
435
 
@@ -430,9 +439,9 @@ Write-only inserts (querying is the downstream tool's job); each needs its own e
430
439
 
431
440
  | Sink | Import from | Extra | Configure |
432
441
  |---|---|---|---|
433
- | `MongoDBSink` | `log_forge.sinks.mongodb` | `mongo` | `MongoDBSink(*, uri="…", database="…", collection="…")` |
434
- | `PostgresSink` | `log_forge.sinks.postgres` | `postgres` | `PostgresSink(table, *, dsn="…", create_table=False)` — JSONB `event` column + extracted columns |
435
- | `ClickHouseSink` | `log_forge.sinks.clickhouse` | `clickhouse` | `ClickHouseSink(table, *, dsn="…", create_table=False)` — MergeTree, columnar insert |
442
+ | `MongoDBSink` | `log_foundry.sinks.mongodb` | `mongo` | `MongoDBSink(*, uri="…", database="…", collection="…")` |
443
+ | `PostgresSink` | `log_foundry.sinks.postgres` | `postgres` | `PostgresSink(table, *, dsn="…", create_table=False)` — JSONB `event` column + extracted columns |
444
+ | `ClickHouseSink` | `log_foundry.sinks.clickhouse` | `clickhouse` | `ClickHouseSink(table, *, dsn="…", create_table=False)` — MergeTree, columnar insert |
436
445
 
437
446
  `PostgresSink` / `ClickHouseSink` default `create_table=False` (you own the schema and indexes); set
438
447
  it `True` for an idempotent `CREATE TABLE IF NOT EXISTS` convenience.
@@ -452,7 +461,7 @@ them (`worker.dropped`) rather than stalling.
452
461
  Because delivery is asynchronous, drain before the process exits:
453
462
 
454
463
  ```python
455
- import log_forge as lf
464
+ import log_foundry as lf
456
465
 
457
466
  lf.shutdown() # flush buffered events and close the sink; blocks until drained
458
467
  ```
@@ -497,7 +506,7 @@ poetry run mypy # typecheck (strict, over src/)
497
506
  every pull request and on push to `main`. A second workflow
498
507
  ([`spec-lint.yml`](.github/workflows/spec-lint.yml)) lints the design specs under `docs/specs/`.
499
508
 
500
- The library uses a src layout (`src/log_forge/`) with a single concept per module: `config`,
509
+ The library uses a src layout (`src/log_foundry/`) with a single concept per module: `config`,
501
510
  `ids`, `model`, `context`, `decorator`, `api`, `console`, `worker`, and the `sinks/` package (the
502
511
  `base` protocol, `stdout`, and one module per sink family — see [Sinks](#sinks)).
503
512
  Deeper design docs live in [`docs/`](docs/) — start with [`docs/architecture.md`](docs/architecture.md).
@@ -513,8 +522,8 @@ sdist and a wheel:
513
522
 
514
523
  | Trigger | Version built | Published to PyPI as |
515
524
  |---|---|---|
516
- | merge to `main` | `0.1.1.devN` | dev pre-release |
517
- | push tag `v0.1.0` | `0.1.0` | stable release |
525
+ | merge to `main` | `X.Y.Z.devN` | dev pre-release |
526
+ | push tag `vX.Y.Z` | `X.Y.Z` | stable release |
518
527
 
519
528
  Dev pre-releases keep the upload path exercised on every merge, so a real release is never the
520
529
  first time it runs. `pip install log-foundry` still resolves to the latest **stable** version —
@@ -523,7 +532,7 @@ pip ignores pre-releases unless you pass `--pre`.
523
532
  Cutting a release is one tag:
524
533
 
525
534
  ```bash
526
- git tag -a v0.2.0 -m "log-forge 0.2.0"
535
+ git tag -a v0.2.0 -m "log-foundry 0.2.0"
527
536
  git push origin v0.2.0
528
537
  ```
529
538