149 lines
7.4 KiB
Markdown
149 lines
7.4 KiB
Markdown
# Миграция 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.
|
||
|
||
|
||
|
||
| Сервис | Готовность | Пролит |
|
||
| ------------ | :--------: | :----: |
|
||
| accounting | | |
|
||
| async | | |
|
||
| document | | |
|
||
| education | | |
|
||
| environment | | |
|
||
| file-manager | | |
|
||
| hr | + | |
|
||
| mailer | | |
|
||
| partner | | |
|
||
| pilot | + | + |
|
||
| private | | |
|
||
| public | | |
|
||
| rubytech | + | |
|
||
| store | | |
|
||
| unisender | | |
|
||
| user | | |
|
||
| yandex | + | |
|
||
| api-gateway | | |
|
||
|