Masking email addresses in logs with Pgcli

The error log was bleeding email addresses across the console. Every query, every audit trail—plain text, exposed. You knew it the second you scrolled. This is the kind of leak that turns a debug session into a security incident.

Masking email addresses in logs with Pgcli is not optional. It is the line between safe logging and an exploitable data dump. Pgcli, the Postgres command-line tool with smart autocompletion and syntax highlighting, will happily echo results from your queries, including sensitive fields like user.email. To protect privacy and comply with policies like GDPR or SOC 2, those values must be masked before they leave the database or land in a log file.

Start by rewriting your queries to replace email output with a masked representation. A common pattern is:

SELECT regexp_replace(email, '([^@]{3})[^@]*(?=@)', '\1***') AS email_masked
FROM users;

This keeps part of the local name and scrubs the rest. Pgcli will display the obfuscated value instantly on execution. No raw address ever surfaces in your logs.

For application-level logging, intercept query responses before printing. In Python with psycopg2, hook your result formatting step:

import re

def mask_email(addr):
    return re.sub(r'([^@]{3})[^@]*(?=@)', r'\1***', addr)

for row in cursor.fetchall():
    safe_row = tuple(mask_email(col) if '@' in str(col) else col for col in row)
    print(safe_row)

This closes the loop—whether Pgcli logs output directly or your app captures it, the sensitive data is stripped at the source.

Test every masking regex against edge cases: subdomains, plus-addressing, mixed case. Commit the masking function into utility modules so it’s never skipped during refactors. Monitor logs in staging to confirm no unmasked data slips through.

When Pgcli is configured in environments that share logs with central collectors, masking becomes critical. It prevents downstream systems from storing or leaking personally identifiable information.

Never let Pgcli reveal production email addresses again. Automate the masking. Keep it simple. Keep it fast. Keep it safe.

See this live in minutes with hoop.dev—connect your Postgres, run queries safely, and watch every email in the console masked by default.