← Back to Blog
Code Snippets June 30, 2026

Find & Redact PII in Logs / Text (Python, zero deps)

🔍 Code Snippet — June 30, 2026

Find & Redact PII in Logs / Text (Python, zero deps)

Sanitizing logs, cleaning data exports, GDPR/HIPAA compliance — manual search-and-replace misses edge cases. This catches emails, phones, SSNs, credit cards, and IPs in a single pass:

\\\python
import re

PII_PATTERNS = [
{"type": "ssn", "pattern": r'\b\d{3}-\d{2}-\d{4}\b', "repl": "XXX-XX-XXXX"},
{"type": "email", "pattern": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b', "repl": "[email redacted]"},
{"type": "phone_us", "pattern": r'(\+1\s?)?(?:\(?\d{3}\)?[-.\s]?)\d{3}[-.\s]?\d{4}\b', "repl": "[phone redacted]"},
{"type": "ipv4", "pattern": r'\b(?:\d{1,3}\.){3}\d{1,3}\b', "repl": "[IP redacted]"},
{"type": "credit_card","pattern": r'\b(?:4\d{3}|\d{4})[-\s]?\d{4}[-\s]?\d{4}[-\s]?(?:\d{4}|\d{3,4})\b', "repl": "XXXX-XXXX-XXXX-XXXX"},
]

def redact_pii(text):
combined = '|'.join(f'(?P<{e["type"]}>{e["pattern"]})' for e in PII_PATTERNS)
repl_map = {e["type"]: e["repl"] for e in PII_PATTERNS}
def _r(m): return repl_map.get(m.lastgroup, m.group(0))
return re.sub(combined, _r, text)

Dry-run: see what matches before redacting


def find_pii_matches(text):
results = []
for e in PII_PATTERNS:
for m in re.finditer(e["pattern"], text):
results.append({"type": e["type"], "match": m.group(0)})
return results
\
\\

Usage: \clean = redact_pii(raw_log_line)\
💡 Pro tip: Add a \--dry-run\ flag for compliance audits. Extend the pattern registry with custom regexes (employee IDs, API keys) by appending to \PII_PATTERNS\. Bookmark this — every team that handles user data needs one.