Log identifiers instead of payloads, redact sensitive fields inside the logger itself, keep test and staging logs on short retention, and add a check that fails a build when output contains patterns that look like personal data. Fixing the source of the log line beats scrubbing files afterwards.
Why logs are the quiet copy
Even teams with clean databases leak through logs. Debug logging prints request bodies. Test runners print failing assertions with full objects. Error trackers capture local variables. Each of those can hold names, emails and tokens, and logs usually travel to wider audiences and sit in storage nobody thinks of as a database.
Under the GDPR, personal data must be limited to what is necessary and kept only as long as needed. Logs are in scope when they contain personal data. This is general information, not legal advice.
Where output ends up
| Source | Hidden data | Control |
|---|---|---|
| Application logs | Request and response bodies | Structured logging with field allowlists |
| Test runner output | Full objects in failed assertions | Print ids and keys only |
| CI job logs | Everything the above print | Retention limit, restricted viewers |
| Error tracking | Local variables, user context | Scrub rules before sending |
| Traces and metrics labels | Emails as label values | Ban personal values in labels |
| Debug dumps | Memory and heap files | Do not store, or restrict tightly |
Steps
- Log events, not objects. Write "order 4821 created for account 77" rather than dumping the order. Developers can look up the rest in a controlled tool if they must.
- Use an allowlist. Configure the logger to emit only named fields. Unknown fields are dropped, so a new sensitive field cannot slip through by accident.
- Redact in the logger. Add a filter that masks known sensitive keys, such as email, phone, token and authorization, before the line is written. Test the filter with unit tests.
- Tame test output. Use assertion helpers that print only the fields that differ. Avoid printing whole fixtures.
- Limit verbosity in staging. Debug level logging should be switched on for a bounded time on a specific service, not left on for the environment.
- Short retention. Keep staging and CI logs for days or weeks, not months. Export only what an investigation needs.
- Scan test output in CI. Add a step that searches the job log and artifacts for patterns such as email addresses on non reserved domains and long digit sequences. Fail the build on a hit.
- Control viewers. Restrict who can read CI logs for sensitive projects, and avoid publishing logs on public links.
About masking features in CI platforms
Many CI systems mask known secret values in logs, but masking usually matches the exact string. A value that is slightly transformed, split across lines or encoded may not be hidden. Treat platform masking as a safety net, not a design.
Sample redaction approach
Keep a short list of sensitive keys, apply it recursively to structured log objects, and replace values with a fixed marker. Review the list whenever the schema gains a new field.
Mistakes to avoid
- Logging full request bodies "just in staging" and forgetting to turn it off.
- Shipping staging logs into the production logging system with long retention.
- Trusting redaction rules that nobody tests.
- Putting email addresses in metric labels, where they multiply storage and exposure.
Quick check
Is it enough to mask logs when viewing?
No. Mask at write time. Masking only in the viewer leaves the raw data in storage and in exports.
How long should test logs be kept?
Only as long as they help you debug and meet your evidence needs. For most teams that is days to a few weeks. Choose a number and automate it.
Sources
Last reviewed: 19 October 2026.