generation API log redaction: record outcomes without leaking request payloads
Generation diagnostics need enough information to distinguish submission, observation, refund reconciliation, and delivery. They rarely need a complete HTTP request dump. Start with an allowlist of approved fields and keep sensitive payloads out by construction. Retrofitting a regular expression over arbitrary serialized objects is much harder to reason about.
Build a small log projection
Use an application operation ID, action category, HTTP status when available, remote state, safe error code, and elapsed time. Include a local job ID only in logs whose access policy permits it. The following helper illustrates field selection; it does not promise to sanitize arbitrary input objects recursively.
export function safeGenerationLog(input) {
const code = typeof input.errorCode === 'string' &&
/^[A-Z0-9_]{1,80}$/.test(input.errorCode) ? input.errorCode : null;
return {
event: 'generation_operation',
action: ['submit', 'observe', 'deliver'].includes(input.action)
? input.action : 'unknown',
operationId: String(input.operationId).slice(0, 100),
httpStatus: Number.isInteger(input.httpStatus) ? input.httpStatus : null,
jobState: ['queued', 'running', 'succeeded', 'failed', 'canceled']
.includes(input.jobState) ? input.jobState : null,
errorCode: code,
elapsedMs: Number.isFinite(input.elapsedMs) ? input.elapsedMs : null,
};
}
Use a structured logger that encodes fields safely instead of concatenating user strings into log lines. Treat operation IDs as untrusted data even when they are useful correlation fields. The helper’s length bounds are local policy choices, not limits imposed by Genace.
Exclude more than the bearer key
Prompts can contain customer information, and output or reference URLs can contain signed access parameters. Cookies, proxy credentials, storage tokens, and raw exception objects can also expose secrets. Avoid logging complete request headers, process environments, or response bodies by default.
When support genuinely needs a payload, use a restricted incident record with a retention policy and deliberate redaction. Record that a diagnostic attachment exists rather than copying it into every analytics event. Do not assume a URL is public merely because it appeared in a succeeded job snapshot.
Preserve useful error distinctions
A public HTTP error has an error type and code, while a job snapshot has state and a diagnostic error object. Keep those categories separate. UPSTREAM_SUBMISSION_FAILED can include error.job_id for recovery, while a normal accepted response uses top-level job_id and a read uses id.
Do not turn every failure into a free-text message field that might include an entire upstream body. Prefer reviewed error codes for aggregation. Retain null accounting values when reconciliation is pending instead of logging an invented zero-cost success.
Test redaction with synthetic secrets
Place unique fake tokens in headers, prompts, query strings, and nested exceptions. Run accepted, failed, timeout, and delivery-error paths and assert those tokens are absent from captured logs. Test multiline input and unusually long error text. The test should inspect every logging destination, including traces and CI artifacts.
Finally, verify operators can still find an operation by its safe correlation ID. Redaction is successful when diagnostics remain actionable without distributing credentials or private assets.
Continue with structured log schemas, trace correlation, and secret scanning.
Continue with security
Where these facts come from
- Editorial keyword hypothesis; search volume not measured
- https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
- codebase: src/ai/api/errors.ts
- codebase: src/ai/jobs/http.ts