fastapi-observer 1.5.0__tar.gz → 1.6.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.6.0/PKG-INFO +215 -0
- fastapi_observer-1.6.0/README.md +139 -0
- fastapi_observer-1.6.0/src/fastapi_observer.egg-info/PKG-INFO +215 -0
- fastapi_observer-1.6.0/src/fastapiobserver/_version.py +1 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/formatter.py +9 -1
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_logging_schema_contract.py +32 -0
- fastapi_observer-1.5.0/PKG-INFO +0 -249
- fastapi_observer-1.5.0/README.md +0 -173
- fastapi_observer-1.5.0/src/fastapi_observer.egg-info/PKG-INFO +0 -249
- fastapi_observer-1.5.0/src/fastapiobserver/_version.py +0 -1
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/LICENSE +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/pyproject.toml +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/setup.cfg +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/SOURCES.txt +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/dependency_links.txt +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/requires.txt +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/top_level.txt +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/formatter.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/providers.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/verify.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/config.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/control_plane.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/db_tracing.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/fastapi.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/integrations/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/integrations/strawberry.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/circuit_breaker.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/filters.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/queueing.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/setup.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/state.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/loguru.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/builder.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/contracts.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/endpoint.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/noop.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/pathing.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/backend.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/client.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/collector.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/exemplars.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/multiprocess.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/registry.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/body_capture.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/context.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/events.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/headers.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/ip.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/metrics.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/request_logging.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/span_errors.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/exclusions.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/lifecycle.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/logs.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/metrics.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/resource.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/settings.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/tracing.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/types.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/plugins.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/profiles.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/propagation.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/py.typed +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/request_context.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/normalize.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/policy.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/proxies.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/redaction.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/builtin.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/discovery.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/factory.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/__init__.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/dlq.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/handler.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/sink.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/protocol.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/registry.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/stats.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/utils.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_audit_chain.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_body_media_allowlist.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_config_settings.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_db_tracing.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_dx_parity.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_excluded_url_precedence.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_fastapi_install.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_fork_queue_listener.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_graphql_integrations.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_lifespan_teardown.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_log_queue_controls.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_logtail_dlq.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_loguru_bridge.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_metrics_contracts.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_metrics_optional_dependency.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_middleware.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_negotiate_content_type.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_integration.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_lifecycle.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_log_correlation.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_logs_from_env.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_logs_pipeline.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_metrics_from_env.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otlp_export_integration.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_plugin_isolation.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_profiles.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_propagation.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_redaction_presets.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_route_template_cardinality.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_runtime_control_auth.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_security_audit_fixes.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_security_redaction.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_sink_circuit_breaker.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_sinks.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_trust_boundary.py +0 -0
- {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_utils_refactors.py +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-observer
|
|
3
|
+
Version: 1.6.0
|
|
4
|
+
Summary: Zero-glue FastAPI observability with security presets and runtime controls
|
|
5
|
+
Author-email: Vitaee <opensource@vitaee.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Vitaee/FastapiObserver
|
|
8
|
+
Project-URL: Documentation, https://vitaee.github.io/FastapiObserver/
|
|
9
|
+
Project-URL: Repository, https://github.com/Vitaee/FastapiObserver.git
|
|
10
|
+
Project-URL: Issues, https://github.com/Vitaee/FastapiObserver/issues
|
|
11
|
+
Project-URL: Funding, https://buymeacoffee.com/FYbPCSu
|
|
12
|
+
Keywords: fastapi,observability,logging,metrics,opentelemetry
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Framework :: FastAPI
|
|
23
|
+
Classifier: Topic :: System :: Logging
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: fastapi>=0.133.0
|
|
30
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
|
31
|
+
Requires-Dist: starlette>=1.3.1
|
|
32
|
+
Provides-Extra: fast-json
|
|
33
|
+
Requires-Dist: orjson>=3.11.7; extra == "fast-json"
|
|
34
|
+
Provides-Extra: prometheus
|
|
35
|
+
Requires-Dist: prometheus-client>=0.24.1; extra == "prometheus"
|
|
36
|
+
Provides-Extra: loguru
|
|
37
|
+
Requires-Dist: loguru>=0.7.2; extra == "loguru"
|
|
38
|
+
Provides-Extra: otel
|
|
39
|
+
Requires-Dist: opentelemetry-api>=1.45.1; extra == "otel"
|
|
40
|
+
Requires-Dist: opentelemetry-sdk>=1.45.1; extra == "otel"
|
|
41
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.45.1; extra == "otel"
|
|
42
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.66b1; extra == "otel"
|
|
43
|
+
Requires-Dist: opentelemetry-instrumentation-logging>=0.66b1; extra == "otel"
|
|
44
|
+
Provides-Extra: otel-httpx
|
|
45
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.66b1; extra == "otel-httpx"
|
|
46
|
+
Provides-Extra: otel-requests
|
|
47
|
+
Requires-Dist: opentelemetry-instrumentation-requests>=0.66b1; extra == "otel-requests"
|
|
48
|
+
Provides-Extra: audit
|
|
49
|
+
Requires-Dist: cryptography>=50.0.0; extra == "audit"
|
|
50
|
+
Provides-Extra: otel-sqlalchemy
|
|
51
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == "otel-sqlalchemy"
|
|
52
|
+
Provides-Extra: all
|
|
53
|
+
Requires-Dist: orjson>=3.11.7; extra == "all"
|
|
54
|
+
Requires-Dist: prometheus-client>=0.24.1; extra == "all"
|
|
55
|
+
Requires-Dist: loguru>=0.7.2; extra == "all"
|
|
56
|
+
Requires-Dist: opentelemetry-api>=1.45.1; extra == "all"
|
|
57
|
+
Requires-Dist: opentelemetry-sdk>=1.45.1; extra == "all"
|
|
58
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.45.1; extra == "all"
|
|
59
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.66b1; extra == "all"
|
|
60
|
+
Requires-Dist: opentelemetry-instrumentation-logging>=0.66b1; extra == "all"
|
|
61
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.66b1; extra == "all"
|
|
62
|
+
Requires-Dist: opentelemetry-instrumentation-requests>=0.66b1; extra == "all"
|
|
63
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == "all"
|
|
64
|
+
Requires-Dist: cryptography>=50.0.0; extra == "all"
|
|
65
|
+
Provides-Extra: dev
|
|
66
|
+
Requires-Dist: httpx2>=2.16.0; extra == "dev"
|
|
67
|
+
Requires-Dist: loguru>=0.7.3; extra == "dev"
|
|
68
|
+
Requires-Dist: mypy>=2.4.1; extra == "dev"
|
|
69
|
+
Requires-Dist: pip-audit>=2.10.1; extra == "dev"
|
|
70
|
+
Requires-Dist: pytest>=9.2.0; extra == "dev"
|
|
71
|
+
Requires-Dist: pytest-randomly>=5.0.0; extra == "dev"
|
|
72
|
+
Requires-Dist: ruff>=0.17.3; extra == "dev"
|
|
73
|
+
Requires-Dist: cyclonedx-bom>=7.6.0; extra == "dev"
|
|
74
|
+
Requires-Dist: uvicorn>=0.54.0; extra == "dev"
|
|
75
|
+
Dynamic: license-file
|
|
76
|
+
|
|
77
|
+
# fastapi-observer
|
|
78
|
+
|
|
79
|
+
[](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
|
|
80
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
81
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
82
|
+
[](https://github.com/Vitaee/FastapiObserver/blob/main/LICENSE)
|
|
83
|
+
|
|
84
|
+
**One-call observability for FastAPI.** JSON logs with request IDs, Prometheus metrics and OpenTelemetry traces, with sensitive data redacted by default. No vendor backend: send it to whatever you already run.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from fastapi import FastAPI
|
|
88
|
+
from fastapiobserver import install_observability
|
|
89
|
+
|
|
90
|
+
app = FastAPI()
|
|
91
|
+
install_observability(app)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every request now writes one JSON line, with the same `trace_id` as its trace when tracing is on:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"timestamp": "2026-10-31T09:10:20.345831+00:00",
|
|
99
|
+
"level": "INFO",
|
|
100
|
+
"logger": "fastapiobserver.middleware",
|
|
101
|
+
"message": "request.completed",
|
|
102
|
+
"app_name": "orders-api",
|
|
103
|
+
"service": "orders",
|
|
104
|
+
"environment": "production",
|
|
105
|
+
"version": "2.3.1",
|
|
106
|
+
"request_id": "d496f541-5e5e-4f14-9174-e9bd697faa62",
|
|
107
|
+
"trace_id": "11d5b6f088bfdba501927e55463be4cd",
|
|
108
|
+
"span_id": "50a12c2d9937eacb",
|
|
109
|
+
"event": {
|
|
110
|
+
"method": "GET",
|
|
111
|
+
"path": "/orders/42",
|
|
112
|
+
"status_code": 200,
|
|
113
|
+
"duration_ms": 1.314,
|
|
114
|
+
"client_ip": "203.0.113.7",
|
|
115
|
+
"error_type": "ok"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Documentation:** [vitaee.github.io/FastapiObserver](https://vitaee.github.io/FastapiObserver/)
|
|
121
|
+
|
|
122
|
+
## Install
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install fastapi-observer # JSON logs, request IDs, redaction
|
|
126
|
+
pip install "fastapi-observer[prometheus]" # + Prometheus /metrics
|
|
127
|
+
pip install "fastapi-observer[otel]" # + OpenTelemetry traces and OTLP logs
|
|
128
|
+
pip install "fastapi-observer[all]" # everything
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Requires Python 3.11 or newer.
|
|
132
|
+
|
|
133
|
+
## Configure with environment variables
|
|
134
|
+
|
|
135
|
+
`install_observability(app)` reads its settings from the environment. These are the ones most apps set:
|
|
136
|
+
|
|
137
|
+
| Variable | Example | What it does |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `APP_NAME`, `SERVICE_NAME`, `ENVIRONMENT`, `APP_VERSION` | `orders-api`, `orders`, `production`, `2.3.1` | Added to every log line |
|
|
140
|
+
| `METRICS_ENABLED` | `true` | Serves Prometheus metrics at `/metrics` (needs `[prometheus]`) |
|
|
141
|
+
| `OTEL_ENABLED`, `OTEL_EXPORTER_OTLP_ENDPOINT` | `true`, `http://collector:4317` | Sends traces over OTLP and adds `trace_id` to logs (needs `[otel]`) |
|
|
142
|
+
| `OBS_REDACTION_PRESET` | `strict`, `pci`, `gdpr` | Chooses which fields and headers are masked |
|
|
143
|
+
| `OBS_PROFILE` | `development`, `production` | Sensible defaults: `production` sets `INFO` logs, a larger log queue and the `strict` preset |
|
|
144
|
+
|
|
145
|
+
Explicit variables and Python arguments always win over profile defaults. Every setting is listed in the [configuration guide](https://vitaee.github.io/FastapiObserver/configuration/).
|
|
146
|
+
|
|
147
|
+
## What you get
|
|
148
|
+
|
|
149
|
+
- **One JSON line per request** with method, path, status, duration and client IP, and your service's name, environment and version on every line, including your own `logging` calls.
|
|
150
|
+
- **Request IDs** taken from `X-Request-ID` when the caller is a trusted proxy, generated otherwise, and returned in the response.
|
|
151
|
+
- **Redaction on by default.** Passwords, tokens, API keys, `Authorization` and cookies are masked before anything is written. Presets cover stricter needs (`strict`, `pci`, `gdpr`).
|
|
152
|
+
- **Prometheus metrics** labelled by route template (`/orders/{order_id}`), so URLs with IDs don't explode cardinality.
|
|
153
|
+
- **OpenTelemetry traces** with `trace_id` and `span_id` in every log line, and optional log export over OTLP.
|
|
154
|
+
- **Non-blocking log writes.** Lines are written from a background thread, so a slow stdout never stalls the event loop.
|
|
155
|
+
|
|
156
|
+
Optional extras: runtime log-level control, a tamper-evident audit log, SQLAlchemy, `httpx` and `requests` tracing, Strawberry GraphQL and a Loguru bridge. See the [documentation](https://vitaee.github.io/FastapiObserver/).
|
|
157
|
+
|
|
158
|
+

|
|
159
|
+
|
|
160
|
+
*The Grafana dashboard from [`examples/full_stack`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/full_stack), a Docker Compose stack you can run locally.*
|
|
161
|
+
|
|
162
|
+
## How it compares
|
|
163
|
+
|
|
164
|
+
| | fastapi-observer | structlog or loguru | OpenTelemetry auto-instrumentation | Logfire |
|
|
165
|
+
|---|---|---|---|---|
|
|
166
|
+
| JSON log line per request | Built in | You write the middleware | No | A span per request |
|
|
167
|
+
| Request ID on every log line | Built in | You wire it up | No | Trace IDs |
|
|
168
|
+
| Redaction by default | Yes, with presets | You write a processor | No | Yes |
|
|
169
|
+
| Prometheus `/metrics` | Built in | No | Through an exporter | No |
|
|
170
|
+
| Traces | OpenTelemetry | No | Yes | Yes |
|
|
171
|
+
| Where data goes | stdout, Prometheus and any OTLP backend | stdout or files | Any OTLP backend | Logfire's platform |
|
|
172
|
+
|
|
173
|
+
**When not to use it:**
|
|
174
|
+
|
|
175
|
+
- You only want logs and need the lowest possible overhead: structlog alone does less work per line.
|
|
176
|
+
- You already send everything to Logfire, Datadog or Sentry: their own FastAPI integrations fit better.
|
|
177
|
+
- Your app isn't built on FastAPI or Starlette.
|
|
178
|
+
|
|
179
|
+
What it costs: with default settings, about 43 µs of CPU per request on an Apple M1. See the [benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/), including a comparison with wiring the same stack by hand.
|
|
180
|
+
|
|
181
|
+
## Works with
|
|
182
|
+
|
|
183
|
+
- **Servers:** uvicorn, gunicorn with `UvicornWorker`, and Granian. Uvicorn's duplicate access line is dropped automatically. To get the servers' own startup lines as JSON too, see [running with gunicorn and Granian](https://vitaee.github.io/FastapiObserver/servers/).
|
|
184
|
+
- **Backends:** Prometheus, Grafana (Loki, Tempo), Jaeger and anything that accepts OTLP. See [backends](https://vitaee.github.io/FastapiObserver/backends/).
|
|
185
|
+
- **Error tracking:** runs alongside Sentry. See the [Sentry notes](https://vitaee.github.io/FastapiObserver/sentry/).
|
|
186
|
+
|
|
187
|
+
## Why you can trust it
|
|
188
|
+
|
|
189
|
+
- Tested in CI on Python 3.11 to 3.15, fully typed, and used in production by its maintainer.
|
|
190
|
+
- Releases are published with PyPI Trusted Publishing and signed attestations. CI runs `pip-audit` and generates an SBOM.
|
|
191
|
+
- Security fixes are called out in the [changelog](https://github.com/Vitaee/FastapiObserver/blob/main/CHANGELOG.md). To report a vulnerability, see [SECURITY.md](https://github.com/Vitaee/FastapiObserver/blob/main/SECURITY.md).
|
|
192
|
+
- Benchmarks are reproducible from [`examples/benchmarks`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/benchmarks).
|
|
193
|
+
|
|
194
|
+
## Compatibility
|
|
195
|
+
|
|
196
|
+
| Component | Supported |
|
|
197
|
+
|---|---|
|
|
198
|
+
| Python | `>=3.11` (CI: `3.11` to `3.15`) |
|
|
199
|
+
| FastAPI | `>=0.133.0` |
|
|
200
|
+
| Starlette | `>=1.3.1` |
|
|
201
|
+
| Prometheus extra | `prometheus-client>=0.24.1` |
|
|
202
|
+
| OpenTelemetry extra | `opentelemetry-api/sdk/exporter>=1.45.1` |
|
|
203
|
+
|
|
204
|
+
## Documentation
|
|
205
|
+
|
|
206
|
+
- [Configuration](https://vitaee.github.io/FastapiObserver/configuration/) and [structured logging](https://vitaee.github.io/FastapiObserver/logging/)
|
|
207
|
+
- [Security and redaction presets](https://vitaee.github.io/FastapiObserver/security/)
|
|
208
|
+
- [OpenTelemetry](https://vitaee.github.io/FastapiObserver/opentelemetry/)
|
|
209
|
+
- [Production deployment](https://vitaee.github.io/FastapiObserver/deployment/) and [Kubernetes](https://vitaee.github.io/FastapiObserver/kubernetes/)
|
|
210
|
+
- [Benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/) and [performance tuning](https://vitaee.github.io/FastapiObserver/tuning/)
|
|
211
|
+
- [Contributing](https://vitaee.github.io/FastapiObserver/contributing/)
|
|
212
|
+
|
|
213
|
+
## Support the project
|
|
214
|
+
|
|
215
|
+
If this library saves you time, you can [buy me a coffee](https://buymeacoffee.com/FYbPCSu).
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# fastapi-observer
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
5
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
6
|
+
[](https://github.com/Vitaee/FastapiObserver/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**One-call observability for FastAPI.** JSON logs with request IDs, Prometheus metrics and OpenTelemetry traces, with sensitive data redacted by default. No vendor backend: send it to whatever you already run.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from fastapi import FastAPI
|
|
12
|
+
from fastapiobserver import install_observability
|
|
13
|
+
|
|
14
|
+
app = FastAPI()
|
|
15
|
+
install_observability(app)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Every request now writes one JSON line, with the same `trace_id` as its trace when tracing is on:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"timestamp": "2026-10-31T09:10:20.345831+00:00",
|
|
23
|
+
"level": "INFO",
|
|
24
|
+
"logger": "fastapiobserver.middleware",
|
|
25
|
+
"message": "request.completed",
|
|
26
|
+
"app_name": "orders-api",
|
|
27
|
+
"service": "orders",
|
|
28
|
+
"environment": "production",
|
|
29
|
+
"version": "2.3.1",
|
|
30
|
+
"request_id": "d496f541-5e5e-4f14-9174-e9bd697faa62",
|
|
31
|
+
"trace_id": "11d5b6f088bfdba501927e55463be4cd",
|
|
32
|
+
"span_id": "50a12c2d9937eacb",
|
|
33
|
+
"event": {
|
|
34
|
+
"method": "GET",
|
|
35
|
+
"path": "/orders/42",
|
|
36
|
+
"status_code": 200,
|
|
37
|
+
"duration_ms": 1.314,
|
|
38
|
+
"client_ip": "203.0.113.7",
|
|
39
|
+
"error_type": "ok"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Documentation:** [vitaee.github.io/FastapiObserver](https://vitaee.github.io/FastapiObserver/)
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install fastapi-observer # JSON logs, request IDs, redaction
|
|
50
|
+
pip install "fastapi-observer[prometheus]" # + Prometheus /metrics
|
|
51
|
+
pip install "fastapi-observer[otel]" # + OpenTelemetry traces and OTLP logs
|
|
52
|
+
pip install "fastapi-observer[all]" # everything
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.11 or newer.
|
|
56
|
+
|
|
57
|
+
## Configure with environment variables
|
|
58
|
+
|
|
59
|
+
`install_observability(app)` reads its settings from the environment. These are the ones most apps set:
|
|
60
|
+
|
|
61
|
+
| Variable | Example | What it does |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `APP_NAME`, `SERVICE_NAME`, `ENVIRONMENT`, `APP_VERSION` | `orders-api`, `orders`, `production`, `2.3.1` | Added to every log line |
|
|
64
|
+
| `METRICS_ENABLED` | `true` | Serves Prometheus metrics at `/metrics` (needs `[prometheus]`) |
|
|
65
|
+
| `OTEL_ENABLED`, `OTEL_EXPORTER_OTLP_ENDPOINT` | `true`, `http://collector:4317` | Sends traces over OTLP and adds `trace_id` to logs (needs `[otel]`) |
|
|
66
|
+
| `OBS_REDACTION_PRESET` | `strict`, `pci`, `gdpr` | Chooses which fields and headers are masked |
|
|
67
|
+
| `OBS_PROFILE` | `development`, `production` | Sensible defaults: `production` sets `INFO` logs, a larger log queue and the `strict` preset |
|
|
68
|
+
|
|
69
|
+
Explicit variables and Python arguments always win over profile defaults. Every setting is listed in the [configuration guide](https://vitaee.github.io/FastapiObserver/configuration/).
|
|
70
|
+
|
|
71
|
+
## What you get
|
|
72
|
+
|
|
73
|
+
- **One JSON line per request** with method, path, status, duration and client IP, and your service's name, environment and version on every line, including your own `logging` calls.
|
|
74
|
+
- **Request IDs** taken from `X-Request-ID` when the caller is a trusted proxy, generated otherwise, and returned in the response.
|
|
75
|
+
- **Redaction on by default.** Passwords, tokens, API keys, `Authorization` and cookies are masked before anything is written. Presets cover stricter needs (`strict`, `pci`, `gdpr`).
|
|
76
|
+
- **Prometheus metrics** labelled by route template (`/orders/{order_id}`), so URLs with IDs don't explode cardinality.
|
|
77
|
+
- **OpenTelemetry traces** with `trace_id` and `span_id` in every log line, and optional log export over OTLP.
|
|
78
|
+
- **Non-blocking log writes.** Lines are written from a background thread, so a slow stdout never stalls the event loop.
|
|
79
|
+
|
|
80
|
+
Optional extras: runtime log-level control, a tamper-evident audit log, SQLAlchemy, `httpx` and `requests` tracing, Strawberry GraphQL and a Loguru bridge. See the [documentation](https://vitaee.github.io/FastapiObserver/).
|
|
81
|
+
|
|
82
|
+

|
|
83
|
+
|
|
84
|
+
*The Grafana dashboard from [`examples/full_stack`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/full_stack), a Docker Compose stack you can run locally.*
|
|
85
|
+
|
|
86
|
+
## How it compares
|
|
87
|
+
|
|
88
|
+
| | fastapi-observer | structlog or loguru | OpenTelemetry auto-instrumentation | Logfire |
|
|
89
|
+
|---|---|---|---|---|
|
|
90
|
+
| JSON log line per request | Built in | You write the middleware | No | A span per request |
|
|
91
|
+
| Request ID on every log line | Built in | You wire it up | No | Trace IDs |
|
|
92
|
+
| Redaction by default | Yes, with presets | You write a processor | No | Yes |
|
|
93
|
+
| Prometheus `/metrics` | Built in | No | Through an exporter | No |
|
|
94
|
+
| Traces | OpenTelemetry | No | Yes | Yes |
|
|
95
|
+
| Where data goes | stdout, Prometheus and any OTLP backend | stdout or files | Any OTLP backend | Logfire's platform |
|
|
96
|
+
|
|
97
|
+
**When not to use it:**
|
|
98
|
+
|
|
99
|
+
- You only want logs and need the lowest possible overhead: structlog alone does less work per line.
|
|
100
|
+
- You already send everything to Logfire, Datadog or Sentry: their own FastAPI integrations fit better.
|
|
101
|
+
- Your app isn't built on FastAPI or Starlette.
|
|
102
|
+
|
|
103
|
+
What it costs: with default settings, about 43 µs of CPU per request on an Apple M1. See the [benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/), including a comparison with wiring the same stack by hand.
|
|
104
|
+
|
|
105
|
+
## Works with
|
|
106
|
+
|
|
107
|
+
- **Servers:** uvicorn, gunicorn with `UvicornWorker`, and Granian. Uvicorn's duplicate access line is dropped automatically. To get the servers' own startup lines as JSON too, see [running with gunicorn and Granian](https://vitaee.github.io/FastapiObserver/servers/).
|
|
108
|
+
- **Backends:** Prometheus, Grafana (Loki, Tempo), Jaeger and anything that accepts OTLP. See [backends](https://vitaee.github.io/FastapiObserver/backends/).
|
|
109
|
+
- **Error tracking:** runs alongside Sentry. See the [Sentry notes](https://vitaee.github.io/FastapiObserver/sentry/).
|
|
110
|
+
|
|
111
|
+
## Why you can trust it
|
|
112
|
+
|
|
113
|
+
- Tested in CI on Python 3.11 to 3.15, fully typed, and used in production by its maintainer.
|
|
114
|
+
- Releases are published with PyPI Trusted Publishing and signed attestations. CI runs `pip-audit` and generates an SBOM.
|
|
115
|
+
- Security fixes are called out in the [changelog](https://github.com/Vitaee/FastapiObserver/blob/main/CHANGELOG.md). To report a vulnerability, see [SECURITY.md](https://github.com/Vitaee/FastapiObserver/blob/main/SECURITY.md).
|
|
116
|
+
- Benchmarks are reproducible from [`examples/benchmarks`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/benchmarks).
|
|
117
|
+
|
|
118
|
+
## Compatibility
|
|
119
|
+
|
|
120
|
+
| Component | Supported |
|
|
121
|
+
|---|---|
|
|
122
|
+
| Python | `>=3.11` (CI: `3.11` to `3.15`) |
|
|
123
|
+
| FastAPI | `>=0.133.0` |
|
|
124
|
+
| Starlette | `>=1.3.1` |
|
|
125
|
+
| Prometheus extra | `prometheus-client>=0.24.1` |
|
|
126
|
+
| OpenTelemetry extra | `opentelemetry-api/sdk/exporter>=1.45.1` |
|
|
127
|
+
|
|
128
|
+
## Documentation
|
|
129
|
+
|
|
130
|
+
- [Configuration](https://vitaee.github.io/FastapiObserver/configuration/) and [structured logging](https://vitaee.github.io/FastapiObserver/logging/)
|
|
131
|
+
- [Security and redaction presets](https://vitaee.github.io/FastapiObserver/security/)
|
|
132
|
+
- [OpenTelemetry](https://vitaee.github.io/FastapiObserver/opentelemetry/)
|
|
133
|
+
- [Production deployment](https://vitaee.github.io/FastapiObserver/deployment/) and [Kubernetes](https://vitaee.github.io/FastapiObserver/kubernetes/)
|
|
134
|
+
- [Benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/) and [performance tuning](https://vitaee.github.io/FastapiObserver/tuning/)
|
|
135
|
+
- [Contributing](https://vitaee.github.io/FastapiObserver/contributing/)
|
|
136
|
+
|
|
137
|
+
## Support the project
|
|
138
|
+
|
|
139
|
+
If this library saves you time, you can [buy me a coffee](https://buymeacoffee.com/FYbPCSu).
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-observer
|
|
3
|
+
Version: 1.6.0
|
|
4
|
+
Summary: Zero-glue FastAPI observability with security presets and runtime controls
|
|
5
|
+
Author-email: Vitaee <opensource@vitaee.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Vitaee/FastapiObserver
|
|
8
|
+
Project-URL: Documentation, https://vitaee.github.io/FastapiObserver/
|
|
9
|
+
Project-URL: Repository, https://github.com/Vitaee/FastapiObserver.git
|
|
10
|
+
Project-URL: Issues, https://github.com/Vitaee/FastapiObserver/issues
|
|
11
|
+
Project-URL: Funding, https://buymeacoffee.com/FYbPCSu
|
|
12
|
+
Keywords: fastapi,observability,logging,metrics,opentelemetry
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Framework :: FastAPI
|
|
23
|
+
Classifier: Topic :: System :: Logging
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: fastapi>=0.133.0
|
|
30
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
|
31
|
+
Requires-Dist: starlette>=1.3.1
|
|
32
|
+
Provides-Extra: fast-json
|
|
33
|
+
Requires-Dist: orjson>=3.11.7; extra == "fast-json"
|
|
34
|
+
Provides-Extra: prometheus
|
|
35
|
+
Requires-Dist: prometheus-client>=0.24.1; extra == "prometheus"
|
|
36
|
+
Provides-Extra: loguru
|
|
37
|
+
Requires-Dist: loguru>=0.7.2; extra == "loguru"
|
|
38
|
+
Provides-Extra: otel
|
|
39
|
+
Requires-Dist: opentelemetry-api>=1.45.1; extra == "otel"
|
|
40
|
+
Requires-Dist: opentelemetry-sdk>=1.45.1; extra == "otel"
|
|
41
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.45.1; extra == "otel"
|
|
42
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.66b1; extra == "otel"
|
|
43
|
+
Requires-Dist: opentelemetry-instrumentation-logging>=0.66b1; extra == "otel"
|
|
44
|
+
Provides-Extra: otel-httpx
|
|
45
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.66b1; extra == "otel-httpx"
|
|
46
|
+
Provides-Extra: otel-requests
|
|
47
|
+
Requires-Dist: opentelemetry-instrumentation-requests>=0.66b1; extra == "otel-requests"
|
|
48
|
+
Provides-Extra: audit
|
|
49
|
+
Requires-Dist: cryptography>=50.0.0; extra == "audit"
|
|
50
|
+
Provides-Extra: otel-sqlalchemy
|
|
51
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == "otel-sqlalchemy"
|
|
52
|
+
Provides-Extra: all
|
|
53
|
+
Requires-Dist: orjson>=3.11.7; extra == "all"
|
|
54
|
+
Requires-Dist: prometheus-client>=0.24.1; extra == "all"
|
|
55
|
+
Requires-Dist: loguru>=0.7.2; extra == "all"
|
|
56
|
+
Requires-Dist: opentelemetry-api>=1.45.1; extra == "all"
|
|
57
|
+
Requires-Dist: opentelemetry-sdk>=1.45.1; extra == "all"
|
|
58
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.45.1; extra == "all"
|
|
59
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.66b1; extra == "all"
|
|
60
|
+
Requires-Dist: opentelemetry-instrumentation-logging>=0.66b1; extra == "all"
|
|
61
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.66b1; extra == "all"
|
|
62
|
+
Requires-Dist: opentelemetry-instrumentation-requests>=0.66b1; extra == "all"
|
|
63
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.66b1; extra == "all"
|
|
64
|
+
Requires-Dist: cryptography>=50.0.0; extra == "all"
|
|
65
|
+
Provides-Extra: dev
|
|
66
|
+
Requires-Dist: httpx2>=2.16.0; extra == "dev"
|
|
67
|
+
Requires-Dist: loguru>=0.7.3; extra == "dev"
|
|
68
|
+
Requires-Dist: mypy>=2.4.1; extra == "dev"
|
|
69
|
+
Requires-Dist: pip-audit>=2.10.1; extra == "dev"
|
|
70
|
+
Requires-Dist: pytest>=9.2.0; extra == "dev"
|
|
71
|
+
Requires-Dist: pytest-randomly>=5.0.0; extra == "dev"
|
|
72
|
+
Requires-Dist: ruff>=0.17.3; extra == "dev"
|
|
73
|
+
Requires-Dist: cyclonedx-bom>=7.6.0; extra == "dev"
|
|
74
|
+
Requires-Dist: uvicorn>=0.54.0; extra == "dev"
|
|
75
|
+
Dynamic: license-file
|
|
76
|
+
|
|
77
|
+
# fastapi-observer
|
|
78
|
+
|
|
79
|
+
[](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
|
|
80
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
81
|
+
[](https://pypi.org/project/fastapi-observer/)
|
|
82
|
+
[](https://github.com/Vitaee/FastapiObserver/blob/main/LICENSE)
|
|
83
|
+
|
|
84
|
+
**One-call observability for FastAPI.** JSON logs with request IDs, Prometheus metrics and OpenTelemetry traces, with sensitive data redacted by default. No vendor backend: send it to whatever you already run.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from fastapi import FastAPI
|
|
88
|
+
from fastapiobserver import install_observability
|
|
89
|
+
|
|
90
|
+
app = FastAPI()
|
|
91
|
+
install_observability(app)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every request now writes one JSON line, with the same `trace_id` as its trace when tracing is on:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"timestamp": "2026-10-31T09:10:20.345831+00:00",
|
|
99
|
+
"level": "INFO",
|
|
100
|
+
"logger": "fastapiobserver.middleware",
|
|
101
|
+
"message": "request.completed",
|
|
102
|
+
"app_name": "orders-api",
|
|
103
|
+
"service": "orders",
|
|
104
|
+
"environment": "production",
|
|
105
|
+
"version": "2.3.1",
|
|
106
|
+
"request_id": "d496f541-5e5e-4f14-9174-e9bd697faa62",
|
|
107
|
+
"trace_id": "11d5b6f088bfdba501927e55463be4cd",
|
|
108
|
+
"span_id": "50a12c2d9937eacb",
|
|
109
|
+
"event": {
|
|
110
|
+
"method": "GET",
|
|
111
|
+
"path": "/orders/42",
|
|
112
|
+
"status_code": 200,
|
|
113
|
+
"duration_ms": 1.314,
|
|
114
|
+
"client_ip": "203.0.113.7",
|
|
115
|
+
"error_type": "ok"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Documentation:** [vitaee.github.io/FastapiObserver](https://vitaee.github.io/FastapiObserver/)
|
|
121
|
+
|
|
122
|
+
## Install
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install fastapi-observer # JSON logs, request IDs, redaction
|
|
126
|
+
pip install "fastapi-observer[prometheus]" # + Prometheus /metrics
|
|
127
|
+
pip install "fastapi-observer[otel]" # + OpenTelemetry traces and OTLP logs
|
|
128
|
+
pip install "fastapi-observer[all]" # everything
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Requires Python 3.11 or newer.
|
|
132
|
+
|
|
133
|
+
## Configure with environment variables
|
|
134
|
+
|
|
135
|
+
`install_observability(app)` reads its settings from the environment. These are the ones most apps set:
|
|
136
|
+
|
|
137
|
+
| Variable | Example | What it does |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `APP_NAME`, `SERVICE_NAME`, `ENVIRONMENT`, `APP_VERSION` | `orders-api`, `orders`, `production`, `2.3.1` | Added to every log line |
|
|
140
|
+
| `METRICS_ENABLED` | `true` | Serves Prometheus metrics at `/metrics` (needs `[prometheus]`) |
|
|
141
|
+
| `OTEL_ENABLED`, `OTEL_EXPORTER_OTLP_ENDPOINT` | `true`, `http://collector:4317` | Sends traces over OTLP and adds `trace_id` to logs (needs `[otel]`) |
|
|
142
|
+
| `OBS_REDACTION_PRESET` | `strict`, `pci`, `gdpr` | Chooses which fields and headers are masked |
|
|
143
|
+
| `OBS_PROFILE` | `development`, `production` | Sensible defaults: `production` sets `INFO` logs, a larger log queue and the `strict` preset |
|
|
144
|
+
|
|
145
|
+
Explicit variables and Python arguments always win over profile defaults. Every setting is listed in the [configuration guide](https://vitaee.github.io/FastapiObserver/configuration/).
|
|
146
|
+
|
|
147
|
+
## What you get
|
|
148
|
+
|
|
149
|
+
- **One JSON line per request** with method, path, status, duration and client IP, and your service's name, environment and version on every line, including your own `logging` calls.
|
|
150
|
+
- **Request IDs** taken from `X-Request-ID` when the caller is a trusted proxy, generated otherwise, and returned in the response.
|
|
151
|
+
- **Redaction on by default.** Passwords, tokens, API keys, `Authorization` and cookies are masked before anything is written. Presets cover stricter needs (`strict`, `pci`, `gdpr`).
|
|
152
|
+
- **Prometheus metrics** labelled by route template (`/orders/{order_id}`), so URLs with IDs don't explode cardinality.
|
|
153
|
+
- **OpenTelemetry traces** with `trace_id` and `span_id` in every log line, and optional log export over OTLP.
|
|
154
|
+
- **Non-blocking log writes.** Lines are written from a background thread, so a slow stdout never stalls the event loop.
|
|
155
|
+
|
|
156
|
+
Optional extras: runtime log-level control, a tamper-evident audit log, SQLAlchemy, `httpx` and `requests` tracing, Strawberry GraphQL and a Loguru bridge. See the [documentation](https://vitaee.github.io/FastapiObserver/).
|
|
157
|
+
|
|
158
|
+

|
|
159
|
+
|
|
160
|
+
*The Grafana dashboard from [`examples/full_stack`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/full_stack), a Docker Compose stack you can run locally.*
|
|
161
|
+
|
|
162
|
+
## How it compares
|
|
163
|
+
|
|
164
|
+
| | fastapi-observer | structlog or loguru | OpenTelemetry auto-instrumentation | Logfire |
|
|
165
|
+
|---|---|---|---|---|
|
|
166
|
+
| JSON log line per request | Built in | You write the middleware | No | A span per request |
|
|
167
|
+
| Request ID on every log line | Built in | You wire it up | No | Trace IDs |
|
|
168
|
+
| Redaction by default | Yes, with presets | You write a processor | No | Yes |
|
|
169
|
+
| Prometheus `/metrics` | Built in | No | Through an exporter | No |
|
|
170
|
+
| Traces | OpenTelemetry | No | Yes | Yes |
|
|
171
|
+
| Where data goes | stdout, Prometheus and any OTLP backend | stdout or files | Any OTLP backend | Logfire's platform |
|
|
172
|
+
|
|
173
|
+
**When not to use it:**
|
|
174
|
+
|
|
175
|
+
- You only want logs and need the lowest possible overhead: structlog alone does less work per line.
|
|
176
|
+
- You already send everything to Logfire, Datadog or Sentry: their own FastAPI integrations fit better.
|
|
177
|
+
- Your app isn't built on FastAPI or Starlette.
|
|
178
|
+
|
|
179
|
+
What it costs: with default settings, about 43 µs of CPU per request on an Apple M1. See the [benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/), including a comparison with wiring the same stack by hand.
|
|
180
|
+
|
|
181
|
+
## Works with
|
|
182
|
+
|
|
183
|
+
- **Servers:** uvicorn, gunicorn with `UvicornWorker`, and Granian. Uvicorn's duplicate access line is dropped automatically. To get the servers' own startup lines as JSON too, see [running with gunicorn and Granian](https://vitaee.github.io/FastapiObserver/servers/).
|
|
184
|
+
- **Backends:** Prometheus, Grafana (Loki, Tempo), Jaeger and anything that accepts OTLP. See [backends](https://vitaee.github.io/FastapiObserver/backends/).
|
|
185
|
+
- **Error tracking:** runs alongside Sentry. See the [Sentry notes](https://vitaee.github.io/FastapiObserver/sentry/).
|
|
186
|
+
|
|
187
|
+
## Why you can trust it
|
|
188
|
+
|
|
189
|
+
- Tested in CI on Python 3.11 to 3.15, fully typed, and used in production by its maintainer.
|
|
190
|
+
- Releases are published with PyPI Trusted Publishing and signed attestations. CI runs `pip-audit` and generates an SBOM.
|
|
191
|
+
- Security fixes are called out in the [changelog](https://github.com/Vitaee/FastapiObserver/blob/main/CHANGELOG.md). To report a vulnerability, see [SECURITY.md](https://github.com/Vitaee/FastapiObserver/blob/main/SECURITY.md).
|
|
192
|
+
- Benchmarks are reproducible from [`examples/benchmarks`](https://github.com/Vitaee/FastapiObserver/tree/main/examples/benchmarks).
|
|
193
|
+
|
|
194
|
+
## Compatibility
|
|
195
|
+
|
|
196
|
+
| Component | Supported |
|
|
197
|
+
|---|---|
|
|
198
|
+
| Python | `>=3.11` (CI: `3.11` to `3.15`) |
|
|
199
|
+
| FastAPI | `>=0.133.0` |
|
|
200
|
+
| Starlette | `>=1.3.1` |
|
|
201
|
+
| Prometheus extra | `prometheus-client>=0.24.1` |
|
|
202
|
+
| OpenTelemetry extra | `opentelemetry-api/sdk/exporter>=1.45.1` |
|
|
203
|
+
|
|
204
|
+
## Documentation
|
|
205
|
+
|
|
206
|
+
- [Configuration](https://vitaee.github.io/FastapiObserver/configuration/) and [structured logging](https://vitaee.github.io/FastapiObserver/logging/)
|
|
207
|
+
- [Security and redaction presets](https://vitaee.github.io/FastapiObserver/security/)
|
|
208
|
+
- [OpenTelemetry](https://vitaee.github.io/FastapiObserver/opentelemetry/)
|
|
209
|
+
- [Production deployment](https://vitaee.github.io/FastapiObserver/deployment/) and [Kubernetes](https://vitaee.github.io/FastapiObserver/kubernetes/)
|
|
210
|
+
- [Benchmarks](https://vitaee.github.io/FastapiObserver/benchmarks/) and [performance tuning](https://vitaee.github.io/FastapiObserver/tuning/)
|
|
211
|
+
- [Contributing](https://vitaee.github.io/FastapiObserver/contributing/)
|
|
212
|
+
|
|
213
|
+
## Support the project
|
|
214
|
+
|
|
215
|
+
If this library saves you time, you can [buy me a coffee](https://buymeacoffee.com/FYbPCSu).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.6.0"
|
|
@@ -29,11 +29,16 @@ class StructuredJsonFormatter(logging.Formatter):
|
|
|
29
29
|
Fields include service identity, request ID, trace/span IDs for
|
|
30
30
|
correlation, and user context. Plugin enrichers are applied unless
|
|
31
31
|
the record has ``_skip_enrichers`` set.
|
|
32
|
+
|
|
33
|
+
Without ``settings``, settings and security policy are read from the
|
|
34
|
+
environment, so ``logging.config.dictConfig`` can build it by name
|
|
35
|
+
(``{"()": "fastapiobserver.StructuredJsonFormatter"}``), as server log
|
|
36
|
+
configs for gunicorn and Granian do.
|
|
32
37
|
"""
|
|
33
38
|
|
|
34
39
|
def __init__(
|
|
35
40
|
self,
|
|
36
|
-
settings: ObservabilitySettings,
|
|
41
|
+
settings: ObservabilitySettings | None = None,
|
|
37
42
|
security_policy: SecurityPolicy | None = None,
|
|
38
43
|
*,
|
|
39
44
|
enrich_event: (
|
|
@@ -45,6 +50,9 @@ class StructuredJsonFormatter(logging.Formatter):
|
|
|
45
50
|
) = None,
|
|
46
51
|
) -> None:
|
|
47
52
|
super().__init__()
|
|
53
|
+
if settings is None:
|
|
54
|
+
settings = ObservabilitySettings.from_env()
|
|
55
|
+
security_policy = security_policy or SecurityPolicy.from_env()
|
|
48
56
|
self.settings = settings
|
|
49
57
|
self.security_policy = security_policy or SecurityPolicy()
|
|
50
58
|
self._enrich_event = enrich_event or apply_log_enrichers
|
|
@@ -2,8 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import logging.config
|
|
5
6
|
import time
|
|
6
7
|
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
7
10
|
from fastapiobserver import LOG_SCHEMA_VERSION, ObservabilitySettings, SecurityPolicy
|
|
8
11
|
from fastapiobserver import __version__ as package_version
|
|
9
12
|
from fastapiobserver.logging import StructuredJsonFormatter, setup_logging, shutdown_logging
|
|
@@ -335,3 +338,32 @@ def test_trace_context_filter_standalone() -> None:
|
|
|
335
338
|
f.filter(record)
|
|
336
339
|
# trace_id/span_id should not be set if there is no context
|
|
337
340
|
assert not hasattr(record, "trace_id")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_formatter_built_by_dictconfig_reads_settings_from_env(
|
|
344
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
345
|
+
) -> None:
|
|
346
|
+
# Server log configs (gunicorn logconfig_dict, Granian --log-config) build the
|
|
347
|
+
# formatter by name, without arguments.
|
|
348
|
+
monkeypatch.setenv("APP_NAME", "orders-api")
|
|
349
|
+
monkeypatch.setenv("SERVICE_NAME", "orders")
|
|
350
|
+
logging.config.dictConfig({
|
|
351
|
+
"version": 1,
|
|
352
|
+
"disable_existing_loggers": False,
|
|
353
|
+
"formatters": {"json": {"()": "fastapiobserver.StructuredJsonFormatter"}},
|
|
354
|
+
"handlers": {"server": {"class": "logging.NullHandler", "formatter": "json"}},
|
|
355
|
+
"loggers": {"tests.server": {"handlers": ["server"], "propagate": False}},
|
|
356
|
+
})
|
|
357
|
+
server_logger = logging.getLogger("tests.server")
|
|
358
|
+
try:
|
|
359
|
+
formatter = server_logger.handlers[0].formatter
|
|
360
|
+
assert formatter is not None
|
|
361
|
+
record = server_logger.makeRecord(
|
|
362
|
+
"tests.server", logging.INFO, __file__, 1, "Listening", (), None
|
|
363
|
+
)
|
|
364
|
+
payload = json.loads(formatter.format(record))
|
|
365
|
+
finally:
|
|
366
|
+
server_logger.handlers.clear()
|
|
367
|
+
assert payload["app_name"] == "orders-api"
|
|
368
|
+
assert payload["service"] == "orders"
|
|
369
|
+
assert payload["message"] == "Listening"
|