Настройка информации о версиях приложения Go с помощью ldflags
Development | Комментировать запись
При развертывании приложений в производственной среде информация о версии и другие метаданные улучшают процессы мониторинга, логирования и отладки. В целом любая идентифицирующая информация в будущем поможет вам отслеживать ваши сборки.
Информация о версии обычно включает в себя высокодинамичные данные, такие как время сборки, машина или пользователь, собирающий двоичный файл, идентификатор коммита системы управления версиями (VCS), на основе которого он был создан, и многое другое. Поскольку эти значения постоянно меняются, кодировать их непосредственно в исходный код и менять перед каждой новой сборкой утомительно, кроме того, можно наделать ошибок: исходные файлы могут перемещаться, а переменные и константы будут заменить файлы в ходе разработки, нарушая процесс сборки.
Читайте также: Переменные и константы в Go
Одним из решений этой проблемы в Go является использование параметра -ldflags в команде go build для вставки динамической информации в двоичный файл во время сборки – при этом нет необходимости изменять исходный код. В этом флаге ld обозначает linker, компоновщик – это программа, которая связывает различные части скомпилированного исходного кода в финальный файл. Соответственно, ldflags – это флаги компоновщика. ldflags так называется потому, что он передает флаг базовому компоновщику цепочки инструментов Go, cmd/link, который позволяет изменять значения импортируемых пакетов во время сборки из командной строки.
В этом мануале вы научитесь использовать -ldflags, чтобы изменить значение переменных во время сборки и добавлять динамическую информацию в бинарный файл (на примере простого приложения, которое выводит информацию о версии на экран).
Требования
Для работы вам нужна рабочая среда Go. У нас есть ряд мануалов для разных систем, которые помогут вам создать такую среду:
- Установка Go и настройка локальной среды разработки в macOS
- Установка Go и настройка локальной среды разработки в Ubuntu 18.04
- Установка Go и настройка локальной среды разработки в Windows 10
1: Сборка тестового приложения
Прежде чем вы сможете использовать ldflags для представления динамических данных, вам сначала нужно создать простое тестовое приложение. Наше приложение изначально будет выводить только статическую информацию о версиях. Давайте создадим его.
В каталоге src создайте каталог с именем вашего приложения. В этом мануале мы назовем его просто app:
mkdir app
Перейдите в этот каталог:
cd app
Затем в текстовом редакторе создайте точку входа main.go:
nano main.go
Теперь введите в этот файл код, с помощью которого приложение сможет выводить информацию о версии:
package main
import (
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
}
Внутри функции main() мы объявили переменную Version, затем ввели строку Version:, за которой следует символ табуляции \t и объявленная переменная.
Читайте также: Основы работы со строками в Go
На этом этапе переменная Version имеет значение development, эта версия будет версией по умолчанию для данного приложения. Позже мы изменим это значение на официальный номер версии, упорядоченный в соответствии с семантическим версионированием.
Сохраните и выйдите из файла. После этого соберите и запустите приложение, чтобы убедиться, что оно отображает правильную версию:
go build
./app
Вы увидите следующий вывод:
Version: development
Теперь у вас есть приложение, которое выводит на экран информацию о версии по умолчанию, но пока что вы не можете передать ему информацию о текущей версии во время сборки. На следующем этапе мы попробуем использовать -ldflags и команду go build, чтобы решить эту проблему.
2: Использование параметра -ldflags
Как упоминалось ранее, ldflags используется для передачи флагов базовому компоновщику в цепочке инструментов Go. Параметр работает по следующему синтаксису:
В этом примере мы передали флаг flag базовой команде go tool link, которая выполняется как часть go build. Эта команда берет содержимое ldflags в двойные кавычки, чтобы избежать символов, которые командная строка может интерпретировать иначе. В этих кавычках вы можете передать много разных флагов link. В данном мануале мы будем использовать флаг -X для записи информации в переменную во время соединения, за которой следует путь пакета к переменной и ее новое значение:
go build -ldflags="-X 'package_path.variable_name=new_value'"
Итак, внутри кавычек теперь есть опция -X и пара ключ-значение, где указывается переменная, которую нужно изменить, и ее новое значение. Символ точки разделяет путь к пакету и имя переменной, а одинарные кавычки используются, чтобы избежать разделяющих символов в паре ключ-значение.
Чтобы заменить переменную Version в вашем приложении, используйте такой синтаксис в последнем блоке команд. Он позволяет передать новое значение и собрать новый файл:
go build -ldflags="-X 'main.Version=v1.0.0'"
В этой команде main – это путь пакета переменной Version, поскольку эта переменная находится в файле main.go. Version – это переменная, в которую вы пишете новые данные, а v1.0.0 – это ее новое значение.
Чтобы использовать ldflags, значение, которое вы хотите изменить, должно существовать и быть переменной уровня пакета с типом string. Это может быть как экспортированная, так и неэкспортированная переменная. Значение не может быть const или устанавливаться в результате вызова функции. Наша переменная Version соответствует всем этим требованиям: она уже объявлена как переменная в файле main.go, а ее текущее значение (development) и требуемое значение (v1.0.0) являются строками.
Как только ваш новый бинарный файл будет собран, запустите приложение:
./app
Вы получите следующий вывод:
Version: v1.0.0
Используя -ldflags, вы успешно изменили значение переменной Version с development на v1.0.0.
У нас получилось изменить строковую переменную внутри простого приложения во время сборки. Используя ldflags, вы можете с помощью командной строки встраивать в двоичный файл сведения о версии, информацию о лицензировании и многое другое.
В этом примере переменная, которую вы изменили, находилась в основной программе, потому определить путь было просто. Но иногда найти путь к переменным сложнее. Далее мы попробуем изменить значения переменных в подпакетах, чтобы научиться определять более сложные пути к пакетам.
3: Изменение переменных в подпакетах
В последнем разделе мы изменили переменную Version, которая была в пакете верхнего уровня. Но это не всегда так. Часто размещать переменные удобнее в другом пакете, так как main не является импортируемым пакетом. Чтобы смоделировать такую ситуацию, мы создадим новый подпакет app/build, который будет хранить информацию о времени сборки бинарного файла и имя пользователя, который выполнил команду build.
Чтобы добавить новый подпакет, сначала добавьте в проект новый каталог build:
mkdir -p build
Затем создайте новый файл build.go для хранения новых переменных:
nano build/build.go
В текстовом редакторе добавьте новые переменные Time и User:
package build
var Time string
var User string
Переменная Time будет содержать строковое представление времени, в которое был создан двоичный файл. Переменная User будет содержать имя пользователя, который собрал этот файл. Поскольку эти две переменные всегда будут иметь значения, вам не нужно устанавливать в них значения по умолчанию, как это было с Version.
Сохраните и закройте файл.
Затем откройте main.go, чтобы добавить новые переменные в ваше приложение:
nano main.go
Добавьте следующие выделенные строки:
package main
import (
"app/build"
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
fmt.Println("build.Time:\t", build.Time)
fmt.Println("build.User:\t", build.User)
}
Эти строки сначала импортируют пакет app/build, а затем выводят build.Time и build.User – так же, как было в случае с Version.
Сохраните файл, затем выйдите из текстового редактора.
Чтобы захватить эти переменные с помощью ldflags, вы можете использовать путь импорта app/build, а за ним указать .User или .Time (поскольку вы уже знаете путь импорта). Однако для моделирования более сложной ситуации, в которой путь к переменной неочевиден, вместо этого нужно использовать команду nm цепочки инструментов Go.
Команда go tool nm выведет символы, относящиеся к данному исполняемому файлу, объектному файлу или архиву. В этом случае символ относится к объекту в коде, такому как определенная или импортированная переменная или функция. Генерируя таблицу символов с помощью nm и используя grep для поиска переменной, вы можете быстро найти информацию о ее пути.
Примечание. Команда nm не поможет вам найти путь к вашей переменной, если имя пакета содержит какие-либо символы, отличные от ASCII, или символы «или%», поскольку это ограничение самого инструмента.
Чтобы использовать эту команду, сначала соберите файл приложения:
go build
Теперь, когда приложение app собрано, можно использовать инструмент nm и выполнить поиск по выводу:
go tool nm ./app | grep app
Инструмент nm выведет много данных. потому в команде мы используем символ |, который направит вывод в команду grep. Она найдет в выводе все строки, в заголовке которых было приложение верхнего уровня app.
Вы получите примерно такой вывод:
55d2c0 D app/build.Time
55d2d0 D app/build.User
4069a0 T runtime.appendIntStr
462580 T strconv.appendEscapedRune
. . .
Первые две строки содержат пути к двум переменным, которые вы ищете: app/build.Time и app/build.User.
Теперь, когда вы знаете нужные пути, соберите приложение еще раз, на этот раз изменяя переменные Version, User, and Time во время сборки. Для этого передайте несколько флагов -X в парпметре -ldflags:
go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'"
Здесь вы передали команду id -u -n для отображения текущего пользователя и команду date для отображения текущей даты.
Как только исполняемый файл соберется, запустите программу:
./app
Эта команда при запуске в системе Unix сгенерирует примерно такой вывод:
Version: v1.0.0
build.Time: Fri Oct 4 19:49:19 UTC 2019
build.User: 8host
Теперь у вас есть бинарный файл, который содержит информацию о версиях и сборке. Эти данные могут пригодиться при устранении неполадок.
Заключение
В мануале вы научились пользоваться параметром ldflags. Этот полезный инструмент позволяет добавлять в сборку файлов информацию о версии приложения. С его помощью вы можете управлять флагами функций, информацией о среде и о версиях и многими другими данными, не внося изменений в исходный код.
Tags: Go, Golang