How to Redact Secrets from Log Messages in Python

Build a lightweight RedactingFormatter class that replaces sensitive tokens like passwords and API keys with [REDACTED] before log messages are printed.

Easy Python 3.9+ Aug 9, 2026 Observability & SRE 12 views 0 copies

Python code

26 lines
Python 3.9+
class RedactingFormatter:
    def __init__(self, secrets):
        self.secrets = secrets

    def redact(self, message):
        for secret in self.secrets:
            message = message.replace(secret, "[REDACTED]")
        return message

    def format(self, record):
        message = record["message"]
        return self.redact(message)


if __name__ == "__main__":
    secrets = ["my_password", "token123"]
    formatter = RedactingFormatter(secrets)

    log_records = [
        {"level": "INFO", "message": "User logged in with my_password"},
        {"level": "DEBUG", "message": "API key token123 used for request"},
        {"level": "ERROR", "message": "Connection failed (token123 vs my_password)"}
    ]

    for record in log_records:
        print(f"{record['level']}: {formatter.format(record)}")

Output

stdout
INFO: User logged in with [REDACTED]
DEBUG: API key [REDACTED] used for request
ERROR: Connection failed ([REDACTED] vs [REDACTED])

How it works

The constructor stores the list of secret strings on the formatter instance. The redact method loops over each secret and calls str.replace to swap every occurrence with [REDACTED] — a simple, dependency-free substitution. The format method extracts the log message from the record dict and passes it through the redaction pipeline, returning a clean string. The if __name__ block simulates a small logging flow, showing how the same formatter can sanitize different severity levels consistently.

Common mistakes

  • Redacting substrings that appear inside legitimate text, like 'token1234', because replace matches partial strings
  • Using mutable state for secrets and changing the list while messages are being processed in a concurrent logger
  • Forgetting to preserve metadata or context (level, timestamp) when returning a raw message string

Variations

  1. Use `re.sub` with word boundaries (`\b`) to avoid partial secret matches
  2. Integrate with Python's `logging.Formatter` by overriding `format` in a subclass that also calls `super().format`

Real-world use cases

  • Stripping database credentials from SQL query logs before they hit central log aggregation.
  • Masking customer API keys in microservice access logs to prevent secrets leaking into SIEM dashboards.
  • Sanitizing CI build output so cloud provider tokens are never printed to the terminal or archived builds.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Observability & SRE

Related tutorials and quizzes for this topic.