ASVS 5.0 V16.2.1 requires every log entry to include the metadata needed for a detailed investigation: when it happened, where, who was involved and what occurred. It is Level 2. Use structured logs with a fixed set of fields added automatically, not hand written messages.
Why it matters
During an incident you reconstruct a timeline. A line that says "update failed" is useless. A line with a timestamp, service, host, request id, user id, source IP, action and outcome can be joined with other records. Metadata also makes alerts and queries possible.
How to implement it
Define the fields and attach them through middleware so developers cannot forget them.
- when: timestamp in UTC
- where: service name, version, host or pod, endpoint
- who: authenticated user id or service identity, source IP, session id hash
- what: event name, outcome, resource id, correlation or request id
import logging, json, uuid, contextvars
from datetime import datetime, timezone
ctx = contextvars.ContextVar("ctx", default={})
class JsonFormatter(logging.Formatter):
def format(self, record):
base = {
"ts": datetime.now(timezone.utc).isoformat(),
"service": "billing-api",
"level": record.levelname,
"event": record.getMessage(),
}
base.update(ctx.get()) # request_id, user_id, ip, route
return json.dumps(base, default=str)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logging.getLogger().addHandler(handler)
def begin_request(user_id, ip, route):
ctx.set({"request_id": str(uuid.uuid4()), "user_id": user_id,
"ip": ip, "route": route})
Call begin_request from one middleware layer, and pass the request id on to downstream services in a header so entries across services share it. Log the user id, not the user's personal details.
How to test it
- Trigger a known action, such as changing a password, in a test environment.
- Find the log entries it produced in the central store.
- Check each for all four categories of metadata.
- Follow the request id across services and confirm you can rebuild the sequence.
- Add an automated test that parses sample log output and fails if a required field is missing.
Common mistakes
- Free text messages that must be parsed with regular expressions.
- Missing user context in background jobs and queue consumers.
- Different field names in different services, such as
uid,useranduserId. - Logging a name or email where a stable id would do.
Quick check
Is a request id required by the standard?
The standard asks for enough metadata to rebuild a timeline. A correlation id is the most practical way to meet that.
Which level does this belong to?
Level 2.