← На главную

Парсинг конфигов в Go с помощью Viper

Редкая программа обходится без файла конфигурации. Даже если вы пишите простенький REST-сервис, то ему как минимум нужно знать, какой порт и на каком интерфейсе слушать, а также где искать PostgreSQL. Что уж говорить о более сложных приложениях. Для чтения конфигов в проектах на Go часто используют библиотеку spf13/viper.

Почему Viper? Потому что он умеет очень много всего:

  • Поддерживаются форматы Yaml, Json, INI и другие;
  • Параметры конфигурации можно переопределять через флаги;
  • Также параметры можно задавать через переменные окружения, как это принято в Kubernetes;
  • Viper умеет забирать параметры из etcd и Consul;
  • Параметры можно менять на лету, что позволяет изменить поведение программы без перезапуска;

Другими словами, какие бы требования не предъявлялись к приложению, Viper почти наверняка сможет их удовлетворить. Вот все им и пользуются.

Все возможности библиотеки мы рассматривать не будем, так как их много и получится пересказ документации. Рассмотрим только самый базовый пример использования, который обычно и нужен:

func run(configPath string) { customFormatter := new(log.TextFormatter) customFormatter.TimestampFormat = "2006-01-02 15:04:05" customFormatter.FullTimestamp = true log.SetFormatter(customFormatter) viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.SetEnvPrefix("restexample") viper.SetDefault("loglevel", "debug") viper.SetDefault("listen", "localhost:8080") viper.SetDefault("db.url", "postgres://localhost") if configPath != "" { log.Infof("Parsing config: %s", configPath) viper.SetConfigFile(configPath) err := viper.ReadInConfig() if err != nil { log.Panicf("Unable to read config file: %s", err) } } else { log.Infof("Config file is not specified.") } logLevelString := viper.GetString("loglevel") logLevel, err := log.ParseLevel(logLevelString) if err != nil { log.Panicf("Unable to parse loglevel: %s", logLevelString) } log.SetLevel(logLevel) log.Infof("Listen: %s", viper.GetString("listen")) log.Infof("DB URL: %s", viper.GetString("db.url")) }

Путь до файла конфигурации передается приложению при помощи обязательного флага --config или его синонима -c, как это ранее было описано в посте Парсинг флагов и аргументов в языке Go.

Пример файла конфигурации:

loglevel: debug listen: 0.0.0.0:8080 db: url: postgres://postgresql@localhost/restservice

Варианты запуска приложения:

$ ./rest Config file is not specified. Listen: localhost:8080 DB URL: postgres://localhost $ ./rest -c ./config.yaml Parsing config: ./config.yaml Listen: 0.0.0.0:8080 DB URL: postgres://restservice@localhost/restservice $ RESTEXAMPLE_LISTEN=localhost:80 ./rest -c ./config.yaml Parsing config: ./config.yaml Listen: localhost:80 DB URL: postgres://restservice@localhost/restservice $ RESTEXAMPLE_LISTEN=localhost:80 ./rest Config file is not specified. Listen: localhost:80 DB URL: postgres://localhost

В выводе опущены таймстемпы, так как они нам сейчас не интересны.

Первый вариант демонстрирует запуск без указания файла конфигурации и каких-либо переменных окружения. В этом случае используются параметры по умолчанию, указанные в коде. Если указать файл конфигурации, как во втором варианте, то параметры из файла перезатрут параметры по умолчанию. Наконец, третий и четвертый варианты запуска демонстрируют, что переменные окружения имеют приоритет как перед параметрами по умолчанию, так и перед параметрами из файла конфигурации.

Как видите, ничего сложного! Наиболее полную и актуальную информацию, как обычно, вы найдете в официальной документации.