Skip to main content

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 использует числовые уровни логирования:

LevelNumberОписаниеКогда использовать
trace10Очень детальная отладкаРедко, только для глубокой диагностики
debug20Debug информацияРазработка, временная диагностика
info30Нормальные операцииБизнес-события, успешные операции
warn40ПредупрежденияНештатные ситуации, но не ошибки
error50ОшибкиИсключения, failed операции
fatal60Критические ошибкиПриложение не может продолжать

LOG_LEVEL Configuration

Environment variable: LOG_LEVEL

Контролирует какие логи приложение выводит в stdout:

LOG_LEVELЧто выводитсяЧто в LokiЧто в Dokploy
debugdebug + info + warn + errorВсёВсё
infoinfo + warn + errorinfo+info+
warnwarn + errorwarn+warn+
errorerror толькоerrorerror
Ключевой принцип

LOG_LEVEL контролирует ВСЁ. Если приложение не вывело лог в stdout — его нигде не будет: ни в Dokploy, ни в Loki, ни в Grafana.

Как изменить

  1. Обновить LOG_LEVEL в Dokploy → Environment Variables
  2. Перезапустить контейнер
  3. Новые логи будут следовать новому уровню

Dokploy vs Grafana

ХарактеристикаDokployGrafana (Loki)
ИсточникDocker stdout напрямуюLoki (через Promtail)
ФильтрацияНетLogQL запросы
UI labelsСвои (debug/info/success)По реальному level
СтруктураСырой JSONParsed fields
ПоискПростой текстПо userId, requestId, etc.
ХранениеDocker logs7 дней retention
НазначениеБыстрый просмотрДетальный анализ
Dokploy UI labels

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

  1. Проверить Promtail:

    docker logs promtail
  2. Проверить Loki:

    curl -s http://localhost:3100/ready
  3. Проверить Docker socket:

    docker exec promtail ls -la /var/run/docker.sock

"Entry out of order" ошибки в Loki

Это происходит когда логи приходят с timestamp старше уже принятых.

Решение: Promtail настроен отбрасывать логи старше 24h:

- drop:
older_than: 24h

Высокое использование диска

  1. Проверить Loki:

    docker exec loki du -sh /loki/
  2. Принудительный compaction:

    curl -X POST http://localhost:3100/compactor/ring/forget
  3. Проверить 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

ServicePortОписание
Prometheus9090Metrics collection
Grafana3030Visualization
Loki3100Log aggregation
Promtail9080Log shipping
Tempo3200, 4317, 4318Distributed 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)