Yeda AI Tips · #160

English

Oculta secretos en la configuración del logger, no en cada punto de llamada

Una sola línea — log.info(request) — y cada API key, token de sesión y contraseña de ese objeto de request acaba de quedar en texto plano en tus logs, en tu agregador de logs y a la vista de todos los que tienen acceso de lectura. La solución no es más disciplina. Es mover la redacción al único lugar por el que pasa toda línea de log: la configuración del logger.

Por qué falla la redacción en el punto de llamada

El patrón común es borrar los secretos donde se registran:

log.info("auth attempt", user=user.id, token="***REDACTED***")

Esto funciona hasta el día en que alguien registra el objeto completo en lugar de campos elegidos a mano, agrega un nuevo campo sensible al payload, o copia un log.info(request) de depuración desde una rama y olvida borrarlo. La redacción en el punto de llamada es una regla que cada ingeniero debe recordar en cada línea, para siempre. El modo de falla es un campo olvidado, y siempre hay alguien que olvida.

Redacta una vez, en el logger

Un processor (structlog) o un filter (la logging estándar) se ubica en la tubería por la que pasa todo evento. Inspecciona el evento, reemplaza las claves sensibles con un marcador y solo entonces entrega el registro al formatter y a los handlers. Nada llega al disco ni a tu agregador sin borrar.

import structlog

SENSITIVE = {"password", "token", "authorization", "api_key", "secret", "cookie"}

def redact(logger, method_name, event_dict):
    for key in list(event_dict):
        if key.lower() in SENSITIVE:
            event_dict[key] = "***REDACTED***"
    return event_dict

structlog.configure(processors=[redact, structlog.processors.JSONRenderer()])

Un processor de structlog es simplemente un callable que recibe (logger, method_name, event_dict) y devuelve el dict para el siguiente processor de la cadena. Coloca redact antes del renderer y cada evento queda borrado antes de serializarse.

La biblioteca estándar tiene la misma junta. Una subclase de logging.Filter muta el LogRecord antes de que cualquier handler lo emita:

import logging

class RedactFilter(logging.Filter):
    def filter(self, record):
        for key in SENSITIVE:
            if hasattr(record, key):
                setattr(record, key, "***REDACTED***")
        record.msg = scrub(record.msg)  # regex sobre el string del mensaje
        return True  # conserva el registro; solo lo mutamos

Adjunta el filter a tus handlers y se ejecuta en cada registro.

Reglas prácticas

DecisiónHaz esto
Dónde redactarEn la config del logger (processor/filter), no en el punto de llamada
Qué buscarUna denylist de nombres de clave más regex para valores (bearer tokens, keys)
Coincidencia de clavesSin distinguir mayúsculas; normaliza Authorization y authorization
Si no hay coincidenciaDevuelve/conserva el registro — un filter que devuelve True muta, no descarta
Payloads anidadosRecorre dicts de forma recursiva; los secretos se esconden en headers, body, meta

Movidas avanzadas

Recursos

¿Construyes funciones de IA que registran mucho? Yeda AI diseña, audita y despliega sistemas LLM en producción — incluida la higiene de logging y secretos a su alrededor. Habla con nosotros · Lee el blog