fastapi-observer 0.1.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 (45) hide show
  1. fastapi_observer-0.1.0/LICENSE +21 -0
  2. fastapi_observer-0.1.0/PKG-INFO +674 -0
  3. fastapi_observer-0.1.0/README.md +611 -0
  4. fastapi_observer-0.1.0/pyproject.toml +99 -0
  5. fastapi_observer-0.1.0/setup.cfg +4 -0
  6. fastapi_observer-0.1.0/src/fastapi_observer.egg-info/PKG-INFO +674 -0
  7. fastapi_observer-0.1.0/src/fastapi_observer.egg-info/SOURCES.txt +43 -0
  8. fastapi_observer-0.1.0/src/fastapi_observer.egg-info/dependency_links.txt +1 -0
  9. fastapi_observer-0.1.0/src/fastapi_observer.egg-info/requires.txt +42 -0
  10. fastapi_observer-0.1.0/src/fastapi_observer.egg-info/top_level.txt +1 -0
  11. fastapi_observer-0.1.0/src/fastapiobserver/__init__.py +114 -0
  12. fastapi_observer-0.1.0/src/fastapiobserver/_version.py +1 -0
  13. fastapi_observer-0.1.0/src/fastapiobserver/config.py +136 -0
  14. fastapi_observer-0.1.0/src/fastapiobserver/control_plane.py +139 -0
  15. fastapi_observer-0.1.0/src/fastapiobserver/fastapi.py +135 -0
  16. fastapi_observer-0.1.0/src/fastapiobserver/logging.py +247 -0
  17. fastapi_observer-0.1.0/src/fastapiobserver/metrics.py +416 -0
  18. fastapi_observer-0.1.0/src/fastapiobserver/middleware.py +410 -0
  19. fastapi_observer-0.1.0/src/fastapiobserver/otel.py +719 -0
  20. fastapi_observer-0.1.0/src/fastapiobserver/plugins.py +65 -0
  21. fastapi_observer-0.1.0/src/fastapiobserver/propagation.py +167 -0
  22. fastapi_observer-0.1.0/src/fastapiobserver/py.typed +1 -0
  23. fastapi_observer-0.1.0/src/fastapiobserver/request_context.py +59 -0
  24. fastapi_observer-0.1.0/src/fastapiobserver/security.py +508 -0
  25. fastapi_observer-0.1.0/src/fastapiobserver/sinks.py +420 -0
  26. fastapi_observer-0.1.0/src/fastapiobserver/utils.py +28 -0
  27. fastapi_observer-0.1.0/tests/test_body_media_allowlist.py +85 -0
  28. fastapi_observer-0.1.0/tests/test_config_settings.py +101 -0
  29. fastapi_observer-0.1.0/tests/test_excluded_url_precedence.py +101 -0
  30. fastapi_observer-0.1.0/tests/test_fastapi_install.py +35 -0
  31. fastapi_observer-0.1.0/tests/test_logging_schema_contract.py +88 -0
  32. fastapi_observer-0.1.0/tests/test_metrics_optional_dependency.py +68 -0
  33. fastapi_observer-0.1.0/tests/test_middleware.py +111 -0
  34. fastapi_observer-0.1.0/tests/test_negotiate_content_type.py +93 -0
  35. fastapi_observer-0.1.0/tests/test_otel_integration.py +123 -0
  36. fastapi_observer-0.1.0/tests/test_otel_log_correlation.py +120 -0
  37. fastapi_observer-0.1.0/tests/test_otel_logs_from_env.py +77 -0
  38. fastapi_observer-0.1.0/tests/test_otel_logs_pipeline.py +72 -0
  39. fastapi_observer-0.1.0/tests/test_otlp_export_integration.py +67 -0
  40. fastapi_observer-0.1.0/tests/test_plugin_isolation.py +61 -0
  41. fastapi_observer-0.1.0/tests/test_redaction_presets.py +71 -0
  42. fastapi_observer-0.1.0/tests/test_route_template_cardinality.py +51 -0
  43. fastapi_observer-0.1.0/tests/test_runtime_control_auth.py +41 -0
  44. fastapi_observer-0.1.0/tests/test_security_redaction.py +83 -0
  45. fastapi_observer-0.1.0/tests/test_trust_boundary.py +53 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vitaee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,674 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-observer
3
+ Version: 0.1.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://github.com/Vitaee/FastapiObserver#readme
9
+ Project-URL: Repository, https://github.com/Vitaee/FastapiObserver.git
10
+ Project-URL: Issues, https://github.com/Vitaee/FastapiObserver/issues
11
+ Keywords: fastapi,observability,logging,metrics,opentelemetry
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
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: Framework :: FastAPI
22
+ Classifier: Topic :: System :: Logging
23
+ Classifier: Topic :: System :: Monitoring
24
+ Requires-Python: <3.15,>=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: fastapi>=0.129.0
28
+ Requires-Dist: pydantic-settings>=2.10.1
29
+ Requires-Dist: starlette>=0.52.1
30
+ Provides-Extra: fast-json
31
+ Requires-Dist: orjson>=3.11.7; extra == "fast-json"
32
+ Provides-Extra: prometheus
33
+ Requires-Dist: prometheus-client>=0.24.1; extra == "prometheus"
34
+ Provides-Extra: otel
35
+ Requires-Dist: opentelemetry-api>=1.39.1; extra == "otel"
36
+ Requires-Dist: opentelemetry-sdk>=1.39.1; extra == "otel"
37
+ Requires-Dist: opentelemetry-exporter-otlp>=1.39.1; extra == "otel"
38
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.60b1; extra == "otel"
39
+ Requires-Dist: opentelemetry-instrumentation-logging>=0.60b1; extra == "otel"
40
+ Provides-Extra: otel-httpx
41
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "otel-httpx"
42
+ Provides-Extra: otel-requests
43
+ Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "otel-requests"
44
+ Provides-Extra: all
45
+ Requires-Dist: orjson>=3.11.7; extra == "all"
46
+ Requires-Dist: prometheus-client>=0.24.1; extra == "all"
47
+ Requires-Dist: opentelemetry-api>=1.39.1; extra == "all"
48
+ Requires-Dist: opentelemetry-sdk>=1.39.1; extra == "all"
49
+ Requires-Dist: opentelemetry-exporter-otlp>=1.39.1; extra == "all"
50
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.60b1; extra == "all"
51
+ Requires-Dist: opentelemetry-instrumentation-logging>=0.60b1; extra == "all"
52
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "all"
53
+ Requires-Dist: opentelemetry-instrumentation-requests>=0.60b1; extra == "all"
54
+ Provides-Extra: dev
55
+ Requires-Dist: httpx>=0.28.1; extra == "dev"
56
+ Requires-Dist: mypy>=1.19.1; extra == "dev"
57
+ Requires-Dist: pip-audit>=2.10.0; extra == "dev"
58
+ Requires-Dist: pytest>=9.0.2; extra == "dev"
59
+ Requires-Dist: pytest-randomly>=3.16.0; extra == "dev"
60
+ Requires-Dist: ruff>=0.15.1; extra == "dev"
61
+ Requires-Dist: cyclonedx-bom>=7.2.1; extra == "dev"
62
+ Dynamic: license-file
63
+
64
+ # fastapi-observer
65
+
66
+ **Zero-glue observability for FastAPI.**
67
+
68
+ `fastapi-observer` gives you structured JSON logs, request correlation, Prometheus metrics, OpenTelemetry tracing, security redaction presets, and runtime controls in one install step and one function call.
69
+
70
+ **Supported Python versions:** `3.10` to `3.14`
71
+
72
+ ---
73
+
74
+ ## Why This Package Exists
75
+
76
+ Most FastAPI services eventually need the same observability plumbing:
77
+ - Structured JSON logging
78
+ - Request and trace correlation
79
+ - Metrics for dashboards and alerts
80
+ - OpenTelemetry setup
81
+ - Redaction/sanitization for sensitive data
82
+ - Runtime controls for incident response
83
+
84
+ Teams usually implement this as custom glue code in every service. That costs engineering time and creates drift between services.
85
+
86
+ `fastapi-observer` replaces this repeated wiring with a consistent, secure-by-default setup.
87
+
88
+ ---
89
+
90
+ ## Read This by Role
91
+
92
+ If you are a new graduate or new to observability:
93
+ 1. Start with [Install](#install)
94
+ 2. Run [5-Minute Quick Start](#5-minute-quick-start)
95
+ 3. Read [Security Defaults and Presets](#security-defaults-and-presets)
96
+ 4. Copy an app from [Examples](#examples)
97
+
98
+ If you are a senior engineer, architect, or CTO:
99
+ 1. Read [What You Get Immediately](#what-you-get-immediately)
100
+ 2. Review [Runtime Control Plane (No Restart)](#runtime-control-plane-no-restart)
101
+ 3. Review [OpenTelemetry (Traces + Optional OTLP Logs)](#opentelemetry-traces--optional-otlp-logs)
102
+ 4. Use [Production Deployment Pattern](#production-deployment-pattern)
103
+
104
+ ---
105
+
106
+ ## What You Get Immediately
107
+
108
+ After one call to `install_observability()`:
109
+
110
+ | Capability | Included | Default |
111
+ |---|---|---|
112
+ | Structured JSON logs | Yes | Enabled |
113
+ | Request ID correlation | Yes | Enabled |
114
+ | Trace/span IDs in logs | Yes (with OTel) | Off until OTel enabled |
115
+ | Prometheus `/metrics` | Yes | Off until `metrics_enabled=True` |
116
+ | Sensitive-data redaction | Yes | Enabled |
117
+ | Security presets (`strict`, `pci`, `gdpr`) | Yes | Available |
118
+ | Runtime control endpoint | Yes | Off until enabled |
119
+ | Plugin hooks for enrichment/hooks | Yes | Available |
120
+
121
+ ---
122
+
123
+ ## Install
124
+
125
+ ```bash
126
+ # Core (logging + metrics + security)
127
+ pip install fastapi-observer
128
+
129
+ # Prometheus metrics support
130
+ pip install "fastapi-observer[prometheus]"
131
+
132
+ # OpenTelemetry tracing/logs support
133
+ pip install "fastapi-observer[otel]"
134
+
135
+ # Everything
136
+ pip install "fastapi-observer[all]"
137
+ ```
138
+
139
+ Import path:
140
+
141
+ ```python
142
+ import fastapiobserver
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 5-Minute Quick Start
148
+
149
+ ```python
150
+ from fastapi import FastAPI
151
+ from fastapiobserver import ObservabilitySettings, install_observability
152
+
153
+ app = FastAPI()
154
+
155
+ settings = ObservabilitySettings(
156
+ app_name="orders-api",
157
+ service="orders",
158
+ environment="production",
159
+ version="0.1.0",
160
+ metrics_enabled=True,
161
+ )
162
+
163
+ install_observability(app, settings)
164
+
165
+
166
+ @app.get("/orders/{order_id}")
167
+ def get_order(order_id: int) -> dict[str, int]:
168
+ return {"order_id": order_id}
169
+ ```
170
+
171
+ Run:
172
+
173
+ ```bash
174
+ uvicorn main:app --reload
175
+ ```
176
+
177
+ Now you have:
178
+ - Structured request logs on every request
179
+ - Request ID propagation
180
+ - Sanitized event payloads
181
+ - Prometheus metrics at `/metrics`
182
+
183
+ ---
184
+
185
+ ## Security Defaults and Presets
186
+
187
+ ### Default protections
188
+
189
+ | Protection | Default | Why |
190
+ |---|---|---|
191
+ | Body logging | `OFF` | Avoid leaking request/response secrets |
192
+ | Sensitive key masking | `ON` | Protect fields like `password`, `token`, `secret` |
193
+ | Sensitive header masking | `ON` | Protect `authorization`, `cookie`, `x-api-key` |
194
+ | Query string in logged path | Excluded | Prevent accidental token leakage |
195
+ | Request ID trust boundary | Trusted CIDRs only | Prevent spoofed correlation IDs |
196
+
197
+ ### Presets for regulated environments
198
+
199
+ ```python
200
+ from fastapiobserver import SecurityPolicy
201
+
202
+ # Strictest option: drop sensitive values and keep minimal safe headers
203
+ strict_policy = SecurityPolicy.from_preset("strict")
204
+
205
+ # PCI-focused redaction fields
206
+ pci_policy = SecurityPolicy.from_preset("pci")
207
+
208
+ # GDPR-focused hashed PII fields
209
+ gdpr_policy = SecurityPolicy.from_preset("gdpr")
210
+ ```
211
+
212
+ Use a preset in installation:
213
+
214
+ ```python
215
+ install_observability(app, settings, security_policy=SecurityPolicy.from_preset("pci"))
216
+ ```
217
+
218
+ ### Allowlist-only logging (audit-style)
219
+
220
+ If your compliance model is "log only approved fields", use allowlists:
221
+
222
+ ```python
223
+ from fastapiobserver import SecurityPolicy
224
+
225
+ policy = SecurityPolicy(
226
+ header_allowlist=("x-request-id", "content-type", "user-agent"),
227
+ event_key_allowlist=("method", "path", "status_code"),
228
+ )
229
+ ```
230
+
231
+ ### Body capture media-type guard
232
+
233
+ ```python
234
+ policy = SecurityPolicy(
235
+ log_request_body=True,
236
+ body_capture_media_types=("application/json",),
237
+ )
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Runtime Control Plane (No Restart)
243
+
244
+ Use runtime controls when you need higher log verbosity or different trace sampling during an incident.
245
+
246
+ ```bash
247
+ export OBSERVABILITY_CONTROL_TOKEN="replace-me"
248
+ ```
249
+
250
+ ```python
251
+ from fastapiobserver import RuntimeControlSettings, install_observability
252
+
253
+ runtime_control = RuntimeControlSettings(enabled=True)
254
+ install_observability(app, settings, runtime_control_settings=runtime_control)
255
+ ```
256
+
257
+ Inspect current runtime values:
258
+
259
+ ```bash
260
+ curl -X GET http://localhost:8000/_observability/control \
261
+ -H "Authorization: Bearer replace-me"
262
+ ```
263
+
264
+ Update runtime values:
265
+
266
+ ```bash
267
+ curl -X POST http://localhost:8000/_observability/control \
268
+ -H "Authorization: Bearer replace-me" \
269
+ -H "Content-Type: application/json" \
270
+ -d '{"log_level":"DEBUG","trace_sampling_ratio":0.25}'
271
+ ```
272
+
273
+ What changes immediately:
274
+ - Root logger level (and uvicorn loggers)
275
+ - Dynamic OTel trace sampling ratio
276
+
277
+ ---
278
+
279
+ ## OpenTelemetry (Traces + Optional OTLP Logs)
280
+
281
+ ```python
282
+ from fastapiobserver import OTelLogsSettings, OTelSettings, install_observability
283
+
284
+ otel_settings = OTelSettings(
285
+ enabled=True,
286
+ service_name="orders-api",
287
+ service_version="2.0.0",
288
+ environment="production",
289
+ otlp_endpoint="http://localhost:4317",
290
+ protocol="grpc", # or "http/protobuf"
291
+ trace_sampling_ratio=1.0,
292
+ extra_resource_attributes={
293
+ "k8s.namespace": "prod",
294
+ "team": "backend",
295
+ },
296
+ )
297
+
298
+ otel_logs_settings = OTelLogsSettings(
299
+ enabled=True,
300
+ logs_mode="both", # "local_json", "otlp", or "both"
301
+ otlp_endpoint="http://localhost:4317",
302
+ protocol="grpc",
303
+ )
304
+
305
+ install_observability(
306
+ app,
307
+ settings,
308
+ otel_settings=otel_settings,
309
+ otel_logs_settings=otel_logs_settings,
310
+ )
311
+ ```
312
+
313
+ Design details:
314
+ - Reuses an externally configured tracer provider if one already exists.
315
+ - Injects trace IDs into application logs for log-trace correlation.
316
+ - Supports runtime sampling updates through the control plane.
317
+ - Sends OTel logs in OTLP mode with the same sanitization policy.
318
+
319
+ ---
320
+
321
+ ## What `install_observability()` Wires Up
322
+
323
+ 1. Structured logging pipeline (JSON formatter + async queue handler).
324
+ 2. Metrics backend and `/metrics` endpoint when metrics are enabled.
325
+ 3. OTel tracing setup when OTel is enabled.
326
+ 4. Request logging middleware with sanitization and context cleanup.
327
+ 5. Runtime control endpoint when runtime control is enabled.
328
+
329
+ Request path lifecycle (high-level):
330
+
331
+ ```text
332
+ Request arrives
333
+ -> request ID / trace context resolved
334
+ -> app handler executes
335
+ -> response classified (ok/client_error/server_error/exception)
336
+ -> payload sanitized by policy
337
+ -> log emitted + metrics recorded
338
+ -> context cleared
339
+ ```
340
+
341
+ ---
342
+
343
+ ## Example JSON Log Event
344
+
345
+ ```json
346
+ {
347
+ "timestamp": "2026-02-18T10:30:00.000000+00:00",
348
+ "level": "INFO",
349
+ "logger": "fastapiobserver.middleware",
350
+ "message": "request.completed",
351
+ "app_name": "orders-api",
352
+ "service": "orders",
353
+ "environment": "production",
354
+ "version": "0.1.0",
355
+ "log_schema_version": "1.0.0",
356
+ "library": "fastapiobserver",
357
+ "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
358
+ "trace_id": "0af7651916cd43dd8448eb211c80319c",
359
+ "span_id": "b7ad6b7169203331",
360
+ "event": {
361
+ "method": "GET",
362
+ "path": "/orders/42",
363
+ "status_code": 200,
364
+ "duration_ms": 3.456,
365
+ "client_ip": "10.0.0.1",
366
+ "error_type": "ok"
367
+ }
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Production Deployment Pattern
374
+
375
+ Recommended topology:
376
+ - FastAPI services emit logs/metrics/traces.
377
+ - Prometheus scrapes each service's `/metrics`.
378
+ - Traces/logs are shipped through an OpenTelemetry Collector (or Alloy).
379
+ - Grafana/Tempo/Loki read from centralized backends.
380
+
381
+ Minimal collector reference:
382
+
383
+ ```yaml
384
+ receivers:
385
+ otlp:
386
+ protocols:
387
+ grpc:
388
+ endpoint: 0.0.0.0:4317
389
+ http:
390
+ endpoint: 0.0.0.0:4318
391
+
392
+ processors:
393
+ memory_limiter:
394
+ limit_mib: 512
395
+ spike_limit_mib: 128
396
+ check_interval: 5s
397
+ batch:
398
+ send_batch_size: 512
399
+ timeout: 5s
400
+
401
+ exporters:
402
+ otlp/tempo:
403
+ endpoint: tempo:4317
404
+ otlp/loki:
405
+ endpoint: loki:3100
406
+
407
+ service:
408
+ pipelines:
409
+ traces:
410
+ receivers: [otlp]
411
+ processors: [memory_limiter, batch]
412
+ exporters: [otlp/tempo]
413
+ logs:
414
+ receivers: [otlp]
415
+ processors: [memory_limiter, batch]
416
+ exporters: [otlp/loki]
417
+ ```
418
+
419
+ Operational notes for leadership:
420
+ - Use TLS and authenticated exporters in production.
421
+ - Keep high-fidelity traces for errors and slow paths; sample the rest.
422
+ - Keep one policy for sanitization across local and OTLP sinks.
423
+
424
+ ---
425
+
426
+ ## Examples
427
+
428
+ The `examples/` directory contains runnable demos:
429
+
430
+ | Example | What it shows |
431
+ |---|---|
432
+ | [`basic_app.py`](examples/basic_app.py) | Minimal setup and request logging |
433
+ | [`security_presets_app.py`](examples/security_presets_app.py) | Preset-based security policy |
434
+ | [`allowlist_app.py`](examples/allowlist_app.py) | Allowlist-only sanitization |
435
+ | [`otel_app.py`](examples/otel_app.py) | OTel tracing and resource attributes |
436
+ | [`full_stack/`](examples/full_stack/) | **Docker Compose stack**: 3 FastAPI services + Grafana + Prometheus + Loki + Tempo |
437
+
438
+ Run an example:
439
+
440
+ ```bash
441
+ uvicorn examples.basic_app:app --reload
442
+ ```
443
+
444
+ ### Dashboard Screenshots (Full-Stack Demo)
445
+
446
+ From `examples/full_stack`, these are real Grafana views generated by `fastapi-observer` telemetry:
447
+
448
+ **Overview panels (latency heatmap, route throughput, errors, CPU/memory):**
449
+
450
+ ![FastAPI Observer dashboard overview](examples/full_stack/screenshot/dashboard_top.png)
451
+
452
+ **Percentiles, request rate, and structured JSON logs in Loki:**
453
+
454
+ ![FastAPI Observer dashboard logs and percentiles](examples/full_stack/screenshot/dashboard_bottom.png)
455
+
456
+ ---
457
+
458
+ ## Environment Variables
459
+
460
+ The library supports configuration from code and env vars. Below are the most relevant env vars by area.
461
+
462
+ ### Identity and logging
463
+
464
+ | Variable | Default | Description |
465
+ |---|---|---|
466
+ | `APP_NAME` | `app` | Namespace for app-level identity |
467
+ | `SERVICE_NAME` | `api` | Service label for logs/metrics |
468
+ | `ENVIRONMENT` | `development` | Environment label |
469
+ | `APP_VERSION` | `0.0.0` | Service version |
470
+ | `LOG_LEVEL` | `INFO` | Root log level |
471
+ | `LOG_DIR` | - | Optional file log directory |
472
+ | `REQUEST_ID_HEADER` | `x-request-id` | Incoming request ID header |
473
+ | `RESPONSE_REQUEST_ID_HEADER` | `x-request-id` | Response request ID header |
474
+
475
+ ### Metrics
476
+
477
+ | Variable | Default | Description |
478
+ |---|---|---|
479
+ | `METRICS_ENABLED` | `false` | Enable metrics backend |
480
+ | `METRICS_PATH` | `/metrics` | Metrics endpoint path |
481
+ | `METRICS_EXCLUDE_PATHS` | `/metrics,/health,/healthz,/docs,/openapi.json` | Skip metrics for noisy endpoints |
482
+ | `METRICS_EXEMPLARS_ENABLED` | `false` | Enable exemplars where supported |
483
+ | `METRICS_FORMAT` | `negotiate` | `prometheus`, `openmetrics`, or `negotiate` |
484
+
485
+ ### Security and trust boundary
486
+
487
+ | Variable | Default | Description |
488
+ |---|---|---|
489
+ | `OBS_REDACTION_PRESET` | - | `strict`, `pci`, `gdpr` |
490
+ | `OBS_REDACTED_FIELDS` | built-in list | CSV keys to redact |
491
+ | `OBS_REDACTED_HEADERS` | built-in list | CSV headers to redact |
492
+ | `OBS_REDACTION_MODE` | `mask` | `mask`, `hash`, `drop` |
493
+ | `OBS_MASK_TEXT` | `***` | Mask replacement text |
494
+ | `OBS_LOG_REQUEST_BODY` | `false` | Enable request body logging |
495
+ | `OBS_LOG_RESPONSE_BODY` | `false` | Enable response body logging |
496
+ | `OBS_MAX_BODY_LENGTH` | `256` | Max captured body bytes |
497
+ | `OBS_HEADER_ALLOWLIST` | - | CSV headers allowed in logs |
498
+ | `OBS_EVENT_KEY_ALLOWLIST` | - | CSV event keys allowed in logs |
499
+ | `OBS_BODY_CAPTURE_MEDIA_TYPES` | - | CSV allowed media types for body capture |
500
+ | `OBS_TRUSTED_PROXY_ENABLED` | `true` | Enable trusted-proxy policy |
501
+ | `OBS_TRUSTED_CIDRS` | RFC1918 + loopback | CSV trusted CIDRs |
502
+ | `OBS_HONOR_FORWARDED_HEADERS` | `false` | Trust forwarded headers |
503
+
504
+ Notes:
505
+ - `OBS_HEADER_ALLOWLIST`, `OBS_EVENT_KEY_ALLOWLIST`, and `OBS_BODY_CAPTURE_MEDIA_TYPES` accept `none`, `null`, or `unset` to clear values.
506
+
507
+ ### OpenTelemetry tracing/log export
508
+
509
+ | Variable | Default | Description |
510
+ |---|---|---|
511
+ | `OTEL_ENABLED` | `false` | Enable tracing instrumentation |
512
+ | `OTEL_SERVICE_NAME` | `SERVICE_NAME` | OTel service name override |
513
+ | `OTEL_SERVICE_VERSION` | `APP_VERSION` | OTel service version override |
514
+ | `OTEL_ENVIRONMENT` | `ENVIRONMENT` | OTel environment override |
515
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | - | OTLP endpoint |
516
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc` | `grpc` or `http/protobuf` |
517
+ | `OTEL_TRACE_SAMPLING_RATIO` | `1.0` | Initial trace sampling ratio |
518
+ | `OTEL_EXTRA_RESOURCE_ATTRIBUTES` | - | CSV `key=value` pairs |
519
+ | `OTEL_EXCLUDED_URLS` | auto-derived | CSV excluded paths for tracing |
520
+ | `OTEL_LOGS_ENABLED` | `false` | Enable OTLP log export |
521
+ | `OTEL_LOGS_MODE` | `local_json` | `local_json`, `otlp`, `both` |
522
+ | `OTEL_LOGS_ENDPOINT` | - | OTLP logs endpoint |
523
+ | `OTEL_LOGS_PROTOCOL` | `grpc` | `grpc` or `http/protobuf` |
524
+
525
+ ### Runtime control plane
526
+
527
+ | Variable | Default | Description |
528
+ |---|---|---|
529
+ | `OBS_RUNTIME_CONTROL_ENABLED` | `false` | Enable runtime control endpoint |
530
+ | `OBS_RUNTIME_CONTROL_PATH` | `/_observability/control` | Control endpoint path |
531
+ | `OBS_RUNTIME_CONTROL_TOKEN_ENV_VAR` | `OBSERVABILITY_CONTROL_TOKEN` | Name of env var containing bearer token |
532
+ | `OBSERVABILITY_CONTROL_TOKEN` | - | Bearer token value used for auth |
533
+
534
+ ### Optional Logtail sink
535
+
536
+ | Variable | Default | Description |
537
+ |---|---|---|
538
+ | `LOGTAIL_ENABLED` | `false` | Enable Better Stack Logtail sink |
539
+ | `LOGTAIL_SOURCE_TOKEN` | - | Logtail source token |
540
+ | `LOGTAIL_BATCH_SIZE` | `50` | Batch size for shipping |
541
+ | `LOGTAIL_FLUSH_INTERVAL` | `2.0` | Flush interval (seconds) |
542
+
543
+ ---
544
+
545
+ ## Advanced Operations
546
+
547
+ ### Middleware ordering for body capture
548
+
549
+ If body capture is enabled, install observability before other middleware:
550
+
551
+ ```python
552
+ from fastapi.middleware.cors import CORSMiddleware
553
+ from fastapiobserver import SecurityPolicy, install_observability
554
+
555
+ install_observability(app, settings, security_policy=SecurityPolicy(log_request_body=True))
556
+ app.add_middleware(CORSMiddleware, allow_origins=["*"])
557
+ ```
558
+
559
+ ### Multi-worker Gunicorn with Prometheus
560
+
561
+ ```bash
562
+ export PROMETHEUS_MULTIPROC_DIR=/tmp/prometheus-metrics
563
+ rm -rf "$PROMETHEUS_MULTIPROC_DIR"
564
+ mkdir -p "$PROMETHEUS_MULTIPROC_DIR"
565
+ ```
566
+
567
+ `gunicorn.conf.py`:
568
+
569
+ ```python
570
+ from fastapiobserver import mark_prometheus_process_dead
571
+
572
+
573
+ def child_exit(server, worker):
574
+ mark_prometheus_process_dead(worker.pid)
575
+ ```
576
+
577
+ ---
578
+
579
+ ## Plugin Hooks
580
+
581
+ Extend behavior without editing package internals:
582
+
583
+ ```python
584
+ from fastapiobserver import register_log_enricher, register_metric_hook
585
+
586
+
587
+ def add_git_sha(payload: dict) -> dict:
588
+ payload["git_sha"] = "abc123"
589
+ return payload
590
+
591
+
592
+ def track_slow_requests(request, response, duration):
593
+ if duration > 1.0:
594
+ print(f"slow request: {request.url.path} {duration:.2f}s")
595
+
596
+
597
+ register_log_enricher("git_sha", add_git_sha)
598
+ register_metric_hook("slow_requests", track_slow_requests)
599
+ ```
600
+
601
+ Plugin failures are isolated and do not crash request handling.
602
+
603
+ ---
604
+
605
+ ## OTel Test Coverage
606
+
607
+ Repository integration tests include:
608
+ - `tests/test_otel_log_correlation.py`: verifies trace/span IDs in logs map to real spans.
609
+ - `tests/test_otlp_export_integration.py`: validates OTLP HTTP export with local collector fixtures.
610
+
611
+ ---
612
+
613
+ ## Release Tracks
614
+
615
+ - `0.1.x`: secure-by-default core
616
+ - `0.2.x`: OTel interoperability, security presets, allowlists
617
+ - `1.0.0`: dynamic runtime controls and plugin stability
618
+
619
+ Current release version: `0.1.0`
620
+
621
+ ## Changelog Policy
622
+
623
+ Breaking changes must be listed under a `Breaking Changes` section in `CHANGELOG.md`.
624
+
625
+ ---
626
+
627
+ ## Packaging and Publishing (Maintainers)
628
+
629
+ ### 1) Build distributions
630
+
631
+ ```bash
632
+ python -m pip install --upgrade pip build
633
+ python -m build
634
+ ```
635
+
636
+ ### 2) Upload to TestPyPI
637
+
638
+ ```bash
639
+ python -m pip install --upgrade twine
640
+ python -m twine upload --repository testpypi dist/*
641
+ ```
642
+
643
+ ### 3) Validate install from TestPyPI
644
+
645
+ ```bash
646
+ python -m pip install \
647
+ --extra-index-url https://test.pypi.org/simple/ \
648
+ fastapi-observer
649
+ ```
650
+
651
+ ### 4) Upload to production PyPI
652
+
653
+ ```bash
654
+ python -m twine upload dist/*
655
+ ```
656
+
657
+ ---
658
+
659
+ ## Local Git Hook (Recommended)
660
+
661
+ ```bash
662
+ git config core.hooksPath .githooks
663
+ ```
664
+
665
+ The pre-push hook runs:
666
+ - `uv run ruff check`
667
+ - `uv run mypy src`
668
+ - `uv run pytest -q`
669
+
670
+ ---
671
+
672
+ ## Roadmap Tracking
673
+
674
+ See [NEXT_STEPS.md](NEXT_STEPS.md) for the active `0.2.0` roadmap and release checklist.