Redact secrets from log message formatter in Python

Build a custom logging.Formatter that masks passwords, API keys, and credit card numbers in log output.

Medium Python 3.9+ Aug 9, 2026 Errors & debugging 14 views 0 copies

Python code

27 lines
Python 3.9+
import re
import logging

class RedactingFormatter(logging.Formatter):
    """Formatter that masks sensitive data in log messages."""
    
    SENSITIVE_PATTERNS = [
        (re.compile(r'password[=:]\s*\S+', re.IGNORECASE), 'password=[REDACTED]'),
        (re.compile(r'api[_-]?key[=:]\s*\S+', re.IGNORECASE), 'api_key=[REDACTED]'),
        (re.compile(r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b'), '[CARD_REDACTED]'),
    ]
    
    def format(self, record):
        message = super().format(record)
        for pattern, replacement in self.SENSITIVE_PATTERNS:
            message = pattern.sub(replacement, message)
        return message

if __name__ == "__main__":
    handler = logging.StreamHandler()
    handler.setFormatter(RedactingFormatter('%(levelname)s: %(message)s'))
    logger = logging.getLogger("app")
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)
    
    logger.info("Connection with password=hunter2 and api_key=abc123 succeeded")
    logger.info("Credit card: 1234-5678-9012-3456")

Output

stdout
INFO: Connection with password=[REDACTED] and api_key=[REDACTED] succeeded
INFO: Credit card: [CARD_REDACTED]

How it works

The RedactingFormatter subclasses logging.Formatter and overrides format(). It first calls the parent format() to get the standard formatted message, then applies each compiled regex pattern to substitute sensitive values with a masked token. Patterns are compiled once at class level to avoid recompilation on every log call. The re.IGNORECASE flag catches variations in casing for password and API key labels. This keeps sensitive data out of logs while preserving the rest of the message structure.

Common mistakes

  • Only masking the message but not the exception traceback or extra fields
  • Using greedier patterns that remove too much text
  • Forgetting to escape regex special characters in sensitive value prefixes

Variations

  1. Use a json.dumps with a custom default function to redact keys recursively in structured logs
  2. Apply the formatter only to specific handlers, not globally, to keep raw logs for debugging

Real-world use cases

  • Preventing credential leaks when logging API request payloads in a web service.
  • Masking card numbers in transaction logs to stay PCI DSS compliant.
  • Sanitizing CI/CD pipeline logs that capture environment variables with secrets.

Sponsored

Run this sample

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

Open editor

More from Errors & debugging

Related tutorials and quizzes for this topic.