Files
obsidian/Работа/Замена zap на slog.md

7.5 KiB
Raw Blame History

Миграция klog/zap → slog

Контекст

tools/klog — тонкая обёртка над go.uber.org/zap, которая является точкой входа для логирования во всех сервисах. Модуль: gitlab.adsw.io/platform/tools/v3, потребляется 15+ репозиториями через vendor.

Текущий формат логов (сохраняется)

{"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-бамп.

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 после пп.410 Низкая
28 private-api-gateway Низкая
29 public-api-gateway Низкая
30 rubytech после пп.1214 Низкая
31 store Низкая
32 unisender Низкая
33 user после пп.1518 Низкая
34 yandex Низкая

Шаг 3 — api-gateway (отдельно, не блокирует остальное)

api-gateway держит *zap.Logger как поле структуры Application и передаёт его в log.NewZapLogger() из библиотеки jensneuse/abstractlogger. Это независимо от klog и не блокирует миграцию остальных сервисов.

# Файл Проблема Сложность
35 api-gateway/pkg/application/application.go Поле ZapLogger *zap.Loggerabstractlogger требует 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
file-manager +
hr +
mailer
partner
pilot + +
private
public
rubytech +
store
unisender
user
yandex +
api-gateway
mq-tools
tools + +