Logging config, Sentry, Prometheus, Grafana, health checks, alerts
Пока приложение работает на ноутбуке, отладка — это print() и трейсбек в консоли. В production вы не видите запросы пользователей напрямую: нужно, чтобы система сама рассказывала, что с ней происходит. Это три разных, дополняющих друг друга практики — часто их называют «три столпа observability».
| Столп | Вопрос, на который отвечает | Инструмент |
|---|---|---|
| Логи | Что именно произошло в этот момент? | logging, ELK/Loki |
| Метрики | Сколько/как часто/как быстро? (тренды) | Prometheus + Grafana |
| Трейсы / ошибки | Где сломалось и почему? | Sentry, OpenTelemetry |
💡 Правило: логируйте события (структурированно), считайте метрики (числа во времени), ловите исключения (с контекстом). Не пытайтесь искать тренды по логам или отлаживать конкретную ошибку по метрикам — это разные инструменты для разных задач.
Django использует стандартный модуль logging Python. Держится всё на иерархии логгеров и уровнях — они и позволяют отфильтровать шум от важного.
| Уровень | Когда |
|---|---|
DEBUG | Детали для разработки (в production обычно выключен) |
INFO | Нормальные значимые события: «пользователь оплатил заказ» |
WARNING | Что-то подозрительное, но обработано: «повтор платежа» |
ERROR | Операция не выполнилась: «не удалось списать средства» |
CRITICAL | Система частично/полностью недоступна |
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
},
},
'handlers': {
'console': {'class': 'logging.StreamHandler', 'formatter': 'json'},
},
'root': {'handlers': ['console'], 'level': 'INFO'},
'loggers': {
'django.request': {'handlers': ['console'], 'level': 'ERROR', 'propagate': False},
'myapp': {'handlers': ['console'], 'level': 'INFO', 'propagate': False},
},
}# использование
import logging
logger = logging.getLogger(__name__)
def checkout(request, order_id):
logger.info('checkout started', extra={'order_id': order_id, 'user_id': request.user.id})
try:
charge(order_id)
except PaymentError:
logger.exception('payment failed', extra={'order_id': order_id}) # с трейсбеком
raise💡 Структурированные (JSON) логи вместо строк — потому что в production логи парсит машина (Loki, ELK, CloudWatch). По полю
order_idвы мгновенно найдёте все события заказа; по строкеf"order {id} failed"— нет.
⚠️ Никогда не логируйте секреты и персональные данные: пароли, токены, номера карт, содержимое
request.bodyс чувствительными полями. Логи часто доступнее, чем сама БД, и хранятся дольше.
⚠️ В контейнере пишите в
stdout, а не в файл. Файл внутри эфемерного контейнера исчезнет, а ротация — головная боль. Сбором логов из stdout занимается платформа (Docker, k8s, systemd).
Логи ERROR легко утонут в потоке. Sentry агрегирует исключения: группирует одинаковые, показывает частоту, трейсбек, значения переменных, какой пользователь и какой запрос их вызвал.
# settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=os.environ['SENTRY_DSN'],
integrations=[DjangoIntegration()],
environment=os.environ.get('ENV', 'production'),
release=os.environ.get('GIT_SHA'), # привязка ошибки к версии
traces_sample_rate=0.1, # 10% запросов — в performance-трейсинг
send_default_pii=False, # не слать персональные данные
)💡 Поле
release(git-SHA) сильно экономит время на разборе: Sentry покажет, что новая пачка ошибок полезла ровно после конкретного деплоя — сразу понятно, что именно её вызвало.
Метрики — это числа во времени: RPS, latency (p50/p95/p99), процент ошибок, длина очереди Celery, использование пула соединений. По ним строят графики и алерты.
# settings.py
INSTALLED_APPS += ['django_prometheus']
MIDDLEWARE = (
['django_prometheus.middleware.PrometheusBeforeMiddleware']
+ MIDDLEWARE
+ ['django_prometheus.middleware.PrometheusAfterMiddleware']
)# urls.py — Prometheus будет скрейпить /metrics
urlpatterns += [path('', include('django_prometheus.urls'))]Не тоните в сотне метрик — следите за четырьмя:
| Сигнал | Что показывает |
|---|---|
| Latency | Время ответа, особенно p95/p99 (хвосты, а не среднее!) |
| Traffic | RPS, нагрузка |
| Errors | Доля 5xx-ответов |
| Saturation | Насколько забиты ресурсы (CPU, пул БД, очередь) |
⚠️ Среднее время ответа врёт. 99% запросов по 50 мс и 1% по 10 с дадут «отличное» среднее, но этот 1% — ваши самые активные пользователи. Всегда смотрите перцентили (p95/p99).
⚠️ Эндпоинт
/metricsдолжен быть закрыт от публичного доступа (внутренняя сеть, basic-auth), иначе вы отдаёте наружу внутреннюю телеметрию.
Оркестратор (k8s, Docker Swarm, балансировщик) должен уметь спросить «ты живой?» и увести трафик с упавшего инстанса. Различают два типа проверок:
| Проба | Вопрос | Поведение |
|---|---|---|
| Liveness | Процесс жив? | Если нет — перезапустить контейнер |
| Readiness | Готов принимать трафик (БД, кэш доступны)? | Если нет — не слать трафик, но не убивать |
# views.py
from django.db import connection
from django.core.cache import cache
from django.http import JsonResponse
def liveness(request):
return JsonResponse({'status': 'alive'}) # просто «процесс отвечает»
def readiness(request):
checks = {}
try:
connection.ensure_connection()
checks['db'] = 'ok'
except Exception as e:
checks['db'] = f'fail: {e}'
try:
cache.set('health', '1', 5)
checks['cache'] = 'ok' if cache.get('health') == '1' else 'fail'
except Exception as e:
checks['cache'] = f'fail: {e}'
ok = all(v == 'ok' for v in checks.values())
return JsonResponse({'checks': checks}, status=200 if ok else 503)💡 Разделяйте liveness и readiness. Если в liveness проверять БД — при кратковременной недоступности БД оркестратор начнёт перезапускать живые контейнеры, усугубляя аварию. БД — это readiness.
Графики, на которые никто не смотрит в 3 часа ночи, не спасут от инцидента. Настройте алерты (Alertmanager, Grafana Alerting) на симптомы для пользователя:
💡 Алертите на симптомы («пользователям медленно/ошибки»), а не на причины («CPU 90%»). Высокий CPU при нормальных ответах — не инцидент. Ошибки при низком CPU — инцидент.
release и environment/metrics от публичного доступаВопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.
Далее: Базы Данных: Продвинутые темы