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.
Python code
27 linesimport 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
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
- Use a json.dumps with a custom default function to redact keys recursively in structured logs
- 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
More from Errors & debugging
- Catch RecursionError and Fail Gracefully in Python easy
- Catch ValueError and print friendly message in Python easy
- Collect Multiple Validation Errors in Python Before Raising medium
- Handle ValueError and ZeroDivisionError in Python with try except easy
- How to Add a Correlation ID to Logging Records in Python medium
- How to Assert Preconditions with Descriptive Messages in Python easy
Keep learning
Related tutorials and quizzes for this topic.