logbrew-fastapi 0.1.0__py3-none-any.whl
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.
- logbrew_fastapi/__init__.py +258 -0
- logbrew_fastapi/examples/__init__.py +1 -0
- logbrew_fastapi/examples/__main__.py +29 -0
- logbrew_fastapi/examples/readme_example.py +31 -0
- logbrew_fastapi/examples/real_user_smoke.py +60 -0
- logbrew_fastapi/py.typed +1 -0
- logbrew_fastapi-0.1.0.dist-info/METADATA +74 -0
- logbrew_fastapi-0.1.0.dist-info/RECORD +10 -0
- logbrew_fastapi-0.1.0.dist-info/WHEEL +5 -0
- logbrew_fastapi-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""FastAPI integration helpers for capturing LogBrew request spans and exceptions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from datetime import UTC, datetime
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from fastapi import FastAPI, Request, Response
|
|
13
|
+
from logbrew_sdk import (
|
|
14
|
+
LogBrewClient,
|
|
15
|
+
RecordingTransport,
|
|
16
|
+
SdkError,
|
|
17
|
+
SpanAttributes,
|
|
18
|
+
TransportError,
|
|
19
|
+
parse_traceparent,
|
|
20
|
+
span_attributes_from_traceparent,
|
|
21
|
+
)
|
|
22
|
+
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
|
23
|
+
from starlette.types import ASGIApp
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(slots=True)
|
|
27
|
+
class LogBrewFastAPIConfig:
|
|
28
|
+
"""Runtime options used by the LogBrew FastAPI middleware."""
|
|
29
|
+
|
|
30
|
+
client: LogBrewClient
|
|
31
|
+
transport: RecordingTransport | None = None
|
|
32
|
+
capture_successful_requests: bool = True
|
|
33
|
+
capture_exceptions: bool = True
|
|
34
|
+
flush_on_response: bool = True
|
|
35
|
+
raise_flush_errors: bool = False
|
|
36
|
+
service_name: str = "fastapi"
|
|
37
|
+
span_id_factory: Callable[[], str] | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def utc_timestamp() -> str:
|
|
41
|
+
"""Return a LogBrew-compatible UTC timestamp."""
|
|
42
|
+
|
|
43
|
+
return datetime.now(UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def request_name(request: Request) -> str:
|
|
47
|
+
"""Return the stable request name used for span and issue titles."""
|
|
48
|
+
|
|
49
|
+
return f"{request.method} {request.url.path}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def request_metadata(
|
|
53
|
+
request: Request,
|
|
54
|
+
*,
|
|
55
|
+
status_code: int | None = None,
|
|
56
|
+
duration_ms: float | None = None,
|
|
57
|
+
) -> dict[str, Any]:
|
|
58
|
+
"""Return metadata that is useful for request-level troubleshooting without including query strings."""
|
|
59
|
+
|
|
60
|
+
metadata: dict[str, Any] = {
|
|
61
|
+
"framework": "fastapi",
|
|
62
|
+
"method": request.method,
|
|
63
|
+
"path": request.url.path,
|
|
64
|
+
}
|
|
65
|
+
route = request.scope.get("route")
|
|
66
|
+
route_path = getattr(route, "path", None)
|
|
67
|
+
if isinstance(route_path, str):
|
|
68
|
+
metadata["route"] = route_path
|
|
69
|
+
if status_code is not None:
|
|
70
|
+
metadata["status_code"] = status_code
|
|
71
|
+
if duration_ms is not None:
|
|
72
|
+
metadata["duration_ms"] = round(duration_ms, 3)
|
|
73
|
+
return metadata
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def capture_request_span(
|
|
77
|
+
client: LogBrewClient,
|
|
78
|
+
request: Request,
|
|
79
|
+
*,
|
|
80
|
+
status_code: int,
|
|
81
|
+
duration_ms: float,
|
|
82
|
+
event_id: str | None = None,
|
|
83
|
+
timestamp: str | None = None,
|
|
84
|
+
span_id_factory: Callable[[], str] | None = None,
|
|
85
|
+
) -> str:
|
|
86
|
+
"""Capture a FastAPI request as a LogBrew span event and return its event id."""
|
|
87
|
+
|
|
88
|
+
span_event_id = event_id or f"evt_fastapi_span_{uuid.uuid4().hex}"
|
|
89
|
+
span_seed = span_event_id.replace("-", "_")
|
|
90
|
+
traceparent = request.headers.get("traceparent")
|
|
91
|
+
attributes: SpanAttributes = {
|
|
92
|
+
"name": request_name(request),
|
|
93
|
+
"traceId": f"trace_{span_seed}",
|
|
94
|
+
"spanId": f"span_{span_seed}",
|
|
95
|
+
"status": "ok" if status_code < 500 else "error",
|
|
96
|
+
"durationMs": duration_ms,
|
|
97
|
+
"metadata": request_metadata(request, status_code=status_code, duration_ms=duration_ms),
|
|
98
|
+
}
|
|
99
|
+
if traceparent:
|
|
100
|
+
try:
|
|
101
|
+
parse_traceparent(traceparent)
|
|
102
|
+
attributes = span_attributes_from_traceparent(
|
|
103
|
+
traceparent,
|
|
104
|
+
name=request_name(request),
|
|
105
|
+
span_id=(span_id_factory or default_span_id_factory)(),
|
|
106
|
+
status="ok" if status_code < 500 else "error",
|
|
107
|
+
duration_ms=duration_ms,
|
|
108
|
+
metadata=request_metadata(request, status_code=status_code, duration_ms=duration_ms),
|
|
109
|
+
)
|
|
110
|
+
except SdkError:
|
|
111
|
+
pass
|
|
112
|
+
client.span(
|
|
113
|
+
span_event_id,
|
|
114
|
+
timestamp or utc_timestamp(),
|
|
115
|
+
attributes,
|
|
116
|
+
)
|
|
117
|
+
return span_event_id
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def capture_exception(
|
|
121
|
+
client: LogBrewClient,
|
|
122
|
+
request: Request,
|
|
123
|
+
exc: BaseException,
|
|
124
|
+
*,
|
|
125
|
+
event_id: str | None = None,
|
|
126
|
+
timestamp: str | None = None,
|
|
127
|
+
) -> str:
|
|
128
|
+
"""Capture an exception raised while handling a FastAPI request and return its event id."""
|
|
129
|
+
|
|
130
|
+
issue_event_id = event_id or f"evt_fastapi_issue_{uuid.uuid4().hex}"
|
|
131
|
+
client.issue(
|
|
132
|
+
issue_event_id,
|
|
133
|
+
timestamp or utc_timestamp(),
|
|
134
|
+
{
|
|
135
|
+
"title": f"{request_name(request)} failed",
|
|
136
|
+
"level": "error",
|
|
137
|
+
"message": str(exc) or exc.__class__.__name__,
|
|
138
|
+
"metadata": {
|
|
139
|
+
**request_metadata(request, status_code=500),
|
|
140
|
+
"exception_type": exc.__class__.__name__,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
return issue_event_id
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class LogBrewFastAPIMiddleware(BaseHTTPMiddleware):
|
|
148
|
+
"""FastAPI middleware that records request spans and exception issues with LogBrew."""
|
|
149
|
+
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
app: ASGIApp,
|
|
153
|
+
*,
|
|
154
|
+
client: LogBrewClient,
|
|
155
|
+
transport: RecordingTransport | None = None,
|
|
156
|
+
capture_successful_requests: bool = True,
|
|
157
|
+
capture_exceptions: bool = True,
|
|
158
|
+
flush_on_response: bool = True,
|
|
159
|
+
raise_flush_errors: bool = False,
|
|
160
|
+
service_name: str = "fastapi",
|
|
161
|
+
span_id_factory: Callable[[], str] | None = None,
|
|
162
|
+
) -> None:
|
|
163
|
+
super().__init__(app)
|
|
164
|
+
self.config = LogBrewFastAPIConfig(
|
|
165
|
+
client=client,
|
|
166
|
+
transport=transport,
|
|
167
|
+
capture_successful_requests=capture_successful_requests,
|
|
168
|
+
capture_exceptions=capture_exceptions,
|
|
169
|
+
flush_on_response=flush_on_response,
|
|
170
|
+
raise_flush_errors=raise_flush_errors,
|
|
171
|
+
service_name=service_name,
|
|
172
|
+
span_id_factory=span_id_factory,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
176
|
+
start = time.perf_counter()
|
|
177
|
+
try:
|
|
178
|
+
response = await call_next(request)
|
|
179
|
+
except Exception as exc:
|
|
180
|
+
duration_ms = (time.perf_counter() - start) * 1000
|
|
181
|
+
if self.config.capture_exceptions:
|
|
182
|
+
capture_exception(self.config.client, request, exc)
|
|
183
|
+
capture_request_span(
|
|
184
|
+
self.config.client,
|
|
185
|
+
request,
|
|
186
|
+
status_code=500,
|
|
187
|
+
duration_ms=duration_ms,
|
|
188
|
+
span_id_factory=self.config.span_id_factory,
|
|
189
|
+
)
|
|
190
|
+
self._flush_if_configured()
|
|
191
|
+
raise
|
|
192
|
+
|
|
193
|
+
duration_ms = (time.perf_counter() - start) * 1000
|
|
194
|
+
if self.config.capture_successful_requests or response.status_code >= 500:
|
|
195
|
+
capture_request_span(
|
|
196
|
+
self.config.client,
|
|
197
|
+
request,
|
|
198
|
+
status_code=response.status_code,
|
|
199
|
+
duration_ms=duration_ms,
|
|
200
|
+
span_id_factory=self.config.span_id_factory,
|
|
201
|
+
)
|
|
202
|
+
self._flush_if_configured()
|
|
203
|
+
return response
|
|
204
|
+
|
|
205
|
+
def _flush_if_configured(self) -> None:
|
|
206
|
+
if not self.config.flush_on_response or self.config.transport is None:
|
|
207
|
+
return
|
|
208
|
+
try:
|
|
209
|
+
self.config.client.flush(self.config.transport)
|
|
210
|
+
except (SdkError, TransportError):
|
|
211
|
+
if self.config.raise_flush_errors:
|
|
212
|
+
raise
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def add_logbrew_middleware(
|
|
216
|
+
app: FastAPI,
|
|
217
|
+
*,
|
|
218
|
+
client: LogBrewClient,
|
|
219
|
+
transport: RecordingTransport | None = None,
|
|
220
|
+
capture_successful_requests: bool = True,
|
|
221
|
+
capture_exceptions: bool = True,
|
|
222
|
+
flush_on_response: bool = True,
|
|
223
|
+
raise_flush_errors: bool = False,
|
|
224
|
+
service_name: str = "fastapi",
|
|
225
|
+
span_id_factory: Callable[[], str] | None = None,
|
|
226
|
+
) -> None:
|
|
227
|
+
"""Install LogBrew request/exception capture middleware on a FastAPI app."""
|
|
228
|
+
|
|
229
|
+
app.add_middleware(
|
|
230
|
+
LogBrewFastAPIMiddleware,
|
|
231
|
+
client=client,
|
|
232
|
+
transport=transport,
|
|
233
|
+
capture_successful_requests=capture_successful_requests,
|
|
234
|
+
capture_exceptions=capture_exceptions,
|
|
235
|
+
flush_on_response=flush_on_response,
|
|
236
|
+
raise_flush_errors=raise_flush_errors,
|
|
237
|
+
service_name=service_name,
|
|
238
|
+
span_id_factory=span_id_factory,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def default_span_id_factory() -> str:
|
|
243
|
+
"""Return a fresh W3C-compatible child span id."""
|
|
244
|
+
|
|
245
|
+
span_id = uuid.uuid4().hex[:16]
|
|
246
|
+
return "0000000000000001" if span_id == "0000000000000000" else span_id
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
__all__ = [
|
|
250
|
+
"LogBrewFastAPIConfig",
|
|
251
|
+
"LogBrewFastAPIMiddleware",
|
|
252
|
+
"add_logbrew_middleware",
|
|
253
|
+
"capture_exception",
|
|
254
|
+
"capture_request_span",
|
|
255
|
+
"request_metadata",
|
|
256
|
+
"request_name",
|
|
257
|
+
"utc_timestamp",
|
|
258
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Runnable examples shipped with logbrew-fastapi."""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import runpy
|
|
5
|
+
|
|
6
|
+
EXAMPLES = {
|
|
7
|
+
"readme-example": "logbrew_fastapi.examples.readme_example",
|
|
8
|
+
"real-user-smoke": "logbrew_fastapi.examples.real_user_smoke",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> int:
|
|
13
|
+
parser = argparse.ArgumentParser(description="Run packaged logbrew-fastapi examples.")
|
|
14
|
+
parser.add_argument("example", nargs="?", choices=sorted(EXAMPLES), default="real-user-smoke")
|
|
15
|
+
parser.add_argument("--list", action="store_true", help="List packaged examples and commands.")
|
|
16
|
+
args = parser.parse_args()
|
|
17
|
+
|
|
18
|
+
if args.list:
|
|
19
|
+
print("readme-example -> python -m logbrew_fastapi.examples readme-example")
|
|
20
|
+
print("real-user-smoke -> python -m logbrew_fastapi.examples real-user-smoke")
|
|
21
|
+
print("default (real-user-smoke) -> python -m logbrew_fastapi.examples")
|
|
22
|
+
return 0
|
|
23
|
+
|
|
24
|
+
runpy.run_module(EXAMPLES[args.example], run_name="__main__")
|
|
25
|
+
return 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from fastapi.testclient import TestClient
|
|
8
|
+
from logbrew_sdk import LogBrewClient, RecordingTransport
|
|
9
|
+
|
|
10
|
+
from logbrew_fastapi import add_logbrew_middleware
|
|
11
|
+
|
|
12
|
+
client = LogBrewClient.create(
|
|
13
|
+
api_key="LOGBREW_API_KEY",
|
|
14
|
+
sdk_name="logbrew-fastapi",
|
|
15
|
+
sdk_version="0.1.0",
|
|
16
|
+
)
|
|
17
|
+
transport = RecordingTransport.always_accept()
|
|
18
|
+
app = FastAPI()
|
|
19
|
+
add_logbrew_middleware(app, client=client, transport=transport)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.get("/health")
|
|
23
|
+
def health() -> dict[str, bool]:
|
|
24
|
+
return {"ok": True}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
with TestClient(app) as http:
|
|
28
|
+
response = http.get("/health")
|
|
29
|
+
print(json.dumps({"ok": response.status_code == 200, "status": response.status_code}), file=sys.stderr)
|
|
30
|
+
|
|
31
|
+
print(transport.sent_bodies[-1])
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from fastapi.testclient import TestClient
|
|
8
|
+
from logbrew_sdk import LogBrewClient, RecordingTransport
|
|
9
|
+
|
|
10
|
+
from logbrew_fastapi import add_logbrew_middleware
|
|
11
|
+
|
|
12
|
+
client = LogBrewClient.create(
|
|
13
|
+
api_key="LOGBREW_API_KEY",
|
|
14
|
+
sdk_name="logbrew-fastapi",
|
|
15
|
+
sdk_version="0.1.0",
|
|
16
|
+
)
|
|
17
|
+
transport = RecordingTransport.always_accept()
|
|
18
|
+
app = FastAPI()
|
|
19
|
+
add_logbrew_middleware(app, client=client, transport=transport, span_id_factory=lambda: "b7ad6b7169203331")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.get("/health")
|
|
23
|
+
def health() -> dict[str, bool]:
|
|
24
|
+
return {"ok": True}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.get("/boom")
|
|
28
|
+
def boom() -> dict[str, bool]:
|
|
29
|
+
raise RuntimeError("broken handler")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
with TestClient(app, raise_server_exceptions=False) as http:
|
|
33
|
+
health_response = http.get(
|
|
34
|
+
"/health?debug=true",
|
|
35
|
+
headers={"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
|
|
36
|
+
)
|
|
37
|
+
boom_response = http.get("/boom")
|
|
38
|
+
|
|
39
|
+
events = []
|
|
40
|
+
for body in transport.sent_bodies:
|
|
41
|
+
events.extend(json.loads(body)["events"])
|
|
42
|
+
first_span = events[0]["attributes"]
|
|
43
|
+
|
|
44
|
+
print(json.dumps({"sdk": client.sdk, "events": events}, indent=2))
|
|
45
|
+
print(
|
|
46
|
+
json.dumps(
|
|
47
|
+
{
|
|
48
|
+
"ok": health_response.status_code == 200 and boom_response.status_code == 500,
|
|
49
|
+
"requests": 2,
|
|
50
|
+
"sentBodies": len(transport.sent_bodies),
|
|
51
|
+
"events": len(events),
|
|
52
|
+
"pending": client.pending_events(),
|
|
53
|
+
"traceId": first_span["traceId"],
|
|
54
|
+
"parentSpanId": first_span["parentSpanId"],
|
|
55
|
+
"spanId": first_span["spanId"],
|
|
56
|
+
"path": first_span["metadata"]["path"],
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
file=sys.stderr,
|
|
60
|
+
)
|
logbrew_fastapi/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logbrew-fastapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FastAPI integration for capturing LogBrew request spans and exceptions.
|
|
5
|
+
Author: LogBrew
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/LogBrewCo/sdk
|
|
8
|
+
Keywords: logbrew,observability,fastapi,asgi,logs
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: FastAPI
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: fastapi>=0.115
|
|
21
|
+
Requires-Dist: httpx2>=2.3
|
|
22
|
+
Requires-Dist: logbrew-sdk==0.1.0
|
|
23
|
+
|
|
24
|
+
# logbrew-fastapi
|
|
25
|
+
|
|
26
|
+
FastAPI integration for capturing LogBrew request spans and exceptions with the public Python SDK.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python3 -m pip install logbrew-sdk logbrew-fastapi
|
|
32
|
+
python3 -m logbrew_fastapi.examples --help
|
|
33
|
+
python3 -m logbrew_fastapi.examples --list
|
|
34
|
+
python3 -m logbrew_fastapi.examples readme-example
|
|
35
|
+
python3 -m logbrew_fastapi.examples real-user-smoke
|
|
36
|
+
python3 -m logbrew_fastapi.examples
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The package is typed, ships `py.typed`, depends on the core `logbrew-sdk`, and keeps FastAPI as a normal framework dependency instead of bundling or monkeypatching the user's app.
|
|
40
|
+
|
|
41
|
+
## Example
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from fastapi import FastAPI
|
|
45
|
+
from logbrew_fastapi import add_logbrew_middleware
|
|
46
|
+
from logbrew_sdk import LogBrewClient, RecordingTransport
|
|
47
|
+
|
|
48
|
+
client = LogBrewClient.create(
|
|
49
|
+
api_key="LOGBREW_API_KEY",
|
|
50
|
+
sdk_name="logbrew-fastapi",
|
|
51
|
+
sdk_version="0.1.0",
|
|
52
|
+
)
|
|
53
|
+
transport = RecordingTransport.always_accept()
|
|
54
|
+
app = FastAPI()
|
|
55
|
+
add_logbrew_middleware(
|
|
56
|
+
app,
|
|
57
|
+
client=client,
|
|
58
|
+
transport=transport,
|
|
59
|
+
span_id_factory=lambda: "b7ad6b7169203331",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@app.get("/health")
|
|
64
|
+
def health() -> dict[str, bool]:
|
|
65
|
+
return {"ok": True}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`add_logbrew_middleware()` records successful requests as span events, records unhandled handler exceptions as issue plus error-span events, and flushes through the provided transport after each response. If no transport is provided, events stay queued on the core client so the app can flush them itself.
|
|
69
|
+
|
|
70
|
+
When an incoming request has a valid W3C `traceparent` header, request capture continues that trace by using the incoming `traceId` and parent span id while creating a fresh child span id. Missing or malformed `traceparent` headers keep the existing synthetic request span behavior so bad client headers do not break the app. Automatic metadata uses the request path without query text. Use `span_id_factory` only when tests need deterministic child span ids.
|
|
71
|
+
|
|
72
|
+
By default, transport failures do not break the FastAPI response path. Set `raise_flush_errors=True` in test environments when you want misconfigured transport behavior to fail loudly.
|
|
73
|
+
|
|
74
|
+
Use a clearly fake placeholder like `LOGBREW_API_KEY` in local examples and tests.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
logbrew_fastapi/__init__.py,sha256=3tO0gmO2QRxYJkO_9yhYuPlO7EJkveR9G40PnMLZwGU,8336
|
|
2
|
+
logbrew_fastapi/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
|
+
logbrew_fastapi/examples/__init__.py,sha256=OuaLy8tnELi1k4lXd_zUZBrT-H4cFXC8nNdRV2noo5E,54
|
|
4
|
+
logbrew_fastapi/examples/__main__.py,sha256=DgcI6MB9zsYud4FVYgydv91LRx42IiBrsSTKwdIsj9U,982
|
|
5
|
+
logbrew_fastapi/examples/readme_example.py,sha256=3tf1fDoX0DNW8fEDRjsoJ1kEywlBwNVjH8IJ30qVrBs,777
|
|
6
|
+
logbrew_fastapi/examples/real_user_smoke.py,sha256=c1IIZ8oExA4LqLjNdQrBkLelVLN54FDYsV39AZqyLd4,1690
|
|
7
|
+
logbrew_fastapi-0.1.0.dist-info/METADATA,sha256=ko6f3shX-oRerGYxncAwUB-3emUjUFtN9lMKSdcPM6c,3043
|
|
8
|
+
logbrew_fastapi-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
logbrew_fastapi-0.1.0.dist-info/top_level.txt,sha256=3pQ3XojprooNP-iW6HGBO5vnJC4ftBXpOKjAC-ZBTKM,16
|
|
10
|
+
logbrew_fastapi-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
logbrew_fastapi
|