Start with questions the log must answer
Examples: Which application event created the message? Which provider attempt received this SMTP response? Which checker/provider timed out? Which operator changed a policy? Design fields for those questions rather than serializing every request and model.
Use generated correlation IDs across website/app/API, queue job, provider event, and safe audit record. Where grouping by email/domain is necessary, use a keyed digest with rotation/segmentation appropriate to the use; avoid an ordinary unsalted hash that can be guessed from common addresses.
Allowlist fields
A delivery event can include UTC timestamp, environment, service/version, correlation ID, tenant ID surrogate, message type, provider, attempt, stage, normalized status class, bounded SMTP code, latency, queue age, and redacted error category. An authentication event can include domains/selectors needed for diagnosis when approved, without local parts or bodies.
Exclude passwords, API/session tokens, reset/verification capabilities, auth headers, complete URLs with secrets, mailbox credentials, encryption keys, raw message bodies/headers, unnecessary addresses, and arbitrary upstream payloads. Sanitize CR/LF and delimiters to prevent log injection.
Separate logs from sensitive evidence
If a raw message is temporarily required for an authorized incident, keep it in restricted encrypted evidence storage with explicit access audit and expiry—not in the general log stream. Give operators redacted views by default and require step-up approval for exceptional access.
Protect the pipeline
Encrypt transport and storage, apply least privilege, separate tenants/environments, monitor read/export/delete/admin actions, and protect integrity. Bound event size and rate so an attacker or provider error cannot fill disk or exhaust ingestion. Define behavior when logging is unavailable; do not block critical mail blindly or silently drop security audit events.
Test redaction and disposal
Send fixtures containing addresses, authorization headers, signed URLs, multiline provider errors, Unicode, and huge payloads. Assert that forbidden values never reach local logs, APM, queue-failure storage, error pages, analytics, or support exports. Test retention and legal hold separately.
A privacy-safe log lets an operator reconstruct state transitions without reconstructing the customer’s message. Review its schema whenever a provider integration adds new fields.