MBL
Go / Проекты / Open-Source-PR / opentelemetry-go #8397 — генерация x-пакета из общего шаблона
Go средний

opentelemetry-go #8397 — генерация x-пакета из общего шаблона

projectsopensourcego

Диаграмма: от бага до PR (archify) →

Смерженный PR: open-telemetry/opentelemetry-go #8397, "sdk/metric/internal/x: generate x package from x component template". Мерж 2026-06-03, +110/-92, файлы sdk/metric/internal/x/{features.go,features_test.go,x.go,x_test.go}.

Что за x-пакет и зачем его шаблонизировать

В opentelemetry-go у каждого SDK-модуля (sdk/internal/x, sdk/log/internal/x, sdk/metric/internal/x, exporters/prometheus/internal/x) есть свой пакет x — единая точка объявления экспериментальных feature-флагов, включаемых через переменные окружения OTEL_GO_X_*. До этого PR каждый такой пакет писался вручную и постепенно расходился в деталях.

PR приводит sdk/metric/internal/x к общему шаблону internal/shared/x/x.go.tmpl, из которого уже сгенерированы остальные три пакета. Итоговый файл x.go начинается с комментария // Code generated by gotmpl. DO NOT MODIFY. — это сигнал инструментам и людям, что правки нужно вносить в шаблон, а не в этот файл, иначе следующая генерация их сотрёт.

Главное изменение: один ключ → список ключей

До PR Feature[T] хранил один key string и отдавал его через Key() string. После — хранит keys []string и отдаёт через Keys() []string, а Lookup() проверяет ключи по очереди и возвращает значение первого непустого:

func (f Feature[T]) Lookup() (v T, ok bool) {
	for _, key := range f.keys {
		vRaw := os.Getenv(key)
		if vRaw != "" {
			return f.parse(vRaw)
		}
	}
	return v, ok
}

Это открывает практическую возможность: одна и та же экспериментальная фича может в будущем поддерживать несколько имён переменной окружения (например старое имя для обратной совместимости и новое — более удачное), без изменения сигнатуры Feature[T] ещё раз.

Проверено вживую на упрощённой копии этого же кода: если фича регистрируется с двумя ключами-алиасами и установлен только "старый" (второй по счёту) — Lookup() всё равно находит значение, перебирая ключи по порядку:

на play.golang.org

Вывод подтверждает: keys: [OTEL_GO_X_METRIC_EXPORT_BATCH_SIZE OTEL_GO_X_METRIC_BATCH_SIZE], нашли через второй ключ: 200 true — значение находится, даже если установлен не первый по счёту алиас.

PR также добавляет новую фичу MetricExportBatchSize (файл features.go) как первое практическое применение шаблона — с полным набором тестов на пустое/невалидное/нулевое/отрицательное/валидное значение переменной окружения.

Почему это ценно на собесе

Здесь ценность не в сложности diff'а (он небольшой и в основном механический), а в самой сути изменения: следование существующей конвенции репозитория вместо изобретения своей. Три других пакета уже были сгенерированы из этого шаблона — задача PR не "придумать, как лучше", а "привести четвёртый пакет к тому же виду, что и первые три", не внося отсебятины. На реальном код-ревью это то же самое умение: сначала понять, как уже принято делать в конкретной кодовой базе, и только потом писать код, а не сразу навязывать свой стиль.

Самопроверка 0 / 2
Понимаю, зачем Feature.Key() заменили на Feature.Keys() — поддержка нескольких алиасов одной переменной окружения
Знаю, что значит "Code generated by gotmpl. DO NOT MODIFY" и почему такой пакет нельзя редактировать руками
Как усвоено?