Golang 環境變數與設定檔: viper

Golang 環境變數與設定檔: viper

2022, Apr 25    

程式部屬時常因執行環境的不同,而需要套用不同的設定,大多數情況我們是透過環境變數來設定,像是 Spring boot 內建讀取 application.yml,或是 Node.js 的 dotenv,這次介紹一個 Golang 的工具 viper,就是類似於前兩者的工具,提供各種環境變數與設定的操作,請看下面的範例

Viper

官方連結

  • 支援 json, toml, yaml, hcl, envfile, properties 格式
  • 支援讀取環境變數
  • 支援遠端讀取設定
  • 支援熱讀取,可以即時更新設定

安裝

go get github.com/spf13/viper

使用

範例設定檔

# _assets/env.yaml
name: go-demo
mode: dev
logger: DEBUG
version: "1.0.0"
server:
  port: 8888
# _assets/env.prod.yaml
mode: prod

這裡準備兩份檔案,模擬實際使用情況,可能需要不同的設定

範例 code

package config

var Env *viper.Viper

func init() {
    Env = initViper()
}

func initViper() *viper.Viper {
    v := viper.New()

    // 設定檔的檔名、格式、路徑
    v.SetConfigName("env")
    v.SetConfigType("yaml")
    v.AddConfigPath("_assets/")

    // 執行讀取設定
    err := v.ReadInConfig()
    if err != nil {
        fmt.Println("[Error] Loading config failed: ", err)
        panic(err)
    }

    // 讀取環境變數時以 _ 為分隔符
    v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    // 設定讀取環境變數時的前綴字
    v.SetEnvPrefix("demo")
    // 讀取環境變數
    v.AutomaticEnv()

    //  Print 實際讀取到的設定
    fmt.Printf("%+v\n",v.AllSettings())
    return v
}

短短幾行 code 包含了讀取檔案以及自動套入環境變數,並且輸出設定的內容,這樣就可以看到設定的內容

如果需要用環境變數套用,則設定的前綴字以及分隔符號,以這邊為例: DEMO_MODE

使用時,可以直接使用 viper.Get 的系列方法,例如:

    fmt.Printf("\n============ Start [%s] version:%s on:%s ============\n",
        config.Env.GetString("name"),
        config.Env.GetString("version"),
        config.Env.GetString("server.port"))

多層結構使用上用 . 隔開,提供各種 Get 的方法,不用額外進行轉換

而如果需要讀取多份設定檔,則可以使用 viper.MergeInConfig 方法,我們改寫上面程式

const envPath = "_assets/"
const envType = "yaml"
const envPrefix = "demo"

func initViper() *viper.Viper {
    v := viper.New()
    v.SetConfigName("env")
    v.SetConfigType(envType)
    v.AddConfigPath(envPath)
    err := v.ReadInConfig()
    if err != nil {
        fmt.Println("[Error] Loading config failed: ", err)
        panic(err)
    }

    // 執行時讀取參數套用不同的設定檔
    if len(os.Args) >= 2 {
        v.SetConfigName("env."+os.Args[1])
         v.SetConfigType(envType)
        v.AddConfigPath(envPath)
        // 合併設定檔
        err := v.MergeInConfig()
        if err != nil {
            fmt.Println("[Error] Merge config failed: ", err)
            panic(err)
        }
    }

    v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
    v.SetEnvPrefix(envPrefix)
    v.AutomaticEnv()

    fmt.Printf("%+v\n",v.AllSettings())
    return v
}

這邊利用執行程式時的參數,來讀取不同的設定檔,並且合併設定檔,這樣就可以看到不同的設定檔的內容,並且會以參數的設定檔為主去取代預設設定檔的內容

執行時如下:

go run main.go prod