Перейти к основному содержимому

Маскирование

Логгер автоматически маскирует значения атрибутов, чьи ключи совпадают с одним из шести суффиксных шаблонов. Маска применяется на этапе emit, до того как запись достигнет любого приёмника, поэтому исходное секретное значение никогда не пересекает границу API логгера в помеченном атрибуте.

Список суффиксов по умолчанию

По умолчанию логгер маскирует атрибуты, чей ключ (case-insensitive) заканчивается на:

_key
_secret
_token
_password
_passphrase
_credentials

Совпавшему ключу значение заменяется на литеральную строку "***". Список суффиксов живёт в config-spec/_meta/secret_patterns.yaml и общий для dagstack/config и dagstack/logger — добавление нового шаблона в одном месте обновляет обе библиотеки на следующем прогоне эмиттера.

Поведение

from dagstack.logger import Logger

logger = Logger.get("auth")

logger.info("user authenticated", attributes={
"user.id": 42,
"api_key": "sk-very-secret-value", # → "***"
"session_token": "ey...", # → "***"
"request.id": "req-abc",
})
# Отправленная запись:
# attributes = {
# "user.id": 42,
# "api_key": "***",
# "session_token": "***",
# "request.id": "req-abc",
# }

Вложенные атрибуты

Маскирование применяется рекурсивно через вложенные maps. Секрет, спрятанный внутри структурированного атрибута, маскируется на любой глубине:

logger.info("config snapshot", attributes={
"config": {
"service.name": "order-service",
"auth": {
"client_secret": "shh", # → "***"
"redirect_url": "https://...",
},
},
})
# Результат:
# attributes = {
# "config": {
# "service.name": "order-service",
# "auth": {
# "client_secret": "***",
# "redirect_url": "https://...",
# },
# },
# }

Что не маскируется

  • LogRecord.body — основное сообщение. Маскирование произвольной подстроки внутри body потребовало бы дорогого pattern matching по plaintext с большим риском ложных срабатываний. Разработчики обязаны форматировать body без секретов; если значение нужно включить — клади его в помеченный атрибут, а не в body.
  • Ключи атрибутов — маскируются только значения, ключи никогда. Ключ api_key остаётся api_key; значение становится "***".
  • OTel-типизированные поляtrace_id, span_id, instrumentation_scope, resource не подпадают под суффиксное совпадение; их смысл зафиксирован спекой.

Пользовательские шаблоны

Phase 1 поставляет только дефолтный список суффиксов. Phase 2 вводит RedactionProcessor в цепочке LogProcessor (спека §10.3), позволяя приложениям добавлять regex-шаблоны:

# Phase 2 — пока не активно в v0.1.x
processors:
- type: redaction
extra_patterns:
- pattern: "user\\.email"
replacement: "<redacted-email>"
- pattern: ".*_pii"
replacement: "***"

Пока процессоры не приземлились, обходной путь — почистить атрибут до того, как передавать его в logger.info(...), например хешируя или урезая поле в коде приложения.

Почему суффиксное совпадение, а не сканирование значений

Сканирование каждого значения по паттернам «выглядит как секрет» (высокоэнтропийные строки, regex для JWT) — дорого и легко ошибается. Совпадение по суффиксу ключа — дёшево, детерминированно и легко аудируется: ревьюер, читающий код, сразу видит, будет ли поле замаскировано.

Дисциплина суффиксов — то, что обеспечивает совместимость маскирования секретов config-spec и маскирования логгера. Один и тот же список шаблонов _key, _secret, _token, _password, _passphrase, _credentials уважают обе библиотеки.

См. также