Skip to main content

Zero-config PII redaction for Python logging

Project description

HushLog

Zero-config PII redaction for Python logging.

PyPI version Python versions CI License: MIT

Features

  • Zero-config -- one call to hushlog.patch() and you're done
  • Non-invasive -- wraps existing formatters, no logger rewrites needed
  • Performant -- pre-compiled regex with heuristic early-exit checks
  • Type-safe -- fully typed with PEP 561 py.typed marker
  • Python 3.10+ -- supports Python 3.10 through 3.13

Installation

pip install hushlog

Or with uv:

uv add hushlog

Quick Start

import logging
import hushlog

# Configure logging FIRST, then patch
logging.basicConfig(level=logging.INFO)
hushlog.patch()

logger = logging.getLogger(__name__)

logger.info("User email: john@example.com")
# Output: User email: [EMAIL REDACTED]

logger.info("Card: 4111-1111-1111-1111")
# Output: Card: [CREDIT_CARD REDACTED]

logger.info("SSN: 123-45-6789")
# Output: SSN: [SSN REDACTED]

How It Works

HushLog wraps your existing logging formatters with a RedactingFormatter that scans the final formatted string for PII patterns. It never replaces loggers or handlers -- your existing logger.info() calls remain unchanged. All regex patterns are pre-compiled at import time with lightweight heuristic pre-checks to minimize overhead on the hot logging path.

What Gets Redacted

Pattern Example Output Notes
Email john@example.com [EMAIL REDACTED] RFC 5322 subset, @ heuristic pre-check
Credit Card 4111-1111-1111-1111 [CREDIT_CARD REDACTED] Luhn validated, supports spaces/dashes
SSN 123-45-6789 [SSN REDACTED] Dashed format only, invalid ranges excluded
Phone (555) 123-4567 [PHONE REDACTED] US NANP, multiple formats
JWT eyJhbGci... [JWT REDACTED] 3-5 segment base64url tokens
AWS Access Key AKIAIOSFODNN7EXAMPLE [AWS_ACCESS_KEY REDACTED] AKIA/ASIA prefixed
AWS Secret Key aws_secret_access_key=... [AWS_SECRET_KEY REDACTED] Context-dependent (requires label)
Stripe Key sk_live_abc123... [STRIPE_KEY REDACTED] sk/pk/rk live/test keys
GitHub Token ghp_xxxx... [GITHUB_TOKEN REDACTED] Classic + fine-grained (github_pat_)
GCP API Key AIzaSyA... [GCP_KEY REDACTED] AIza-prefixed keys
Generic Secret password=MyS3cret [SECRET REDACTED] Label-based (password, secret, api_key, etc.)
IPv4 192.168.1.1 [IPV4 REDACTED] Octet-validated, rejects version strings
IPv6 2001:db8::8a2e:370:7334 [IPV6 REDACTED] Full, compressed, and mixed forms

Configuration

Disable specific built-in patterns or add custom ones:

from hushlog import Config

hushlog.patch(Config(
    disable_patterns=frozenset({"phone"}),
    custom_patterns={"internal_id": r"ID-[A-Z]{3}-[0-9]{6}"},
))

Partial Masking

Show partial values instead of full redaction:

hushlog.patch(Config(mask_style="partial"))
# john@example.com → j***@e***.com
# 4111111111111111 → ****-****-****-1111
# 078-05-1120      → ***-**-1120
# (555) 234-5678   → (***) ***-5678

Use a custom mask character:

hushlog.patch(Config(mask_style="partial", mask_character="#"))
# john@example.com → j###@e###.com

Note: Partial masking reveals partial information (first/last characters). In small organizations, this may be identifying. Use mask_style="full" (default) for maximum privacy.

JSON / Structured Logging

HushLog supports JSON log output with automatic PII redaction in all string values, including nested structures.

RedactingJsonFormatter

Use RedactingJsonFormatter as a drop-in JSON formatter for any handler:

import logging
from hushlog import Config, RedactingJsonFormatter
from hushlog._registry import PatternRegistry  # internal API

registry = PatternRegistry.from_config(Config())
formatter = RedactingJsonFormatter(registry)

handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.getLogger().addHandler(handler)

logger = logging.getLogger(__name__)
logger.info("Contact user@example.com", extra={"ssn": "078-05-1120"})
# Output: {"message": "Contact [EMAIL REDACTED]", "ssn": "[SSN REDACTED]", ...}

Works with or without python-json-logger installed. Install the optional dependency for enhanced JSON serialization:

pip install hushlog[json]

redact_dict()

For manual redaction of dict/list/string structures:

import hushlog

data = {"user": {"email": "alice@corp.io", "name": "Alice", "age": 30}}
clean = hushlog.redact_dict(data)
# {"user": {"email": "[EMAIL REDACTED]", "name": "Alice", "age": 30}}

Note: redact_dict() creates a new PatternRegistry on every call. For repeated use, create a registry once via PatternRegistry.from_config() and call registry.redact_dict() directly.

Teardown

Call unpatch() to remove HushLog's formatter wrappers and restore the original formatters. This is useful for testing or runtime toggling:

hushlog.unpatch()

Calling unpatch() without a prior patch() is safe (no-op). Calling patch() multiple times is also safe (idempotent).

Limitations

  • Only handlers present on the root logger at patch() time are wrapped. Handlers added later will not be redacted.
  • Named loggers with propagate=False and their own handlers bypass root-level redaction.
  • structlog/loguru integrations planned for v0.3.0.
  • Phone detection is US NANP only.

Planned

Structlog/loguru integrations, pattern validation, and more. See the roadmap for details.

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

License

MIT -- see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hushlog-0.3.0a1.tar.gz (76.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hushlog-0.3.0a1-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file hushlog-0.3.0a1.tar.gz.

File metadata

  • Download URL: hushlog-0.3.0a1.tar.gz
  • Upload date:
  • Size: 76.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for hushlog-0.3.0a1.tar.gz
Algorithm Hash digest
SHA256 23852ebf52a7558c0fc7598958795a9433a9ba93ff9c3e8379b05533723e4dab
MD5 c2259478fc527bb9da7c716d66f0969c
BLAKE2b-256 23f72fe450139f1ad4aacb8e47add1559d867b3dac5336f28207cff583116c22

See more details on using hashes here.

Provenance

The following attestation bundles were made for hushlog-0.3.0a1.tar.gz:

Publisher: release.yml on FelipeMorandini/hushlog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hushlog-0.3.0a1-py3-none-any.whl.

File metadata

  • Download URL: hushlog-0.3.0a1-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for hushlog-0.3.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 de3deaea3e0f74e0113203f36761b03cbccda6bb0970e151cdc06ffa3566d2cb
MD5 4c35ed58ab69edbd47b80ff864f50417
BLAKE2b-256 ea4f7f13de44d166a1d4f2f03f6f44dbbd7a629e6f4513153371e0cf75756d21

See more details on using hashes here.

Provenance

The following attestation bundles were made for hushlog-0.3.0a1-py3-none-any.whl:

Publisher: release.yml on FelipeMorandini/hushlog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page