Redact sume_live_ API keys from Python logs with a handler filter
A logging.Filter on a logger skips records from child loggers. Put the redaction filter on the handler so a sume_live_ key never reaches the log file.

Sume API keys start with sume_live_. The authentication docs say keys belong on trusted servers and that a key which shows in logs or chat history should be rotated. A redaction filter is a seatbelt, not a cure: it stops the next leak, and it does not make an already logged key safe.
In Python the usual mistake is attaching the filter to a logger. A filter on a logger runs only for records logged directly on that logger, not for records from its child loggers that propagate up. Attach it to the handler and every record that reaches the handler is cleaned.
The filter
It formats the message first, so keys inside %s arguments are caught, then clears args so the message is not formatted twice.
import logging, re
KEY = re.compile(r"sume_live_[\w-]+")
class RedactSumeKeys(logging.Filter):
def filter(self, record):
record.msg = KEY.sub("sume_live_[redacted]", record.getMessage())
record.args = ()
return True
handler = logging.StreamHandler()
handler.addFilter(RedactSumeKeys()) # on the handler, not a logger
logging.basicConfig(level=logging.INFO, handlers=[handler])
http_log = logging.getLogger("myapp.http.sume") # a child logger still passes the handler
http_log.info("POST /v1/images headers=%s", {"x-api-key": "sume_live_abc123-XYZ"})
http_log.info("rotated to sume_live_new_key_789")What the output looks like
Both log lines print sume_live_[redacted], including the one from myapp.http.sume, a child logger the filter was never attached to. If you attach the same filter to the root logger instead, the child record sails through unredacted.
Limits to keep in mind
- The pattern matches the
sume_live_prefix followed by word characters and hyphens. If you log a header dict, make sure the value is a string the regex can see; a key split across fields is not matched. - Other handlers you add later, such as a Sentry or file handler, need the filter too.
- Rotate the key if the raw value was ever written: create the new key, check it with
GET /v1/me, deploy it, then revoke the old one. - A pre-commit secret scan catches keys in source; this filter catches keys at runtime.
Sources
Related posts
More in Developers
- Redeliver a Sume job webhook: POST /v1/jobs/{id}/webhook/redeliver
Redeliver re-sends the real terminal event of a job with a fresh timestamp and signature, even after all 10 automatic attempts are used. Needs jobs:write.
- Redis sorted set scheduler for AI video job polling in Python
Keep video job ids in a Redis ZSET scored by next-poll time, claim due ids with ZREM so no two workers poll one job, reschedule by next_poll_after_seconds.
- Refuse an empty Sume API key or webhook secret: a fail-fast loader
An empty SUME_API_KEY or webhook secret fails late and confusingly. Load both at boot, reject empty or whitespace values, and never print them. Python, tested.
- Video fallback ladder: why reusing an Idempotency-Key returns 409
On Sume's video API a repeated Idempotency-Key with a different body is a 409. When you fall back from one model id to the next, derive a new key per id.
Written by Sume