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.
Files changed (124) hide show
  1. fastapi_observer-1.6.0/PKG-INFO +215 -0
  2. fastapi_observer-1.6.0/README.md +139 -0
  3. fastapi_observer-1.6.0/src/fastapi_observer.egg-info/PKG-INFO +215 -0
  4. fastapi_observer-1.6.0/src/fastapiobserver/_version.py +1 -0
  5. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/formatter.py +9 -1
  6. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_logging_schema_contract.py +32 -0
  7. fastapi_observer-1.5.0/PKG-INFO +0 -249
  8. fastapi_observer-1.5.0/README.md +0 -173
  9. fastapi_observer-1.5.0/src/fastapi_observer.egg-info/PKG-INFO +0 -249
  10. fastapi_observer-1.5.0/src/fastapiobserver/_version.py +0 -1
  11. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/LICENSE +0 -0
  12. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/pyproject.toml +0 -0
  13. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/setup.cfg +0 -0
  14. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/SOURCES.txt +0 -0
  15. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/dependency_links.txt +0 -0
  16. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/requires.txt +0 -0
  17. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapi_observer.egg-info/top_level.txt +0 -0
  18. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/__init__.py +0 -0
  19. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/__init__.py +0 -0
  20. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/formatter.py +0 -0
  21. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/providers.py +0 -0
  22. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/audit/verify.py +0 -0
  23. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/config.py +0 -0
  24. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/control_plane.py +0 -0
  25. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/db_tracing.py +0 -0
  26. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/fastapi.py +0 -0
  27. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/integrations/__init__.py +0 -0
  28. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/integrations/strawberry.py +0 -0
  29. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/__init__.py +0 -0
  30. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/circuit_breaker.py +0 -0
  31. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/filters.py +0 -0
  32. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/queueing.py +0 -0
  33. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/setup.py +0 -0
  34. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/logging/state.py +0 -0
  35. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/loguru.py +0 -0
  36. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/__init__.py +0 -0
  37. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/builder.py +0 -0
  38. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/contracts.py +0 -0
  39. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/endpoint.py +0 -0
  40. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/noop.py +0 -0
  41. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/pathing.py +0 -0
  42. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/__init__.py +0 -0
  43. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/backend.py +0 -0
  44. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/client.py +0 -0
  45. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/collector.py +0 -0
  46. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/exemplars.py +0 -0
  47. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/prometheus/multiprocess.py +0 -0
  48. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/metrics/registry.py +0 -0
  49. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/__init__.py +0 -0
  50. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/body_capture.py +0 -0
  51. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/context.py +0 -0
  52. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/events.py +0 -0
  53. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/headers.py +0 -0
  54. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/ip.py +0 -0
  55. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/metrics.py +0 -0
  56. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/request_logging.py +0 -0
  57. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/middleware/span_errors.py +0 -0
  58. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/__init__.py +0 -0
  59. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/exclusions.py +0 -0
  60. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/lifecycle.py +0 -0
  61. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/logs.py +0 -0
  62. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/metrics.py +0 -0
  63. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/resource.py +0 -0
  64. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/settings.py +0 -0
  65. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/tracing.py +0 -0
  66. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/otel/types.py +0 -0
  67. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/plugins.py +0 -0
  68. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/profiles.py +0 -0
  69. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/propagation.py +0 -0
  70. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/py.typed +0 -0
  71. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/request_context.py +0 -0
  72. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/__init__.py +0 -0
  73. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/normalize.py +0 -0
  74. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/policy.py +0 -0
  75. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/proxies.py +0 -0
  76. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/security/redaction.py +0 -0
  77. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/__init__.py +0 -0
  78. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/builtin.py +0 -0
  79. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/discovery.py +0 -0
  80. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/factory.py +0 -0
  81. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/__init__.py +0 -0
  82. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/dlq.py +0 -0
  83. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/handler.py +0 -0
  84. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/logtail/sink.py +0 -0
  85. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/protocol.py +0 -0
  86. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/registry.py +0 -0
  87. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/sinks/stats.py +0 -0
  88. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/src/fastapiobserver/utils.py +0 -0
  89. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_audit_chain.py +0 -0
  90. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_body_media_allowlist.py +0 -0
  91. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_config_settings.py +0 -0
  92. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_db_tracing.py +0 -0
  93. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_dx_parity.py +0 -0
  94. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_excluded_url_precedence.py +0 -0
  95. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_fastapi_install.py +0 -0
  96. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_fork_queue_listener.py +0 -0
  97. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_graphql_integrations.py +0 -0
  98. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_lifespan_teardown.py +0 -0
  99. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_log_queue_controls.py +0 -0
  100. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_logtail_dlq.py +0 -0
  101. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_loguru_bridge.py +0 -0
  102. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_metrics_contracts.py +0 -0
  103. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_metrics_optional_dependency.py +0 -0
  104. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_middleware.py +0 -0
  105. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_negotiate_content_type.py +0 -0
  106. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_integration.py +0 -0
  107. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_lifecycle.py +0 -0
  108. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_log_correlation.py +0 -0
  109. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_logs_from_env.py +0 -0
  110. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_logs_pipeline.py +0 -0
  111. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otel_metrics_from_env.py +0 -0
  112. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_otlp_export_integration.py +0 -0
  113. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_plugin_isolation.py +0 -0
  114. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_profiles.py +0 -0
  115. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_propagation.py +0 -0
  116. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_redaction_presets.py +0 -0
  117. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_route_template_cardinality.py +0 -0
  118. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_runtime_control_auth.py +0 -0
  119. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_security_audit_fixes.py +0 -0
  120. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_security_redaction.py +0 -0
  121. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_sink_circuit_breaker.py +0 -0
  122. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_sinks.py +0 -0
  123. {fastapi_observer-1.5.0 → fastapi_observer-1.6.0}/tests/test_trust_boundary.py +0 -0
  124. {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
+ [![CI](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml/badge.svg)](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
80
+ [![PyPI](https://img.shields.io/pypi/v/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
81
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
82
+ [![License](https://img.shields.io/pypi/l/fastapi-observer)](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
+ ![Grafana dashboard built from fastapi-observer metrics, traces and logs](https://raw.githubusercontent.com/Vitaee/FastapiObserver/main/examples/full_stack/screenshot/dashboard_top.png)
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
+ [![CI](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml/badge.svg)](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
6
+ [![License](https://img.shields.io/pypi/l/fastapi-observer)](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
+ ![Grafana dashboard built from fastapi-observer metrics, traces and logs](https://raw.githubusercontent.com/Vitaee/FastapiObserver/main/examples/full_stack/screenshot/dashboard_top.png)
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
+ [![CI](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml/badge.svg)](https://github.com/Vitaee/FastapiObserver/actions/workflows/ci.yml)
80
+ [![PyPI](https://img.shields.io/pypi/v/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
81
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi-observer)](https://pypi.org/project/fastapi-observer/)
82
+ [![License](https://img.shields.io/pypi/l/fastapi-observer)](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
+ ![Grafana dashboard built from fastapi-observer metrics, traces and logs](https://raw.githubusercontent.com/Vitaee/FastapiObserver/main/examples/full_stack/screenshot/dashboard_top.png)
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"