Настройка логування бекенду мобільного приложення (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, 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 днів. Вартість розраховується індивідуально.







