Настройка логирования бэкенда мобильного приложения (ELK Stack)
Лог в файл — это не логирование. Когда бэкенд мобильного приложения запущен на нескольких серверах и генерирует десятки тысяч строк в минуту, поиск причины ошибки через ssh + grep занимает часы. ELK Stack (Elasticsearch + Logstash + Kibana) или его более лёгкий вариант EFK (с Fluent Bit вместо Logstash) превращает этот процесс в запрос с фильтрами за секунды.
Что именно настраиваем
Стек сбора логов состоит из трёх частей: структурированное логирование на уровне приложения, агент для сбора и отправки, хранилище с поисковым движком и UI.
Структурированные логи
Приложение должно писать логи в JSON — не в plain text. Строка вида [ERROR] 2024-01-15 14:23:11 UserService: null pointer — мусор для Elasticsearch. JSON-лог — данные:
# Python, structlog
import structlog
logger = structlog.get_logger()
def authenticate_user(user_id: str, device_id: str):
logger.info(
"auth_attempt",
user_id=user_id,
device_id=device_id,
platform="ios",
app_version="3.2.1",
)
try:
token = auth_service.verify(user_id)
logger.info("auth_success", user_id=user_id, token_expires_in=3600)
return token
except InvalidTokenError as e:
logger.warning("auth_failed", user_id=user_id, reason=str(e))
raise
// Go, zerolog
log.Error().
Str("user_id", userID).
Str("device_id", deviceID).
Str("endpoint", "/api/v1/auth").
Int("status_code", 401).
Dur("duration_ms", elapsed).
Msg("authentication failed")
Обязательные поля в каждом логе: timestamp (ISO 8601), level, service, request_id (для трассировки запроса через несколько сервисов), user_id (если применимо). request_id — UUID, который генерируется на входящем запросе в middleware и пробрасывается во все дочерние вызовы через context.
Fluent Bit для сбора логов
Fluent Bit предпочтительнее Logstash для большинства setup'ов: потребляет ~1MB RAM против 500MB у Logstash, конфигурируется в INI/YAML, работает как DaemonSet в Kubernetes.
# fluent-bit.conf
[INPUT]
Name tail
Path /var/log/app/*.log
Parser json
Tag app.*
Refresh_Interval 5
[FILTER]
Name grep
Match app.*
Regex level (warn|error|fatal)
[OUTPUT]
Name es
Match app.*
Host elasticsearch
Port 9200
Index mobile-backend-logs
Type _doc
Logstash_Format On
Logstash_Prefix mobile-backend
Фильтр grep на уровне Fluent Bit — снижает объём данных в Elasticsearch, если debug-логи нужны только локально.
Elasticsearch и индекс
Для мобильного бэкенда с умеренной нагрузкой (до 10M событий в сутки) достаточно одной ноды Elasticsearch или managed-кластера (Elastic Cloud, AWS OpenSearch). Index lifecycle management (ILM) — обязателен: логи старше 30 дней переводим в cold tier или удаляем, иначе диск закончится за неделю.
// Шаблон маппинга для предотвращения dynamic mapping
{
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"level": { "type": "keyword" },
"service": { "type": "keyword" },
"user_id": { "type": "keyword" },
"request_id": { "type": "keyword" },
"message": { "type": "text" },
"duration_ms": { "type": "long" },
"status_code": { "type": "integer" }
}
}
}
Dynamic mapping — источник проблем: Elasticsearch автоматически выведет тип поля из первого значения, и если status_code первый раз пришёл как строка — все последующие числовые значения вызовут ошибку маппинга.
Kibana: поиск и дашборды
После настройки сбора — создаём Index Pattern в Kibana, настраиваем Discover для поиска по полям, строим Lens-дашборды:
- Error rate по endpoint за последний час
- Топ-10 медленных запросов (
duration_ms> 1000) - Активные пользователи по
user_idв реалтайме - Heatmap ошибок по часам и дням
KQL (Kibana Query Language) для поиска проще, чем SQL: level: error AND service: auth-service AND duration_ms > 500 — и сразу видишь все медленные ошибки авторизации.
Безопасность
Логи содержат user_id, device information, иногда — фрагменты данных запросов. Никогда не логировать: пароли, токены в полном виде, номера карт, персональные данные. PII в логах — это GDPR-нарушение. Маскируем на уровне логгера:
def mask_sensitive(data: dict) -> dict:
sensitive_keys = {'password', 'token', 'card_number', 'cvv'}
return {k: '***' if k in sensitive_keys else v for k, v in data.items()}
Сроки
Базовый EFK-стек с Docker Compose, структурированное логирование одного сервиса, базовые Kibana-дашборды: 2–3 дня. Production-ready setup с ILM, алертами через Kibana Watcher, несколькими сервисами и security: 5–8 дней. Стоимость рассчитывается индивидуально.







