Logging records specific events in a software system, such as received requests or raised errors. Logs reveal which endpoints are used and what failures occur, making them a basic signal for observability and API security. section 11.2
Useful security logs preserve evidence of malicious input, unauthorized responses, malformed requests, and unusually slow processing. Request headers, bodies, URLs, status codes, and latency can help reproduce an attack or investigate a vulnerability, but sensitive fields must be removed. section 11.5 section 11.7
A practical logging checklist includes:
- Record requests, errors, relevant incidents, unauthorized responses such as 401 or 403, malformed responses such as 400 or 422, and suspicious processing latency. section 11.5
- Capture enough request context to investigate behavior, including headers, request body, full URL, response information, and a correlation context such as a trace ID. section 11.2 section 11.5
- Exclude sensitive values from recorded payloads; for example, log order details while omitting a credit-card field. section 11.5
- Analyze logs alongside traces and metrics: traces connect processing steps, while metrics expose aggregate usage patterns and outliers. section 11.2 section 11.7
Investigating a suspicious request
An order request containing a possible SQL or script injection produces a log with its endpoint, relevant request details, response status, latency, and trace context. An investigator can replay the captured request locally, use breakpoints to follow its processing, and identify the failure. The recorded payload must omit sensitive fields such as credit-card data. section 11.5
Listing 11.5 Excluding sensitive fields from the logs
# file: ch12/server.py
[...]
@server.post(
"/orders",
response_model=GetOrder,
status_code=status.HTTP_201_CREATED
)
async def place_order(
request: Request,
order_details: PlaceOrder,
user_claims: UserClaims = Depends(authorize_access)
):
with tracer.start_as_current_span("cart.create") as span:
span.set_attribute("user.sub", user_claims.sub)
span.set_attribute(
"request.headers", json.dumps(dict(request.headers))
)
span.set_attribute(
"http.request_body",
order_details.model_dump_json(exclude=["credit_card"])
)
[...]
Shows a request span that records headers and user identity while excluding the credit-card field from the request body. section 11.5







































