Back to Blog

Redact Secrets Without Destroying Debug Value

There is a reason teams keep sharing raw logs: sanitized logs are often useless.

This happens when redaction takes a "delete the line" approach, or when scripts corrupt structure in the process. If sanitization removes the clues needed to debug, people will bypass it. The goal is not maximum deletion. It is maximum safety with preserved signal.

Why over-redaction happens

Over-redaction is usually not a technical failure. It is a psychological one.

When someone builds a sanitization script under pressure — after an incident, before a deadline — the instinct is to err on the side of deletion. Removing too much feels safe. Removing too little feels risky. So the script removes entire lines, entire fields, entire sections.

The problem surfaces later, when someone tries to debug using the sanitized output and realizes there is nothing actionable left. At that point, the path of least resistance is to skip sanitization entirely next time. The tool that was supposed to make sharing safer ends up making it less safe.

The fix is not looser rules. It is a clearer mental model of what redaction is supposed to do: remove the sensitive payload, preserve everything else.

What makes a sanitized log still useful

Useful logs have three properties: structure (the log stays parseable, especially JSON), labels (you can see what kind of value was present — token vs password vs URL), and context (surrounding text stays intact so the error story still makes sense).

This is why redacting values is almost always better than dropping fields.

Common mistakes that destroy debug value

Dropping entire sections. Removing every line that contains "Authorization" also removes the most important part of an auth failure. The header name tells you which credential was involved. The surrounding lines tell you what was being requested. Only the token value itself is sensitive.

Better to keep the header name and redact the token value:

Authorization: Bearer <REDACTED_TOKEN>

Corrupting JSON. If your sanitizer breaks quoting or escapes, your parsing pipeline breaks with it. A log line that was valid JSON becomes an unparseable string, and you lose the ability to query or filter it downstream. Replace only the value, keep valid JSON:

{"password":"<REDACTED_PASSWORD>"}

Redacting keys instead of values. Removing the key name removes meaning. The key tells you what kind of secret was present. Without it, you cannot even tell what the redaction represents.

This:

{"x-api-key":"<REDACTED_API_KEY>"}

is useful. You know an API key was present, you know which header it was in, and you know it was redacted. This:

{"<REDACTED>":"<REDACTED>"}

tells you nothing. You cannot debug from it, and you cannot audit it.

Redacting entire stack traces. Stack traces rarely contain secrets directly. What they do contain — class names, method names, file paths, line numbers — is almost never sensitive and is often the most valuable part of a bug report. A sanitizer that removes entire stack traces because one line included a connection string has destroyed the signal to protect the noise.

Preserve the shape, not the payload

When people debug, they look for shapes: which header was present, which environment variable was set, which URL was used, which service name appeared in the stack trace. Redaction should preserve those shapes while removing the sensitive payload.

The redaction label matters too. <REDACTED_BEARER_TOKEN> is more useful than <REDACTED>. It tells the reader what was there without revealing the value. That information is not sensitive — knowing a bearer token existed in a request is not a security risk. Knowing the actual token is.

Validate before you send

Before sharing sanitized logs, ask three questions: Can you still identify the failing request? Can you still see which subsystem failed — db, cache, auth? Do you still have timestamps and correlation IDs?

If the answer is no, the problem is usually scope, not sanitization. Share a smaller slice. Most debugging sessions only need the 10–15 minutes around the failure window, not hours of output.

Preview makes this easy to verify before committing:

cat logs.txt | logshield scan --dry-run

If the preview shows critical context will be lost, adjust what you are sharing. Do not abandon sanitization.

The test that matters

Here is a practical check. After sanitizing, give the output to someone who was not involved in the incident and ask: "Can you tell what went wrong?"

If they cannot, the sanitized output is not useful for debugging and will not be useful for a support ticket or a post-mortem either. That is not a reason to send raw logs. It is a reason to rethink what you are sharing and how.

Sanitization done well should be invisible to the reader. They should get useful debugging context. They just should not get your credentials.

---

Next: CI log retention. If support tickets are the human leak path, CI logs are the automated one.