# 📋 Логирование

## Стек технологий

| Компонент | Технология |
|-----------|------------|
| Фасад | SLF4J (`org.slf4j.Logger`) |
| Реализация | Logback (поставляется с `spring-boot-starter-web`) |
| Конфигурация | Стандартная Spring Boot (без кастомного `logback.xml`) |
| Экспорт (прод) | OpenTelemetry Java Agent → OTLP → SigNoz |
| Контекст тенанта | SLF4J MDC (`tenant.id`) |

---

## Архитектура

```mermaid
graph LR
    Code["Java-код<br/>log.info(...)"] --> SLF4J["SLF4J API"]
    SLF4J --> Logback["Logback"]
    Logback -->|"Локальная разработка"| Console["stdout / stderr"]
    Logback -->|"Продакшн"| OTelAgent["OTel Java Agent<br/>(Logback Appender)"]
    OTelAgent -->|"OTLP HTTP"| SigNoz["SigNoz"]
```

### Локальная разработка

Логи выводятся в `stdout` контейнера в стандартном формате Spring Boot:

```
2026-03-22 12:00:00.123  INFO 1 --- [main] c.m.app.config.DataInitializer : Initializing databases for 1 tenant(s)...
```

Просмотр логов:

```bash
docker compose logs -f backend
```

### Продакшн (Kubernetes)

OpenTelemetry Java Agent подключается как `-javaagent` в [Dockerfile](file:///mnt/HDD/magistr/magistr/backend/Dockerfile) и автоматически перехватывает логи Logback, экспортируя их в SigNoz по OTLP.

```dockerfile
ENTRYPOINT ["java", "-javaagent:opentelemetry-javaagent.jar", "-jar", "app.jar"]
```

Конфигурация агента задаётся через переменные окружения в [backend.yaml](file:///mnt/HDD/magistr/k8s/backend.yaml):

| Переменная | Значение | Назначение |
|------------|----------|------------|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://192.168.1.100:4318` | Адрес SigNoz Collector |
| `OTEL_SERVICE_NAME` | `magistr-backend` | Имя сервиса в SigNoz |
| `OTEL_RESOURCE_ATTRIBUTES` | `deployment.environment=default` | Окружение |
| `OTEL_LOGS_EXPORTER` | `otlp` | Экспорт логов через OTLP |
| `OTEL_METRICS_EXPORTER` | `otlp` | Экспорт метрик через OTLP |
| `OTEL_TRACES_EXPORTER` | `otlp` | Экспорт трейсов через OTLP |
| `OTEL_INSTRUMENTATION_LOGBACK_APPENDER_EXPERIMENTAL_CAPTURE_MDC_ATTRIBUTES` | `tenant.id` | Захват MDC-атрибута в логи |

> [!NOTE]
> В локальной разработке OpenTelemetry Agent также встроен в Docker-образ, но без переменных `OTEL_*` он работает в режиме noop — логи идут только в stdout.

---

## Мультитенантный контекст (MDC)

Каждый HTTP-запрос обогащается tenant ID через [TenantInterceptor](file:///mnt/HDD/magistr/magistr/backend/src/main/java/com/magistr/app/config/tenant/TenantInterceptor.java):

```java
// preHandle — при входе запроса
MDC.put("tenant.id", tenant);
Span.current().setAttribute("tenant.id", tenant);

// afterCompletion — после завершения
MDC.remove("tenant.id");
```

Это позволяет:
- Фильтровать логи по тенанту в SigNoz
- Коррелировать логи с трейсами через Span-атрибуты
- Идентифицировать, к какому университету относится каждая запись

---

## Использование в коде

### Классы с логированием

| Класс | Уровни | Что логируется |
|-------|--------|----------------|
| `TenantInterceptor` | DEBUG, WARN | Резолвинг тенанта, неизвестный тенант (404) |
| `TenantDataSourceConfig` | INFO, WARN, ERROR | Загрузка тенантов, fallback на H2 |
| `TenantRoutingDataSource` | INFO, WARN | Добавление/удаление тенантов, тест соединения |
| `TenantConfigWatcher` | INFO, ERROR, WARN | Изменения ConfigMap, Flyway миграции |
| `KubernetesTenantSecretUpdater` | INFO, WARN, ERROR | Безопасное обновление tenant Secret без вывода его содержимого |
| `DataInitializer` | INFO | Инициализация БД при старте |
| `ScheduleController` | INFO, ERROR | Запросы динамического расписания и ошибки генерации |

### Паттерн использования

```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClass {
    private static final Logger log = LoggerFactory.getLogger(MyClass.class);

    public void doSomething() {
        log.info("Операция выполнена: параметр={}", value);
        log.error("Операция завершилась ошибкой: типОшибки={}",
                e.getClass().getSimpleName());
        log.debug("Технические детали ошибки операции", e);
    }
}
```

Production-сообщения пишутся на русском языке и не включают `exception.getMessage()`,
JDBC URL, логины, пароли или содержимое Secret. Технический тип исключения допускается как
структурированное поле, а полный стек — в отдельной русскоязычной записи уровня `DEBUG`.

### Рекомендации по уровням

| Уровень | Когда использовать |
|---------|-------------------|
| `ERROR` | Необработанные ошибки, сбои подключения к БД, провалы миграций |
| `WARN` | Неизвестный тенант, нет конфигурации, fallback-сценарии |
| `INFO` | Успешные операции, CRUD-действия, старт/стоп компонентов |
| `DEBUG` | Детали резолвинга тенанта, ping-запросы |

---

## Настройка уровня логирования

В [application.properties](file:///mnt/HDD/magistr/magistr/backend/src/main/resources/application.properties) (по умолчанию закомментировано):

```properties
# Включить DEBUG для всего приложения
#logging.level.root=DEBUG

# Только для пакета приложения
logging.level.com.magistr.app=DEBUG

# Только для конкретного класса
logging.level.com.magistr.app.config.tenant.TenantInterceptor=DEBUG
```

Также можно задавать через переменные окружения:

```bash
LOGGING_LEVEL_ROOT=DEBUG
LOGGING_LEVEL_COM_MAGISTR_APP=DEBUG
```

---

## Просмотр логов

### Локально (Docker Compose)

```bash
# Все логи backend
docker compose logs -f backend

# Фильтрация по ключевому слову
docker compose logs -f backend | grep "tenant"
```

### Продакшн (SigNoz)

Логи доступны в веб-интерфейсе SigNoz → раздел **Logs**:
- Фильтрация по `service.name = magistr-backend`
- Фильтрация по `tenant.id` (из MDC)
- Корреляция с трейсами через общий `trace_id`
