fastapi-observer 0.4.1__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-0.4.1/src/fastapi_observer.egg-info → fastapi_observer-1.2.0}/PKG-INFO +126 -5
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/README.md +118 -3
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/pyproject.toml +9 -1
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0/src/fastapi_observer.egg-info}/PKG-INFO +126 -5
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/SOURCES.txt +7 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/requires.txt +8 -0
- {fastapi_observer-0.4.1 → 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-0.4.1 → 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-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/fastapi.py +36 -1
- {fastapi_observer-0.4.1 → 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-0.4.1/src/fastapiobserver/_version.py +0 -1
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/LICENSE +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/setup.cfg +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/dependency_links.txt +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapi_observer.egg-info/top_level.txt +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/control_plane.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/integrations/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/integrations/strawberry.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/circuit_breaker.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/filters.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/formatter.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/queueing.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/logging/state.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/loguru.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/builder.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/contracts.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/endpoint.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/noop.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/pathing.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/backend.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/client.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/collector.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/exemplars.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/prometheus/multiprocess.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/metrics/registry.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/body_capture.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/context.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/events.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/headers.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/ip.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/metrics.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/request_logging.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/middleware/span_errors.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/lifecycle.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/logs.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/metrics.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/resource.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/settings.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/tracing.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/otel/types.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/plugins.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/propagation.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/py.typed +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/request_context.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/security/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/security/normalize.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/security/policy.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/security/proxies.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/security/redaction.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/builtin.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/discovery.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/factory.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/__init__.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/dlq.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/handler.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/logtail/sink.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/protocol.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/registry.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/sinks/stats.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/src/fastapiobserver/utils.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_body_media_allowlist.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_config_settings.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_dx_parity.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_excluded_url_precedence.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_fastapi_install.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_graphql_integrations.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_log_queue_controls.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_logging_schema_contract.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_logtail_dlq.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_loguru_bridge.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_metrics_contracts.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_metrics_optional_dependency.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_middleware.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_negotiate_content_type.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_integration.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_lifecycle.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_log_correlation.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_logs_from_env.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_logs_pipeline.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otel_metrics_from_env.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_otlp_export_integration.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_plugin_isolation.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_propagation.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_redaction_presets.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_route_template_cardinality.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_runtime_control_auth.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_security_audit_fixes.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_security_redaction.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_sink_circuit_breaker.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_sinks.py +0 -0
- {fastapi_observer-0.4.1 → fastapi_observer-1.2.0}/tests/test_trust_boundary.py +0 -0
- {fastapi_observer-0.4.1 → 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:
|
|
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
|
|
@@ -10,7 +10,7 @@ Project-URL: Repository, https://github.com/Vitaee/FastapiObserver.git
|
|
|
10
10
|
Project-URL: Issues, https://github.com/Vitaee/FastapiObserver/issues
|
|
11
11
|
Project-URL: Funding, https://buymeacoffee.com/FYbPCSu
|
|
12
12
|
Keywords: fastapi,observability,logging,metrics,opentelemetry
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -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
|
|
@@ -1053,10 +1166,12 @@ Reproducible benchmark harness and methodology:
|
|
|
1053
1166
|
|
|
1054
1167
|
- `0.1.x`: secure-by-default core
|
|
1055
1168
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
1056
|
-
- `0.3.x`:
|
|
1057
|
-
- `
|
|
1169
|
+
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
1170
|
+
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
1171
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1172
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
1058
1173
|
|
|
1059
|
-
Current release version: `
|
|
1174
|
+
Current release version: `1.2.0`
|
|
1060
1175
|
|
|
1061
1176
|
## Changelog Policy
|
|
1062
1177
|
|
|
@@ -1066,6 +1181,12 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
1066
1181
|
|
|
1067
1182
|
## Packaging and Publishing (Maintainers)
|
|
1068
1183
|
|
|
1184
|
+
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1185
|
+
|
|
1186
|
+
```bash
|
|
1187
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1069
1190
|
### 1) Build distributions
|
|
1070
1191
|
|
|
1071
1192
|
```bash
|
|
@@ -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
|
|
@@ -983,10 +1090,12 @@ Reproducible benchmark harness and methodology:
|
|
|
983
1090
|
|
|
984
1091
|
- `0.1.x`: secure-by-default core
|
|
985
1092
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
986
|
-
- `0.3.x`:
|
|
987
|
-
- `
|
|
1093
|
+
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
1094
|
+
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
1095
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1096
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
988
1097
|
|
|
989
|
-
Current release version: `
|
|
1098
|
+
Current release version: `1.2.0`
|
|
990
1099
|
|
|
991
1100
|
## Changelog Policy
|
|
992
1101
|
|
|
@@ -996,6 +1105,12 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
996
1105
|
|
|
997
1106
|
## Packaging and Publishing (Maintainers)
|
|
998
1107
|
|
|
1108
|
+
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1109
|
+
|
|
1110
|
+
```bash
|
|
1111
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1112
|
+
```
|
|
1113
|
+
|
|
999
1114
|
### 1) Build distributions
|
|
1000
1115
|
|
|
1001
1116
|
```bash
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
{ name = "Vitaee", email = "opensource@vitaee.dev" },
|
|
13
13
|
]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
@@ -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:
|
|
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
|
|
@@ -10,7 +10,7 @@ Project-URL: Repository, https://github.com/Vitaee/FastapiObserver.git
|
|
|
10
10
|
Project-URL: Issues, https://github.com/Vitaee/FastapiObserver/issues
|
|
11
11
|
Project-URL: Funding, https://buymeacoffee.com/FYbPCSu
|
|
12
12
|
Keywords: fastapi,observability,logging,metrics,opentelemetry
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -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
|
|
@@ -1053,10 +1166,12 @@ Reproducible benchmark harness and methodology:
|
|
|
1053
1166
|
|
|
1054
1167
|
- `0.1.x`: secure-by-default core
|
|
1055
1168
|
- `0.2.x`: OTel interoperability, security presets, allowlists
|
|
1056
|
-
- `0.3.x`:
|
|
1057
|
-
- `
|
|
1169
|
+
- `0.3.x`: GraphQL observability, error fingerprinting, and Logtail DLQ durability
|
|
1170
|
+
- `0.4.x`: package modularization, sink/registry hardening, and runtime control token rotation
|
|
1171
|
+
- `1.0.x`: first stable release contract for production deployments
|
|
1172
|
+
- `1.2.0`: tamper-evident audit logging and SQLAlchemy trace/commenter integration
|
|
1058
1173
|
|
|
1059
|
-
Current release version: `
|
|
1174
|
+
Current release version: `1.2.0`
|
|
1060
1175
|
|
|
1061
1176
|
## Changelog Policy
|
|
1062
1177
|
|
|
@@ -1066,6 +1181,12 @@ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG
|
|
|
1066
1181
|
|
|
1067
1182
|
## Packaging and Publishing (Maintainers)
|
|
1068
1183
|
|
|
1184
|
+
Recommended release command (uses `.env` with `PYPI_TOKEN`):
|
|
1185
|
+
|
|
1186
|
+
```bash
|
|
1187
|
+
scripts/deploy_pypi.sh --tag v1.2.0 --push-tag
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1069
1190
|
### 1) Build distributions
|
|
1070
1191
|
|
|
1071
1192
|
```bash
|
|
@@ -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-0.4.1 → 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
|
+
]
|