Поздравляю! Ты дошел до финала курса "Production Mastery". Это последнее, недостающее звено, которое превратит твою систему из «работает» в «надежно работает и легко чинится».
У тебя уже есть бот, который торгует 24/7. У него есть мониторинг, бэкапы и автоматическое развертывание. Но есть одна боль, знакомая каждому, кто управляет автоматическими системами...
Давай быстро пробежимся по понятиям, которые нам сегодня понадобятся. Без воды, только суть.
Чтобы понять всю мощь Loki, давай сравним два подхода к поиску проблемы.
| ❌ До Loki (твой текущий процесс) | ✅ После этого урока (с Loki) |
|---|---|
| 1. Получаешь уведомление, что бот сделал странную сделку. | 1. Получаешь автоматическое уведомление в Telegram: "Error rate > 5 in 5m". |
| 2. Подключаешься к серверу по SSH. | 2. Открываешь Grafana в браузере. |
3. Выполняешь docker-compose logs trading-bot --since 1h > bot.log. Ждешь. |
3. В поле поиска вводишь: {app="trading-bot", level="error"}. |
4. Открываешь файл bot.log размером 50 МБ и ищешь глазами или через grep слово "ERROR". |
4. Мгновенно видишь все ошибки за последний час. |
| 5. Находишь ошибку, но хочешь посмотреть, что было до нее. Снова скроллишь гигантский файл. | 5. Кликаешь на строку с ошибкой и видишь логи "до" и "после" в удобном интерфейсе. |
| 6. Потрачено: 1-2 часа. Нервы на пределе. | 6. Потрачено: 5 минут. Проблема ясна. |
Представь: 3 часа ночи. Тебя будит уведомление от биржи: твоя позиция по ETH закрыта с убытком в $500. Сердце уходит в пятки. Ты же настраивал стоп-лосс гораздо ниже! Что произошло?
Ты вскакиваешь, открываешь ноутбук, подключаешься к серверу. Твой бот — это Docker-контейнер. Первая мысль — посмотреть логи. Ты пишешь:
docker-compose logs trading-bot --since 2h
На экран вываливается стена текста. Десятки тысяч строк. Информация о каждом тике цены, каждом пинге к API биржи, каждом расчете индикатора. Ты пытаешься найти что-то осмысленное с помощью grep, но это как искать иголку в стоге сена. Ты не знаешь, что именно искать: "error", "stop-loss", "close position"?
Через час мучительного скроллинга и фильтрации ты наконец находишь строчку, где бот получил от биржи странный ответ, не смог его обработать, запаниковал и закрыл позицию по рынку. Проблема найдена, но какой ценой? Ты потратил кучу времени и нервов. А если бы у тебя было 5 ботов? Ты бы искал в 5 раз дольше.
Эта боль — главная причина, по которой мы сегодня внедряем Loki.
grep?grep и docker logs — это как поиск по одному огромному текстовому файлу на твоем компьютере. Это работает для маленьких файлов, но не для потока данных в реальном времени.
Loki — это Google для твоих логов.
Ключевое отличие — в метках (labels). Loki не индексирует весь текст лога (поэтому он такой дешевый и быстрый). Он индексирует только метки, которые ты сам задаешь. Например:
app="trading-bot-eth"env="production"level="error"Когда ты делаешь запрос {app="trading-bot-eth", level="error"}, Loki мгновенно находит все потоки логов с этими метками, а уже потом ищет по тексту внутри этих отфильтрованных потоков. Это на порядки эффективнее, чем читать всё подряд.
docker-compose.ymlМы расширим наш стек из урока 6, где мы настраивали Grafana и Prometheus. Grafana у нас уже есть, и она умеет работать с Loki "из коробки".
Открой свой docker-compose.yml и добавь в него сервисы loki и promtail. Вот полный пример, как он должен выглядеть:
version: '3.8'
services:
# Твой бот (или боты) уже здесь
trading-bot:
image: my-trading-bot:latest
restart: unless-stopped
# ... остальные настройки ...
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped
# Prometheus у тебя уже может быть, оставляем его
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
restart: unless-stopped
# --- Новые сервисы ---
loki:
image: grafana/loki:2.9.2
ports:
- "3100:3100"
volumes:
- loki-data:/loki
# Мы добавим конфиг для retention чуть позже
# - ./loki-config.yml:/etc/loki/local-config.yaml
command: -config.file=/etc/loki/local-config.yaml
restart: unless-stopped
promtail:
image: grafana/promtail:2.9.2
volumes:
- /var/log:/var/log
- ./promtail-config.yml:/etc/promtail/config.yml
# Важнейшая строка! Даем Promtail доступ к сокету Docker,
# чтобы он мог автоматически находить все контейнеры и их логи.
- /var/run/docker.sock:/var/run/docker.sock
command: -config.file=/etc/promtail/config.yml
restart: unless-stopped
depends_on:
- loki
volumes:
grafana-data:
loki-data:
Теперь, после запуска docker-compose up -d, у тебя поднимутся два новых контейнера: база данных для логов и сборщик.
Promtail нужно объяснить, откуда брать логи. Мы используем его волшебную способность — автообнаружение Docker-контейнеров. Для этого создадим файл promtail-config.yml рядом с docker-compose.yml.
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
# Файл, где Promtail запоминает, на каком месте он закончил читать логи.
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: containers
# Главная магия здесь:
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
# Для каждого найденного контейнера Promtail будет создавать метки
relabel_configs:
# Имя контейнера
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
# Имя приложения из лейблов docker-compose
- source_labels: ['__meta_docker_container_label_com_docker_compose_service']
target_label: 'app'
Что здесь происходит? Promtail подключается к Docker и для каждого запущенного контейнера автоматически собирает логи. При этом он создает две полезные метки: container (например, my-project-trading-bot-1) и app (например, trading-bot). Этого уже достаточно для начала!
Теперь самое интересное. Перезапусти свой стек: docker-compose down && docker-compose up -d.
http://localhost:3000).http://loki:3100. Нажми Save & Test. Ты должен увидеть "Data source is working".app. Выбери app="trading-bot". Ты увидишь все логи своего бота!А теперь решим нашу ночную проблему. Чтобы найти все ошибки за последний час, введи в строку запроса:
{app="trading-bot"} |= "ERROR"
Нажми Enter. И... вот они! Все строки, содержащие "ERROR", отфильтрованы и показаны тебе. Никакого SSH и grep. Это уже победа.
Поиск по тексту "ERROR" — это хорошо, но мы можем лучше. Что если мы хотим найти все логи, связанные с конкретной сделкой? Для этого логи должны быть не просто текстом, а структурой. Например, JSON.
Установим библиотеку для JSON-логирования: pip install python-json-logger.
Теперь в коде твоего бота, там где ты настраиваешь логирование, используй logging.config.dictConfig. Это стандартный способ в Python. Вот полный пример конфигурации:
import logging
import logging.config
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(name)s %(levelname)s %(message)s %(trade_id)s',
},
},
'handlers': {
'stdout': {
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
'formatter': 'json',
},
},
'loggers': {
'': {
'handlers': ['stdout'],
'level': 'INFO',
},
}
}
# Применяем конфигурацию при старте приложения
logging.config.dictConfig(LOGGING_CONFIG)
# Пример использования
logger = logging.getLogger(__name__)
def process_trade(trade_id, amount):
extra_info = {'trade_id': trade_id}
logger.info(
f"Processing trade with amount {amount}",
extra=extra_info
)
try:
# ... какая-то логика ...
if amount < 0:
raise ValueError("Amount cannot be negative")
logger.info("Trade processed successfully", extra=extra_info)
except Exception as e:
logger.error(
f"Failed to process trade: {e}",
extra=extra_info,
exc_info=True # Добавляет traceback в лог
)
# ---
logger.info("Bot started")
process_trade("tx-12345", 100)
process_trade("tx-67890", -50)
Что изменилось? Теперь каждая строка лога — это JSON! В Grafana это будет выглядеть как структурированные поля. И теперь ты можешь писать еще более мощные запросы:
Найти все события для сделки "tx-67890":
{app="trading-bot"} | json | trade_id="tx-67890"
Оператор | json говорит Loki распарсить строку как JSON, а | trade_id="tx-67890" — это фильтр по содержимому JSON-поля. Теперь ты можешь отследить жизненный цикл любой операции в твоей системе за секунды.
Хватит смотреть в логи, пусть они сами приходят к тебе, когда что-то не так. Настроим алерт в Grafana, который будет писать в Telegram, если количество ошибок превысит порог.
count_over_time({app="trading-bot"} |= "ERROR" [5m])
$A > 5. Это значит: "если запрос А вернул значение больше 5".Готово! Теперь, если твой бот начнет сыпать ошибками, ты узнаешь об этом первым, а не от брокера.
Последний штрих. Логи — это не то, что нужно хранить вечно. Они занимают место. Loki спроектирован для работы с дешевыми облачными хранилищами (типа Amazon S3), но для нашей одиночной установки мы просто настроим автоматическое удаление старых логов.
Создай файл loki-config.yml:
auth_enabled: false
server:
http_listen_port: 3100
ingester:
lifecycler:
address: 127.0.0.1
ring:
kvstore:
store: inmemory
replication_factor: 1
final_sleep: 0s
chunk_idle_period: 1m
chunk_target_size: 1048576
chunk_retain_period: 30s
max_transfer_retries: 0
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/boltdb-shipper-active
cache_location: /loki/boltdb-shipper-cache
cache_ttl: 24h
shared_store: filesystem
filesystem:
directory: /loki/chunks
# --- Самая важная часть ---
limits_config:
# Удалять логи старше 30 дней (720 часов)
retention_period: 720h
Теперь раскомментируй строку с монтированием этого конфига в docker-compose.yml:
loki:
# ...
volumes:
- loki-data:/loki
- ./loki-config.yml:/etc
[tokens: in=511 out=5403 thinking=2593 | cost=$0.0806]
Хочешь больше? Полный курс AI Agents включает Production-модуль + 27 других уроков
AI Agents $199 →