diff --git a/Работа/Замена zap на slog.md b/Работа/Замена zap на slog.md new file mode 100644 index 0000000..95664fb --- /dev/null +++ b/Работа/Замена zap на slog.md @@ -0,0 +1,124 @@ +# Миграция klog/zap → slog + +## Контекст + +`tools/klog` — тонкая обёртка над `go.uber.org/zap`, которая является точкой входа для логирования во всех сервисах. +Модуль: `gitlab.adsw.io/platform/tools/v3`, потребляется 15+ репозиториями через vendor. + +## Текущий формат логов (сохраняется) + +```json +{"level":"info","caller":"main.go:42","msg":"starting service","source":"accounting","date":"2026-05-08T12:34:56.789+03:00"} +{"level":"error","caller":"auth.go:28","msg":"fetching user profile","source":"accounting","date":"2026-05-08T12:34:56.789+03:00","error":"context deadline exceeded"} +``` + +Поля: `level` (строчными), `caller`, `msg`, `source` (при init), `date` (ISO 8601 в каждом вызове через `cookFields()`). + +Формат воспроизводится через `log/slog` с кастомным JSON handler'ом + `ReplaceAttr` для приведения уровней к нижнему регистру. + +## Стратегия + +Публичный API `klog` **не меняется** — сервисы обновляют vendor и ничего не ломается. + +Единственное breaking изменение: `SetLogger(*zap.Logger)` → `SetLogger(*slog.Logger)`. +Используется только в тестах через `zap.NewNop()`. +**Решение:** добавить `SetNopLogger()` — тесты меняют одну строку без импорта zap. + +--- + +## Шаг 0 — tools (делается первым, разблокирует всё остальное) + +| # | Файл | Что менять | Сложность | +|---|------|------------|-----------| +| 1 | `tools/klog/log.go` | Переписать на `log/slog` с кастомным JSON handler'ом, сохранить формат, добавить `SetNopLogger()` | Средняя | +| 2 | `tools/go.mod` | Удалить `go.uber.org/zap` из `require` | Низкая | +| 3 | `tools/vendor/` | `go mod tidy && go mod vendor` | Низкая | + +После выпустить тег `v3.9.0`. + +--- + +## Шаг 1 — тесты с `klog.SetLogger(zap.NewNop())` (критично) + +Это **единственные места**, где внешний код завязан на тип `*zap.Logger`. +После обновления vendor без этих правок сервисы **не скомпилируются**. + +Замена везде одинаковая: `klog.SetLogger(zap.NewNop())` → `klog.SetNopLogger()` + +| # | Репозиторий | Файл | Сложность | +|---|-------------|------|-----------| +| 4 | `pilot` | `internal/service/agreement_link_test.go` | Низкая | +| 5 | `pilot` | `internal/service/application_test.go` | Низкая | +| 6 | `pilot` | `internal/service/distro_download_link_test.go` | Низкая | +| 7 | `pilot` | `internal/service/distro_test.go` | Низкая | +| 8 | `pilot` | `internal/service/download_history_test.go` | Низкая | +| 9 | `pilot` | `internal/service/download_link_test.go` | Низкая | +| 10 | `pilot` | `internal/service/mail_test.go` | Низкая | +| 11 | `hr` | `internal/service/department_test.go` | Низкая | +| 12 | `rubytech` | `internal/service/client_test.go` | Низкая | +| 13 | `rubytech` | `internal/service/customer_test.go` | Низкая | +| 14 | `rubytech` | `internal/service/project_test.go` | Низкая | +| 15 | `user` | `internal/private/graph/resolver_test.go` | Низкая | +| 16 | `user` | `internal/service/role_test.go` | Низкая | +| 17 | `user` | `internal/service/session_test.go` | Низкая | +| 18 | `user` | `internal/service/user_test.go` | Низкая | + +--- + +## Шаг 2 — vendor-бамп во всех сервисах (рутина, не требует изменений кода) + +Для сервисов без `SetLogger(zap.NewNop())` достаточно только обновить vendor. +Для `pilot`, `hr`, `rubytech`, `user` — сначала шаг 1, потом vendor-бамп. + +```bash +go get gitlab.adsw.io/platform/tools/v3@v3.9.0 +go mod vendor +``` + +| # | Репозиторий | Зависимость | Сложность | +|---|-------------|-------------|-----------| +| 19 | `accounting` | — | Низкая | +| 20 | `async` | — | Низкая | +| 21 | `document-generator` | — | Низкая | +| 22 | `education` | — | Низкая | +| 23 | `file-manager` | — | Низкая | +| 24 | `hr` | после п.11 | Низкая | +| 25 | `mailer` | — | Низкая | +| 26 | `partner` | — | Низкая | +| 27 | `pilot` | после пп.4–10 | Низкая | +| 28 | `private-api-gateway` | — | Низкая | +| 29 | `public-api-gateway` | — | Низкая | +| 30 | `rubytech` | после пп.12–14 | Низкая | +| 31 | `store` | — | Низкая | +| 32 | `unisender` | — | Низкая | +| 33 | `user` | после пп.15–18 | Низкая | +| 34 | `yandex` | — | Низкая | + +--- + +## Шаг 3 — api-gateway (отдельно, не блокирует остальное) + +`api-gateway` держит `*zap.Logger` как поле структуры `Application` и передаёт его +в `log.NewZapLogger()` из библиотеки `jensneuse/abstractlogger`. Это **независимо от klog** +и не блокирует миграцию остальных сервисов. + +| # | Файл | Проблема | Сложность | +|---|------|----------|-----------| +| 35 | `api-gateway/pkg/application/application.go` | Поле `ZapLogger *zap.Logger` — `abstractlogger` требует zap. Нужно либо оставить zap только здесь, либо заменить `abstractlogger` на slog-совместимый аналог | Высокая | +| 36 | `api-gateway/cmd/http/main.go` | `klog.InitLogger()` возвращает `*zap.Logger` сейчас, после миграции вернёт `*slog.Logger` — нужно отвязать инициализацию zap от klog | Средняя | + +--- + +## Порядок выполнения + +``` +Шаг 0: tools/klog/log.go → slog + SetNopLogger() → тег v3.9.0 + │ + ├─► Шаг 1: исправить 15 тестовых файлов (pilot, hr, rubytech, user) + │ + └─► Шаг 2: vendor-бамп в каждом сервисе по мере готовности (в течение месяца) + │ + └─► Шаг 3: api-gateway — решить вопрос с abstractlogger (отдельно) +``` + +Шаг 0 и Шаг 1 должны быть выполнены **до** того, как любой сервис обновит vendor.