fastapi-observer 1.0.0__tar.gz → 1.2.0__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.
- {fastapi_observer-1.0.0/src/fastapi_observer.egg-info → fastapi_observer-1.2.0}/PKG-INFO +118 -4
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/README.md +111 -3
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/pyproject.toml +8 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0/src/fastapi_observer.egg-info}/PKG-INFO +118 -4
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/SOURCES.txt +7 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/requires.txt +8 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/__init__.py +22 -0
- fastapi_observer-1.2.0/src/fastapiobserver/_version.py +1 -0
- fastapi_observer-1.2.0/src/fastapiobserver/audit/__init__.py +14 -0
- fastapi_observer-1.2.0/src/fastapiobserver/audit/formatter.py +96 -0
- fastapi_observer-1.2.0/src/fastapiobserver/audit/providers.py +51 -0
- fastapi_observer-1.2.0/src/fastapiobserver/audit/verify.py +129 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/config.py +8 -0
- fastapi_observer-1.2.0/src/fastapiobserver/db_tracing.py +143 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/fastapi.py +36 -1
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/setup.py +15 -2
- fastapi_observer-1.2.0/tests/test_audit_chain.py +287 -0
- fastapi_observer-1.2.0/tests/test_db_tracing.py +166 -0
- fastapi_observer-1.0.0/src/fastapiobserver/_version.py +0 -1
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/LICENSE +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/setup.cfg +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/dependency_links.txt +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/top_level.txt +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/control_plane.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/integrations/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/integrations/strawberry.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/circuit_breaker.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/filters.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/formatter.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/queueing.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/state.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/loguru.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/builder.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/contracts.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/endpoint.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/noop.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/pathing.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/backend.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/client.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/collector.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/exemplars.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/multiprocess.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/registry.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/body_capture.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/context.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/events.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/headers.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/ip.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/metrics.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/request_logging.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/span_errors.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/lifecycle.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/logs.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/metrics.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/resource.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/settings.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/tracing.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/types.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/plugins.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/propagation.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/py.typed +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/request_context.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/security/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/security/normalize.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/security/policy.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/security/proxies.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/security/redaction.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/builtin.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/discovery.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/factory.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/__init__.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/dlq.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/handler.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/sink.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/protocol.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/registry.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/stats.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapiobserver/utils.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_body_media_allowlist.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_config_settings.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_dx_parity.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_excluded_url_precedence.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_fastapi_install.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_graphql_integrations.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_log_queue_controls.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_logging_schema_contract.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_logtail_dlq.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_loguru_bridge.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_metrics_contracts.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_metrics_optional_dependency.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_middleware.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_negotiate_content_type.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_integration.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_lifecycle.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_log_correlation.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_logs_from_env.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_logs_pipeline.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otel_metrics_from_env.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_otlp_export_integration.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_plugin_isolation.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_propagation.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_redaction_presets.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_route_template_cardinality.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_runtime_control_auth.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_security_audit_fixes.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_security_redaction.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_sink_circuit_breaker.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_sinks.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_trust_boundary.py +0 -0
- {fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/tests/test_utils_refactors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-observer
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Zero-glue FastAPI observability with security presets and runtime controls
|
|
5
5
|
Author-email: Vitaee <opensource@vitaee.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -45,6 +45,10 @@ Provides-Extra: otel-httpx
|
|
|
45
45
|
Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "otel-httpx"
|
|
46
46
|
Provides-Extra: otel-requests
|
|
47
47
|
Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "otel-requests"
|
|
48
|
+
Provides-Extra: audit
|
|
49
|
+
Requires-Dist: cryptography>=46.0.5; extra == "audit"
|
|
50
|
+
Provides-Extra: otel-sqlalchemy
|
|
51
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.60b1; extra == "otel-sqlalchemy"
|
|
48
52
|
Provides-Extra: all
|
|
49
53
|
Requires-Dist: orjson>=3.11.7; extra == "all"
|
|
50
54
|
Requires-Dist: prometheus-client>=0.24.1; extra == "all"
|
|
@@ -56,6 +60,8 @@ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.60b1; extra == "all"
|
|
|
56
60
|
Requires-Dist: opentelemetry-instrumentation-logging>=0.60b1; extra == "all"
|
|
57
61
|
Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "all"
|
|
58
62
|
Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "all"
|
|
63
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.60b1; extra == "all"
|
|
64
|
+
Requires-Dist: cryptography>=46.0.5; extra == "all"
|
|
59
65
|
Provides-Extra: dev
|
|
60
66
|
Requires-Dist: httpx>=0.28.1; extra == "dev"
|
|
61
67
|
Requires-Dist: loguru>=0.7.2; extra == "dev"
|
|
@@ -645,6 +651,8 @@ The `examples/` directory contains runnable demos:
|
|
|
645
651
|
| [`security_presets_app.py`](examples/security_presets_app.py) | Preset-based security policy |
|
|
646
652
|
| [`allowlist_app.py`](examples/allowlist_app.py) | Allowlist-only sanitization |
|
|
647
653
|
| [`otel_app.py`](examples/otel_app.py) | OTel tracing and resource attributes |
|
|
654
|
+
| [`audit_app.py`](examples/audit_app.py) | Tamper-evident cryptographic log signatures |
|
|
655
|
+
| [`db_tracing_app.py`](examples/db_tracing_app.py) | SQLCommenter and database trace injection |
|
|
648
656
|
| [`graphql_app.py`](examples/graphql_app.py) | Native Strawberry GraphQL observability |
|
|
649
657
|
| [`benchmarks/`](examples/benchmarks/) | Baseline vs observer benchmark harness |
|
|
650
658
|
| [`k8s/`](examples/k8s/) | Kubernetes-native stack with Prometheus + Loki + Tempo + Grafana |
|
|
@@ -776,6 +784,111 @@ Notes:
|
|
|
776
784
|
> [!TIP]
|
|
777
785
|
> The Logtail Dead Letter Queue (DLQ) provides best-effort local durability. If the internal memory queue overflows under immense API pressure (`queue.Full`), or if an external network partition completely exhausts the outbound HTTP retry backoff, the dropped log payloads are immediately salvaged into local NDJSON envelopes. You can replay these files to BetterStack later using the provided `scripts/replay_dlq.py` utility.
|
|
778
786
|
|
|
787
|
+
### Tamper-evident audit logging
|
|
788
|
+
|
|
789
|
+
For regulated industries (Fintech, Healthcare, SOC 2) where you must prove logs were not altered, deleted, or reordered:
|
|
790
|
+
|
|
791
|
+
```bash
|
|
792
|
+
pip install "fastapi-observer[audit]"
|
|
793
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
794
|
+
export OBS_AUDIT_LOGGING_ENABLED="true"
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
```python
|
|
798
|
+
install_observability(app, settings)
|
|
799
|
+
# Every JSON log now contains _audit_seq and _audit_sig fields
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Each log record is chained via HMAC-SHA256: record B's signature includes the signature of record A. Breaking any link (tamper, delete, reorder) invalidates the chain.
|
|
803
|
+
|
|
804
|
+
| Variable | Default | Description |
|
|
805
|
+
|---|---|---|
|
|
806
|
+
| `OBS_AUDIT_LOGGING_ENABLED` | `false` | Enable HMAC-SHA256 hash chain |
|
|
807
|
+
| `OBS_AUDIT_KEY_ENV_VAR` | `OBS_AUDIT_SECRET_KEY` | Name of env var containing the signing key |
|
|
808
|
+
| `OBS_AUDIT_SECRET_KEY` | - | The HMAC signing key (read by `LocalHMACProvider`) |
|
|
809
|
+
|
|
810
|
+
Custom key provider (e.g. KMS / Vault):
|
|
811
|
+
|
|
812
|
+
```python
|
|
813
|
+
from fastapiobserver import AuditKeyProvider
|
|
814
|
+
|
|
815
|
+
class VaultKeyProvider:
|
|
816
|
+
def get_key(self) -> bytes:
|
|
817
|
+
return vault_client.get_secret("audit-signing-key").encode()
|
|
818
|
+
|
|
819
|
+
install_observability(app, settings, audit_key_provider=VaultKeyProvider())
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Verify logs with the CLI:
|
|
823
|
+
|
|
824
|
+
```bash
|
|
825
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
826
|
+
python scripts/verify_audit_chain.py exported_logs.ndjson
|
|
827
|
+
# PASS — 1042 records verified, chain intact.
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### SQLCommenter & database tracing
|
|
831
|
+
|
|
832
|
+
Bridges the gap between FastAPI application traces and database performance monitoring. When enabled, the `trace_id` is automatically injected into raw SQL queries as a comment:
|
|
833
|
+
|
|
834
|
+
```sql
|
|
835
|
+
SELECT * FROM users /*traceparent='00-abc123def456-01',route='/api/users'*/
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
This lets DBAs correlate slow Postgres/MySQL queries directly back to the originating HTTP request.
|
|
839
|
+
|
|
840
|
+
```bash
|
|
841
|
+
pip install "fastapi-observer[otel-sqlalchemy]"
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
**Automatic** (via `install_observability`):
|
|
845
|
+
|
|
846
|
+
```python
|
|
847
|
+
from sqlalchemy import create_engine
|
|
848
|
+
|
|
849
|
+
engine = create_engine("postgresql://...")
|
|
850
|
+
install_observability(app, settings, otel_settings=otel_settings, db_engine=engine)
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Multiple engines (e.g. read/write replicas):
|
|
854
|
+
|
|
855
|
+
```python
|
|
856
|
+
write_engine = create_engine("postgresql://primary/...")
|
|
857
|
+
read_engine = create_engine("postgresql://replica/...")
|
|
858
|
+
install_observability(
|
|
859
|
+
app, settings,
|
|
860
|
+
otel_settings=otel_settings,
|
|
861
|
+
db_engine=[write_engine, read_engine],
|
|
862
|
+
db_commenter_options={"opentelemetry_values": True, "route": False},
|
|
863
|
+
)
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
**Manual** (standalone):
|
|
867
|
+
|
|
868
|
+
```python
|
|
869
|
+
from fastapiobserver import instrument_sqlalchemy, instrument_sqlalchemy_async
|
|
870
|
+
|
|
871
|
+
# Sync engine
|
|
872
|
+
instrument_sqlalchemy(engine)
|
|
873
|
+
|
|
874
|
+
# Async engine (extracts .sync_engine automatically)
|
|
875
|
+
instrument_sqlalchemy_async(async_engine)
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Custom commenter options:
|
|
879
|
+
|
|
880
|
+
```python
|
|
881
|
+
instrument_sqlalchemy(engine, commenter_options={
|
|
882
|
+
"opentelemetry_values": True, # traceparent (default: True)
|
|
883
|
+
"db_driver": True, # e.g. psycopg2 (default: True)
|
|
884
|
+
"route": True, # e.g. /api/users (default: True)
|
|
885
|
+
"db_framework": True, # e.g. sqlalchemy (default: False)
|
|
886
|
+
})
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
> [!CAUTION]
|
|
890
|
+
> **Database statement PII risk:** The OpenTelemetry SQLAlchemy instrumentor captures raw SQL as the `db.statement` span attribute and exports it directly to your tracing backend (Jaeger, Tempo, Datadog). These span attributes are **not** scrubbed by `fastapi-observer`'s `SecurityPolicy`. If your application has poorly parameterized queries (e.g. `WHERE email = 'user@example.com'`), PII will leak into your trace storage. Always use parameterized queries and review your `db.statement` exports in production.
|
|
891
|
+
|
|
779
892
|
---
|
|
780
893
|
|
|
781
894
|
## Advanced Operations
|
|
@@ -1055,9 +1168,10 @@ Reproducible benchmark harness and methodology:
|
|
|
1055
1168
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
1056
1169
|
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
1057
1170
|
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
1058
|
-
- `1.0.
|
|
1171
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1172
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
1059
1173
|
|
|
1060
|
-
Current release version: `1.
|
|
1174
|
+
Current release version: `1.2.0`
|
|
1061
1175
|
|
|
1062
1176
|
## Changelog Policy
|
|
1063
1177
|
|
|
@@ -1070,7 +1184,7 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
1070
1184
|
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1071
1185
|
|
|
1072
1186
|
```bash
|
|
1073
|
-
scripts/deploy_pypi.sh --tag v1.
|
|
1187
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1074
1188
|
```
|
|
1075
1189
|
|
|
1076
1190
|
### 1) Build distributions
|
|
@@ -575,6 +575,8 @@ The `examples/` directory contains runnable demos:
|
|
|
575
575
|
| [`security_presets_app.py`](examples/security_presets_app.py) | Preset-based security policy |
|
|
576
576
|
| [`allowlist_app.py`](examples/allowlist_app.py) | Allowlist-only sanitization |
|
|
577
577
|
| [`otel_app.py`](examples/otel_app.py) | OTel tracing and resource attributes |
|
|
578
|
+
| [`audit_app.py`](examples/audit_app.py) | Tamper-evident cryptographic log signatures |
|
|
579
|
+
| [`db_tracing_app.py`](examples/db_tracing_app.py) | SQLCommenter and database trace injection |
|
|
578
580
|
| [`graphql_app.py`](examples/graphql_app.py) | Native Strawberry GraphQL observability |
|
|
579
581
|
| [`benchmarks/`](examples/benchmarks/) | Baseline vs observer benchmark harness |
|
|
580
582
|
| [`k8s/`](examples/k8s/) | Kubernetes-native stack with Prometheus + Loki + Tempo + Grafana |
|
|
@@ -706,6 +708,111 @@ Notes:
|
|
|
706
708
|
> [!TIP]
|
|
707
709
|
> The Logtail Dead Letter Queue (DLQ) provides best-effort local durability. If the internal memory queue overflows under immense API pressure (`queue.Full`), or if an external network partition completely exhausts the outbound HTTP retry backoff, the dropped log payloads are immediately salvaged into local NDJSON envelopes. You can replay these files to BetterStack later using the provided `scripts/replay_dlq.py` utility.
|
|
708
710
|
|
|
711
|
+
### Tamper-evident audit logging
|
|
712
|
+
|
|
713
|
+
For regulated industries (Fintech, Healthcare, SOC 2) where you must prove logs were not altered, deleted, or reordered:
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
pip install "fastapi-observer[audit]"
|
|
717
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
718
|
+
export OBS_AUDIT_LOGGING_ENABLED="true"
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
```python
|
|
722
|
+
install_observability(app, settings)
|
|
723
|
+
# Every JSON log now contains _audit_seq and _audit_sig fields
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Each log record is chained via HMAC-SHA256: record B's signature includes the signature of record A. Breaking any link (tamper, delete, reorder) invalidates the chain.
|
|
727
|
+
|
|
728
|
+
| Variable | Default | Description |
|
|
729
|
+
|---|---|---|
|
|
730
|
+
| `OBS_AUDIT_LOGGING_ENABLED` | `false` | Enable HMAC-SHA256 hash chain |
|
|
731
|
+
| `OBS_AUDIT_KEY_ENV_VAR` | `OBS_AUDIT_SECRET_KEY` | Name of env var containing the signing key |
|
|
732
|
+
| `OBS_AUDIT_SECRET_KEY` | - | The HMAC signing key (read by `LocalHMACProvider`) |
|
|
733
|
+
|
|
734
|
+
Custom key provider (e.g. KMS / Vault):
|
|
735
|
+
|
|
736
|
+
```python
|
|
737
|
+
from fastapiobserver import AuditKeyProvider
|
|
738
|
+
|
|
739
|
+
class VaultKeyProvider:
|
|
740
|
+
def get_key(self) -> bytes:
|
|
741
|
+
return vault_client.get_secret("audit-signing-key").encode()
|
|
742
|
+
|
|
743
|
+
install_observability(app, settings, audit_key_provider=VaultKeyProvider())
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
Verify logs with the CLI:
|
|
747
|
+
|
|
748
|
+
```bash
|
|
749
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
750
|
+
python scripts/verify_audit_chain.py exported_logs.ndjson
|
|
751
|
+
# PASS — 1042 records verified, chain intact.
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### SQLCommenter & database tracing
|
|
755
|
+
|
|
756
|
+
Bridges the gap between FastAPI application traces and database performance monitoring. When enabled, the `trace_id` is automatically injected into raw SQL queries as a comment:
|
|
757
|
+
|
|
758
|
+
```sql
|
|
759
|
+
SELECT * FROM users /*traceparent='00-abc123def456-01',route='/api/users'*/
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
This lets DBAs correlate slow Postgres/MySQL queries directly back to the originating HTTP request.
|
|
763
|
+
|
|
764
|
+
```bash
|
|
765
|
+
pip install "fastapi-observer[otel-sqlalchemy]"
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Automatic** (via `install_observability`):
|
|
769
|
+
|
|
770
|
+
```python
|
|
771
|
+
from sqlalchemy import create_engine
|
|
772
|
+
|
|
773
|
+
engine = create_engine("postgresql://...")
|
|
774
|
+
install_observability(app, settings, otel_settings=otel_settings, db_engine=engine)
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
Multiple engines (e.g. read/write replicas):
|
|
778
|
+
|
|
779
|
+
```python
|
|
780
|
+
write_engine = create_engine("postgresql://primary/...")
|
|
781
|
+
read_engine = create_engine("postgresql://replica/...")
|
|
782
|
+
install_observability(
|
|
783
|
+
app, settings,
|
|
784
|
+
otel_settings=otel_settings,
|
|
785
|
+
db_engine=[write_engine, read_engine],
|
|
786
|
+
db_commenter_options={"opentelemetry_values": True, "route": False},
|
|
787
|
+
)
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
**Manual** (standalone):
|
|
791
|
+
|
|
792
|
+
```python
|
|
793
|
+
from fastapiobserver import instrument_sqlalchemy, instrument_sqlalchemy_async
|
|
794
|
+
|
|
795
|
+
# Sync engine
|
|
796
|
+
instrument_sqlalchemy(engine)
|
|
797
|
+
|
|
798
|
+
# Async engine (extracts .sync_engine automatically)
|
|
799
|
+
instrument_sqlalchemy_async(async_engine)
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Custom commenter options:
|
|
803
|
+
|
|
804
|
+
```python
|
|
805
|
+
instrument_sqlalchemy(engine, commenter_options={
|
|
806
|
+
"opentelemetry_values": True, # traceparent (default: True)
|
|
807
|
+
"db_driver": True, # e.g. psycopg2 (default: True)
|
|
808
|
+
"route": True, # e.g. /api/users (default: True)
|
|
809
|
+
"db_framework": True, # e.g. sqlalchemy (default: False)
|
|
810
|
+
})
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
> [!CAUTION]
|
|
814
|
+
> **Database statement PII risk:** The OpenTelemetry SQLAlchemy instrumentor captures raw SQL as the `db.statement` span attribute and exports it directly to your tracing backend (Jaeger, Tempo, Datadog). These span attributes are **not** scrubbed by `fastapi-observer`'s `SecurityPolicy`. If your application has poorly parameterized queries (e.g. `WHERE email = 'user@example.com'`), PII will leak into your trace storage. Always use parameterized queries and review your `db.statement` exports in production.
|
|
815
|
+
|
|
709
816
|
---
|
|
710
817
|
|
|
711
818
|
## Advanced Operations
|
|
@@ -985,9 +1092,10 @@ Reproducible benchmark harness and methodology:
|
|
|
985
1092
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
986
1093
|
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
987
1094
|
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
988
|
-
- `1.0.
|
|
1095
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1096
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
989
1097
|
|
|
990
|
-
Current release version: `1.
|
|
1098
|
+
Current release version: `1.2.0`
|
|
991
1099
|
|
|
992
1100
|
## Changelog Policy
|
|
993
1101
|
|
|
@@ -1000,7 +1108,7 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
1000
1108
|
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1001
1109
|
|
|
1002
1110
|
```bash
|
|
1003
|
-
scripts/deploy_pypi.sh --tag v1.
|
|
1111
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1004
1112
|
```
|
|
1005
1113
|
|
|
1006
1114
|
### 1) Build distributions
|
|
@@ -65,6 +65,12 @@ otel-httpx = [
|
|
|
65
65
|
otel-requests = [
|
|
66
66
|
"opentelemetry-instrumentation-requests>=0.60b1",
|
|
67
67
|
]
|
|
68
|
+
audit = [
|
|
69
|
+
"cryptography>=46.0.5",
|
|
70
|
+
]
|
|
71
|
+
otel-sqlalchemy = [
|
|
72
|
+
"opentelemetry-instrumentation-sqlalchemy>=0.60b1",
|
|
73
|
+
]
|
|
68
74
|
all = [
|
|
69
75
|
"orjson>=3.11.7",
|
|
70
76
|
"prometheus-client>=0.24.1",
|
|
@@ -76,6 +82,8 @@ all = [
|
|
|
76
82
|
"opentelemetry-instrumentation-logging>=0.60b1",
|
|
77
83
|
"opentelemetry-instrumentation-httpx>=0.60b1",
|
|
78
84
|
"opentelemetry-instrumentation-requests>=0.60b1",
|
|
85
|
+
"opentelemetry-instrumentation-sqlalchemy>=0.60b1",
|
|
86
|
+
"cryptography>=46.0.5",
|
|
79
87
|
]
|
|
80
88
|
dev = [
|
|
81
89
|
"httpx>=0.28.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-observer
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Zero-glue FastAPI observability with security presets and runtime controls
|
|
5
5
|
Author-email: Vitaee <opensource@vitaee.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -45,6 +45,10 @@ Provides-Extra: otel-httpx
|
|
|
45
45
|
Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "otel-httpx"
|
|
46
46
|
Provides-Extra: otel-requests
|
|
47
47
|
Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "otel-requests"
|
|
48
|
+
Provides-Extra: audit
|
|
49
|
+
Requires-Dist: cryptography>=46.0.5; extra == "audit"
|
|
50
|
+
Provides-Extra: otel-sqlalchemy
|
|
51
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.60b1; extra == "otel-sqlalchemy"
|
|
48
52
|
Provides-Extra: all
|
|
49
53
|
Requires-Dist: orjson>=3.11.7; extra == "all"
|
|
50
54
|
Requires-Dist: prometheus-client>=0.24.1; extra == "all"
|
|
@@ -56,6 +60,8 @@ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.60b1; extra == "all"
|
|
|
56
60
|
Requires-Dist: opentelemetry-instrumentation-logging>=0.60b1; extra == "all"
|
|
57
61
|
Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "all"
|
|
58
62
|
Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "all"
|
|
63
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.60b1; extra == "all"
|
|
64
|
+
Requires-Dist: cryptography>=46.0.5; extra == "all"
|
|
59
65
|
Provides-Extra: dev
|
|
60
66
|
Requires-Dist: httpx>=0.28.1; extra == "dev"
|
|
61
67
|
Requires-Dist: loguru>=0.7.2; extra == "dev"
|
|
@@ -645,6 +651,8 @@ The `examples/` directory contains runnable demos:
|
|
|
645
651
|
| [`security_presets_app.py`](examples/security_presets_app.py) | Preset-based security policy |
|
|
646
652
|
| [`allowlist_app.py`](examples/allowlist_app.py) | Allowlist-only sanitization |
|
|
647
653
|
| [`otel_app.py`](examples/otel_app.py) | OTel tracing and resource attributes |
|
|
654
|
+
| [`audit_app.py`](examples/audit_app.py) | Tamper-evident cryptographic log signatures |
|
|
655
|
+
| [`db_tracing_app.py`](examples/db_tracing_app.py) | SQLCommenter and database trace injection |
|
|
648
656
|
| [`graphql_app.py`](examples/graphql_app.py) | Native Strawberry GraphQL observability |
|
|
649
657
|
| [`benchmarks/`](examples/benchmarks/) | Baseline vs observer benchmark harness |
|
|
650
658
|
| [`k8s/`](examples/k8s/) | Kubernetes-native stack with Prometheus + Loki + Tempo + Grafana |
|
|
@@ -776,6 +784,111 @@ Notes:
|
|
|
776
784
|
> [!TIP]
|
|
777
785
|
> The Logtail Dead Letter Queue (DLQ) provides best-effort local durability. If the internal memory queue overflows under immense API pressure (`queue.Full`), or if an external network partition completely exhausts the outbound HTTP retry backoff, the dropped log payloads are immediately salvaged into local NDJSON envelopes. You can replay these files to BetterStack later using the provided `scripts/replay_dlq.py` utility.
|
|
778
786
|
|
|
787
|
+
### Tamper-evident audit logging
|
|
788
|
+
|
|
789
|
+
For regulated industries (Fintech, Healthcare, SOC 2) where you must prove logs were not altered, deleted, or reordered:
|
|
790
|
+
|
|
791
|
+
```bash
|
|
792
|
+
pip install "fastapi-observer[audit]"
|
|
793
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
794
|
+
export OBS_AUDIT_LOGGING_ENABLED="true"
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
```python
|
|
798
|
+
install_observability(app, settings)
|
|
799
|
+
# Every JSON log now contains _audit_seq and _audit_sig fields
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Each log record is chained via HMAC-SHA256: record B's signature includes the signature of record A. Breaking any link (tamper, delete, reorder) invalidates the chain.
|
|
803
|
+
|
|
804
|
+
| Variable | Default | Description |
|
|
805
|
+
|---|---|---|
|
|
806
|
+
| `OBS_AUDIT_LOGGING_ENABLED` | `false` | Enable HMAC-SHA256 hash chain |
|
|
807
|
+
| `OBS_AUDIT_KEY_ENV_VAR` | `OBS_AUDIT_SECRET_KEY` | Name of env var containing the signing key |
|
|
808
|
+
| `OBS_AUDIT_SECRET_KEY` | - | The HMAC signing key (read by `LocalHMACProvider`) |
|
|
809
|
+
|
|
810
|
+
Custom key provider (e.g. KMS / Vault):
|
|
811
|
+
|
|
812
|
+
```python
|
|
813
|
+
from fastapiobserver import AuditKeyProvider
|
|
814
|
+
|
|
815
|
+
class VaultKeyProvider:
|
|
816
|
+
def get_key(self) -> bytes:
|
|
817
|
+
return vault_client.get_secret("audit-signing-key").encode()
|
|
818
|
+
|
|
819
|
+
install_observability(app, settings, audit_key_provider=VaultKeyProvider())
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Verify logs with the CLI:
|
|
823
|
+
|
|
824
|
+
```bash
|
|
825
|
+
export OBS_AUDIT_SECRET_KEY="your-signing-secret"
|
|
826
|
+
python scripts/verify_audit_chain.py exported_logs.ndjson
|
|
827
|
+
# PASS — 1042 records verified, chain intact.
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### SQLCommenter & database tracing
|
|
831
|
+
|
|
832
|
+
Bridges the gap between FastAPI application traces and database performance monitoring. When enabled, the `trace_id` is automatically injected into raw SQL queries as a comment:
|
|
833
|
+
|
|
834
|
+
```sql
|
|
835
|
+
SELECT * FROM users /*traceparent='00-abc123def456-01',route='/api/users'*/
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
This lets DBAs correlate slow Postgres/MySQL queries directly back to the originating HTTP request.
|
|
839
|
+
|
|
840
|
+
```bash
|
|
841
|
+
pip install "fastapi-observer[otel-sqlalchemy]"
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
**Automatic** (via `install_observability`):
|
|
845
|
+
|
|
846
|
+
```python
|
|
847
|
+
from sqlalchemy import create_engine
|
|
848
|
+
|
|
849
|
+
engine = create_engine("postgresql://...")
|
|
850
|
+
install_observability(app, settings, otel_settings=otel_settings, db_engine=engine)
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Multiple engines (e.g. read/write replicas):
|
|
854
|
+
|
|
855
|
+
```python
|
|
856
|
+
write_engine = create_engine("postgresql://primary/...")
|
|
857
|
+
read_engine = create_engine("postgresql://replica/...")
|
|
858
|
+
install_observability(
|
|
859
|
+
app, settings,
|
|
860
|
+
otel_settings=otel_settings,
|
|
861
|
+
db_engine=[write_engine, read_engine],
|
|
862
|
+
db_commenter_options={"opentelemetry_values": True, "route": False},
|
|
863
|
+
)
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
**Manual** (standalone):
|
|
867
|
+
|
|
868
|
+
```python
|
|
869
|
+
from fastapiobserver import instrument_sqlalchemy, instrument_sqlalchemy_async
|
|
870
|
+
|
|
871
|
+
# Sync engine
|
|
872
|
+
instrument_sqlalchemy(engine)
|
|
873
|
+
|
|
874
|
+
# Async engine (extracts .sync_engine automatically)
|
|
875
|
+
instrument_sqlalchemy_async(async_engine)
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Custom commenter options:
|
|
879
|
+
|
|
880
|
+
```python
|
|
881
|
+
instrument_sqlalchemy(engine, commenter_options={
|
|
882
|
+
"opentelemetry_values": True, # traceparent (default: True)
|
|
883
|
+
"db_driver": True, # e.g. psycopg2 (default: True)
|
|
884
|
+
"route": True, # e.g. /api/users (default: True)
|
|
885
|
+
"db_framework": True, # e.g. sqlalchemy (default: False)
|
|
886
|
+
})
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
> [!CAUTION]
|
|
890
|
+
> **Database statement PII risk:** The OpenTelemetry SQLAlchemy instrumentor captures raw SQL as the `db.statement` span attribute and exports it directly to your tracing backend (Jaeger, Tempo, Datadog). These span attributes are **not** scrubbed by `fastapi-observer`'s `SecurityPolicy`. If your application has poorly parameterized queries (e.g. `WHERE email = 'user@example.com'`), PII will leak into your trace storage. Always use parameterized queries and review your `db.statement` exports in production.
|
|
891
|
+
|
|
779
892
|
---
|
|
780
893
|
|
|
781
894
|
## Advanced Operations
|
|
@@ -1055,9 +1168,10 @@ Reproducible benchmark harness and methodology:
|
|
|
1055
1168
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
1056
1169
|
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
1057
1170
|
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
1058
|
-
- `1.0.
|
|
1171
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1172
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
1059
1173
|
|
|
1060
|
-
Current release version: `1.
|
|
1174
|
+
Current release version: `1.2.0`
|
|
1061
1175
|
|
|
1062
1176
|
## Changelog Policy
|
|
1063
1177
|
|
|
@@ -1070,7 +1184,7 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
1070
1184
|
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1071
1185
|
|
|
1072
1186
|
```bash
|
|
1073
|
-
scripts/deploy_pypi.sh --tag v1.
|
|
1187
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1074
1188
|
```
|
|
1075
1189
|
|
|
1076
1190
|
### 1) Build distributions
|
|
@@ -10,6 +10,7 @@ src/fastapiobserver/__init__.py
|
|
|
10
10
|
src/fastapiobserver/_version.py
|
|
11
11
|
src/fastapiobserver/config.py
|
|
12
12
|
src/fastapiobserver/control_plane.py
|
|
13
|
+
src/fastapiobserver/db_tracing.py
|
|
13
14
|
src/fastapiobserver/fastapi.py
|
|
14
15
|
src/fastapiobserver/loguru.py
|
|
15
16
|
src/fastapiobserver/plugins.py
|
|
@@ -17,6 +18,10 @@ src/fastapiobserver/propagation.py
|
|
|
17
18
|
src/fastapiobserver/py.typed
|
|
18
19
|
src/fastapiobserver/request_context.py
|
|
19
20
|
src/fastapiobserver/utils.py
|
|
21
|
+
src/fastapiobserver/audit/__init__.py
|
|
22
|
+
src/fastapiobserver/audit/formatter.py
|
|
23
|
+
src/fastapiobserver/audit/providers.py
|
|
24
|
+
src/fastapiobserver/audit/verify.py
|
|
20
25
|
src/fastapiobserver/integrations/__init__.py
|
|
21
26
|
src/fastapiobserver/integrations/strawberry.py
|
|
22
27
|
src/fastapiobserver/logging/__init__.py
|
|
@@ -72,8 +77,10 @@ src/fastapiobserver/sinks/logtail/__init__.py
|
|
|
72
77
|
src/fastapiobserver/sinks/logtail/dlq.py
|
|
73
78
|
src/fastapiobserver/sinks/logtail/handler.py
|
|
74
79
|
src/fastapiobserver/sinks/logtail/sink.py
|
|
80
|
+
tests/test_audit_chain.py
|
|
75
81
|
tests/test_body_media_allowlist.py
|
|
76
82
|
tests/test_config_settings.py
|
|
83
|
+
tests/test_db_tracing.py
|
|
77
84
|
tests/test_dx_parity.py
|
|
78
85
|
tests/test_excluded_url_precedence.py
|
|
79
86
|
tests/test_fastapi_install.py
|
{fastapi_observer-1.0.0 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/requires.txt
RENAMED
|
@@ -13,6 +13,11 @@ opentelemetry-instrumentation-fastapi>=0.60b1
|
|
|
13
13
|
opentelemetry-instrumentation-logging>=0.60b1
|
|
14
14
|
opentelemetry-instrumentation-httpx>=0.60b1
|
|
15
15
|
opentelemetry-instrumentation-requests>=0.60b1
|
|
16
|
+
opentelemetry-instrumentation-sqlalchemy>=0.60b1
|
|
17
|
+
cryptography>=46.0.5
|
|
18
|
+
|
|
19
|
+
[audit]
|
|
20
|
+
cryptography>=46.0.5
|
|
16
21
|
|
|
17
22
|
[dev]
|
|
18
23
|
httpx>=0.28.1
|
|
@@ -44,5 +49,8 @@ opentelemetry-instrumentation-httpx>=0.60b1
|
|
|
44
49
|
[otel-requests]
|
|
45
50
|
opentelemetry-instrumentation-requests>=0.60b1
|
|
46
51
|
|
|
52
|
+
[otel-sqlalchemy]
|
|
53
|
+
opentelemetry-instrumentation-sqlalchemy>=0.60b1
|
|
54
|
+
|
|
47
55
|
[prometheus]
|
|
48
56
|
prometheus-client>=0.24.1
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
|
+
from .audit import (
|
|
3
|
+
AuditChainFormatter,
|
|
4
|
+
AuditKeyProvider,
|
|
5
|
+
AuditVerificationResult,
|
|
6
|
+
LocalHMACProvider,
|
|
7
|
+
verify_audit_chain,
|
|
8
|
+
)
|
|
2
9
|
from .config import ObservabilitySettings
|
|
3
10
|
from .control_plane import RuntimeControlSettings, mount_control_plane
|
|
11
|
+
from .db_tracing import (
|
|
12
|
+
instrument_sqlalchemy,
|
|
13
|
+
instrument_sqlalchemy_async,
|
|
14
|
+
uninstrument_sqlalchemy,
|
|
15
|
+
)
|
|
4
16
|
from .fastapi import install_observability
|
|
5
17
|
from .logging import (
|
|
6
18
|
LOG_SCHEMA_VERSION,
|
|
@@ -165,4 +177,14 @@ __all__ = [
|
|
|
165
177
|
"set_user_context",
|
|
166
178
|
# Version
|
|
167
179
|
"__version__",
|
|
180
|
+
# Audit
|
|
181
|
+
"AuditChainFormatter",
|
|
182
|
+
"AuditKeyProvider",
|
|
183
|
+
"AuditVerificationResult",
|
|
184
|
+
"LocalHMACProvider",
|
|
185
|
+
"verify_audit_chain",
|
|
186
|
+
# Database tracing
|
|
187
|
+
"instrument_sqlalchemy",
|
|
188
|
+
"instrument_sqlalchemy_async",
|
|
189
|
+
"uninstrument_sqlalchemy",
|
|
168
190
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.0"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Tamper-evident audit logging — optional HMAC-SHA256 hash chain."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .formatter import AuditChainFormatter
|
|
5
|
+
from .providers import AuditKeyProvider, LocalHMACProvider
|
|
6
|
+
from .verify import AuditVerificationResult, verify_audit_chain
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"AuditChainFormatter",
|
|
10
|
+
"AuditKeyProvider",
|
|
11
|
+
"AuditVerificationResult",
|
|
12
|
+
"LocalHMACProvider",
|
|
13
|
+
"verify_audit_chain",
|
|
14
|
+
]
|