Log Architecture
Архитектура системы логирования: от приложения до Grafana.
Overview
Логи проходят через несколько слоёв, каждый из которых выполняет свою функцию:
┌─────────────────────────────────────────────────────────────────────┐
│ Application │
│ LOG_LEVEL=info (env var) │
│ ↓ │
│ Pino logger → stdout │
│ (level 20=debug, 30=info, 40=warn, 50=error) │
└───────┬─────────────────────────────────────────────────────────────┘
↓
┌───────────────────────────────────────────────────────────────────────┐
│ Docker json-file driver │
│ /var/lib/docker/containers/{id}/{id}-json.log │
│ │
│ ⚠️ Configure rotation to prevent disk overflow (see below) │
└───────┬───────────────────────────────────┬───────────────────────────┘
↓ ↓
┌───────────────────┐ ┌───────────────────────────────────────┐
│ Dokploy UI │ │ Promtail │
│ Shows ALL logs │ │ - Parses JSON structure │
│ (simple viewer) │ │ - Extracts: level, userId, etc. │
│ │ │ - Drops: health checks, old logs │
└───────────────────┘ └───────────────┬───────────────────────┘
↓
┌───────────────────────────────────────┐
│ Loki │
│ - Stores structured logs │
│ - Retention: 7 days │
│ - Queryable via LogQL │
└───────────────┬───────────────────────┘
↓
┌───────────────────────────────────────┐
│ Grafana │
│ Query by: level, userId, requestId │
│ Filter: {level="30"} for info only │
└───────────────────────────────────────┘
Log Levels
Pino использует числовые уровни логирования:
| Level | Number | Описание | Когда использовать |
|---|---|---|---|
trace | 10 | Очень детальная отладка | Редко, только для глубокой диагностики |
debug | 20 | Debug информация | Разработка, временная диагностика |
info | 30 | Нормальные операции | Бизнес-события, успешные операции |
warn | 40 | Предупреждения | Нештатные ситуации, но не ошибки |
error | 50 | Ошибки | Исключения, failed операции |
fatal | 60 | Критические ошибки | Приложение не может продолжать |
LOG_LEVEL Configuration
Environment variable: LOG_LEVEL
Контролирует какие логи приложение выводит в stdout:
| LOG_LEVEL | Что выводится | Что в Loki | Что в Dokploy |
|---|---|---|---|
debug | debug + info + warn + error | Всё | Всё |
info | info + warn + error | info+ | info+ |
warn | warn + error | warn+ | warn+ |
error | error только | error | error |
LOG_LEVEL контролирует ВСЁ. Если приложение не вывело лог в stdout — его нигде не будет: ни в Dokploy, ни в Loki, ни в Grafana.
Как изменить
- Обновить
LOG_LEVELв Dokploy → Environment Variables - Перезапустить контейнер
- Новые логи будут следовать новому уровню
Dokploy vs Grafana
| Характеристика | Dokploy | Grafana (Loki) |
|---|---|---|
| Источник | Docker stdout напрямую | Loki (через Promtail) |
| Фильтрация | Нет | LogQL запросы |
| UI labels | Свои (debug/info/success) | По реальному level |
| Структура | Сырой JSON | Parsed fields |
| Поиск | Простой текст | По userId, requestId, etc. |
| Хранение | Docker logs | 7 дней retention |
| Назначение | Быстрый просмотр | Детальный анализ |
Dokploy показывает свои labels (debug, info, success) — это визуализация UI, не реальный log level.
Реальный уровень — в JSON поле "level": 30.
Grafana Queries (LogQL)
Базовые запросы
# Все backend логи
{service="goloot-backend"}
# Только ошибки
{service="goloot-backend"} | json | level >= 50
# Только info level (точно)
{service="goloot-backend"} | json | level = 30
# Фильтр по пользователю
{service="goloot-backend"} | json | userId = "user-uuid-here"
# Фильтр по requestId
{service="goloot-backend"} | json | requestId = "req-uuid-here"
# Поиск в сообщении
{service="goloot-backend"} |= "Telegram"
Debugging Flow
# 1. Найти логи пользователя
{service="goloot-backend"} | json | userId = "user-uuid"
# 2. Найти ошибки за период
{service="goloot-backend"} | json | userId = "user-uuid" | level >= 50
# 3. Все логи одного запроса (по requestId)
{service="goloot-backend"} | json | requestId = "abc-123-def"
# 4. Бизнес-события
{service="goloot-backend"} |= "[BUSINESS]" | json | userId = "user-uuid"
Storage & Retention
Loki Retention
Логи хранятся 7 дней (168 часов).
Конфигурация в monitoring/loki-config.yml:
compactor:
retention_enabled: true
retention_delete_delay: 2h
table_manager:
retention_deletes_enabled: true
retention_period: 168h # 7 days
Логи старше 7 дней автоматически удаляются при compaction.
Docker Log Rotation
Docker по умолчанию не ротирует логи. Без настройки диск переполнится!
На VPS сервере:
# 1. Создать/редактировать конфиг
sudo nano /etc/docker/daemon.json
Добавить:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
Это ограничивает каждый контейнер:
- 3 файла максимум
- 50MB на файл
- Итого: ~150MB max на контейнер
# 2. Перезапустить Docker
sudo systemctl restart docker
# 3. Проверить
docker info | grep -i log
Существующие контейнеры нужно пересоздать для применения новых настроек. Новые контейнеры автоматически используют rotation policy.
Проверка использования диска
# Размер Docker логов
sudo du -sh /var/lib/docker/containers/*/
# Найти самые большие логи
sudo find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \; | sort -k5 -h
# Размер данных Loki
docker exec loki du -sh /loki/
Troubleshooting
Логи не появляются в Grafana
-
Проверить Promtail:
docker logs promtail -
Проверить Loki:
curl -s http://localhost:3100/ready -
Проверить Docker socket:
docker exec promtail ls -la /var/run/docker.sock
"Entry out of order" ошибки в Loki
Это происходит когда логи приходят с timestamp старше уже принятых.
Решение: Promtail настроен отбрасывать логи старше 24h:
- drop:
older_than: 24h
Высокое использование диска
-
Проверить Loki:
docker exec loki du -sh /loki/ -
Принудительный compaction:
curl -X POST http://localhost:3100/compactor/ring/forget -
Проверить Docker логи:
sudo du -sh /var/lib/docker/containers/*/
Debug логи не видны при LOG_LEVEL=debug
Проверить что контейнер перезапущен после изменения env variable:
# В Dokploy: Deployments → Restart
# Или через CLI:
docker restart <container_id>
Promtail Pipeline
Promtail обрабатывает логи перед отправкой в Loki:
pipeline_stages:
# 1. Parse Docker JSON wrapper
- json:
expressions:
output: log
# 2. Parse application JSON
- json:
expressions:
level: level
msg: msg
requestId: requestId
userId: userId
# 3. Extract labels (low cardinality)
- labels:
level:
# 4. Structured metadata (high cardinality)
- structured_metadata:
requestId:
userId:
# 5. Drop old logs
- drop:
older_than: 24h
# 6. Drop health checks
- drop:
expression: '.*(health|metrics|favicon).*'
Infrastructure
Services
| Service | Port | Описание |
|---|---|---|
| Prometheus | 9090 | Metrics collection |
| Grafana | 3030 | Visualization |
| Loki | 3100 | Log aggregation |
| Promtail | 9080 | Log shipping |
| Tempo | 3200, 4317, 4318 | Distributed tracing |
Quick Start
cd monitoring
docker-compose up -d
Access
- Grafana:
https://grafana.goloot.online(Traefik + SSL, логин/пароль) - Prometheus, Loki — только внутри Docker network (нет внешних портов)
- API-доступ через Grafana datasource proxy (см. Monitoring Setup → Grafana API)
Related
- Troubleshooting Guide — operational runbook: от жалобы пользователя до root cause
- Observability — Logger API, Metrics, Tracing (код)
- Logging Guidelines — Стандарты логирования