API Access Logs#
An access log is a structured record of REST and gRPC service access: who called which operation, the parameters that identify the request, and the status and outcome of the response. Records cover both handled business calls and requests the service rejects before a handler runs — authentication failures, unknown routes and methods, and malformed requests — so administrators can track and debug invalid access as well as successful calls. Access logs are meant for audit trails and request-level troubleshooting, and are separate from the operational logs and metrics described in Observability. They are disabled by default; when disabled, no access records are produced and the service behaves exactly as described elsewhere in this guide.
Access records are emitted on the standard output alongside the operational logs, as a dedicated record type a log pipeline can filter on, so no additional collector or endpoint is required. When structured logging is enabled (see Logging), each record is emitted as JSON.
What Is Recorded#
When enabled, the service logs all service access except infrastructure and discovery endpoints. Specifically:
A record is produced for every business call the service accepts and processes, whether it succeeds or fails. This is a combined record carrying both
requestandresponse, because each published operation is a single request/response exchange.A record is also produced for a request the service rejects before a handler runs, so failed and invalid access is auditable — authentication failures (
401/UNAUTHENTICATED), authorization failures (403/PERMISSION_DENIED), unknown routes and methods (404/405/UNIMPLEMENTED), malformed or unparseable requests (400/INVALID_ARGUMENT), rate limiting (429), and server errors (500/INTERNAL). A request that never resolves to a business operation produces a call-level record: it carriesoperation,transport, header fields, andresponse, but norequest, because no input was parsed. For an unknown REST route or gRPC method theoperationis the raw{METHOD} {path}(REST) or requested method path (gRPC) actually attempted.Records are not produced for infrastructure or discovery endpoints, even when they fail — the health and readiness probes, and the gRPC health, reflection, and capability-discovery services.
Each record contains the following fields:
Field |
Description |
|---|---|
|
Which API surface served the call: |
|
For REST, the method and route template (for example |
|
The identifying inputs of the call: path parameters (such as |
|
The call’s terminal status: for REST the HTTP status code, for gRPC the numeric status code ( |
|
Present only when there is something to add: |
header fields |
Extra top-level fields copied from request headers per the configured header map (see Configuration), for example |
A REST record for a successful user lookup looks like this:
{
"type": "access",
"access": {
"transport": "REST",
"ip": "203.0.113.10",
"ua": "Mozilla/5.0",
"operation": "GET /api/v1alpha/users/{userId}",
"request": { "params": { "user_id": "abc-123" } },
"response": { "status": 200 }
}
}
A call-level record for an unauthenticated request omits request entirely:
{
"type": "access",
"access": {
"transport": "REST",
"operation": "GET /api/v1alpha/users",
"response": { "status": 401, "detail": { "message": "missing or invalid Authorization header" } }
}
}
When structured logging is disabled (the default), the same record is rendered in the
human-readable text form used by the operational logs, with the access fields flattened into one
indented key.path: value line per value:
2026-09-09T03:18:00.123456Z INFO access:
transport: REST
ip: 203.0.113.10
ua: Mozilla/5.0
operation: GET /api/v1alpha/users/{userId}
request.params.user_id: abc-123
response.status: 200
The request.params are recorded on a best-effort basis: they reflect only the inputs determined
before a failure occurred, so a call that fails early may record fewer parameters than a successful
one, and a call rejected before a handler records none at all.
What Is Never Logged#
Access records are deliberately minimal. They never contain:
Credentials — the
Authorizationheader, bearer tokens, or cookies are never copied, regardless of the header map.Response bodies — user, group, and membership payloads are never logged; only a count of the items a list returned appears in
response.detail.
Logged Operations#
The table below summarizes what each published operation records. REST and gRPC behave identically; the gRPC method that corresponds to each REST endpoint is listed alongside it.
Operation |
|
Success |
|---|---|---|
|
(none) |
(none) |
|
supplied |
|
|
|
(none) |
|
|
|
|
|
(none) |
|
supplied |
|
|
|
(none) |
|
|
|
|
|
(none) |
On any error, response.status carries the status code and response.detail.message describes the
failure; the logged message may be more specific than the generic message returned to the caller.
For gRPC, response.detail.code carries the status name.
Requests that do not match any operation above — unknown paths, unsupported methods, and
unauthenticated or malformed calls — are still logged as call-level records using the raw
requested operation and the resulting error response, with no request.params. Infrastructure
and discovery endpoints (the health and readiness probes and the gRPC health, reflection, and
capability-discovery services) are never logged, even on failure.
Configuration#
Access logging exposes two settings: a master switch, disabled by default, and a header map that selects which request headers are copied onto each record as top-level fields.
Setting |
Helm value ( |
Default |
Description |
|---|---|---|---|
Enable access logs |
|
|
When |
Header map |
|
|
Map of access-record field name to request header name. Each present header is copied as a top-level field. |
Both settings render as environment variables in the reader pod specification, so changing either and upgrading the release rolls the reader pods automatically. Each value takes effect when the pod starts.
Configuring Access Logs in the Helm Chart#
Add the settings under observability.logging.access in userinfo-values.yaml. The example below
enables access logging and copies the caller IP and user agent onto each record:
observability:
logging:
access:
enabled: true
headers:
ip: "x-forwarded-for"
ua: "user-agent"
Install or upgrade the release with the same commands used for the synchronized directory deployment in User Info Service Configuration.
See Also#
Observability — where access logs sit alongside the operational logs, traces, and metrics, and the request-header forwarding that shares the
observabilityconfiguration section.