Vipper日誌庫的學習

村望老弟發表於2021-11-02

1.簡單介紹

Vipper日誌庫的學習
安裝:

 go get github.com/spf13/viper

Viper 是 Go 應用程式的完整配置解決方案,它旨在在應用程式中工作,並且可以處理所有型別的配置需求和格式。

  • 設定預設值
  • 從 JSON、TOML、YAML、HCL、envfile 和 Java 屬性配置檔案中讀取
  • 實時觀看和重新讀取配置檔案(可選)
  • 從環境變數中讀取
  • 從遠端配置系統(etcd 或 Consul)讀取,並觀察變化
  • 從命令列標誌讀取
  • 從緩衝區讀取
  • 設定顯式值
    Viper 可以被認為是滿足您所有應用程式配置需求的登錄檔。

2.建立預設值

一個好的配置系統將支援預設值。鍵不需要預設值,但在未通過配置檔案、環境變數、遠端配置或標誌設定鍵的情況下它很有用。

//如果沒有從配置檔案讀到對應的配置,但是被使用了,就會去使用預設值
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})

3.讀取配置檔案

Viper需要最小的配置,所以它知道在哪裡尋找配置檔案。Viper支援JSON, TOML, YAML, HCL, INI, envfile和Java Properties檔案。Viper可以搜尋多個路徑,但目前一個Viper例項只支援一個配置檔案。Viper不預設任何配置搜尋路徑,而是將預設決定權留給一個應用程式。(他不會約定你的配置檔案應該放在哪裡,也不會規定你用什麼格式或者名稱來規定你的配置檔案,你可以自己決定,告訴它就好!)

viper.SetConfigName("config") // 配置檔案的名稱(不帶副檔名)
viper.SetConfigType("yaml") // 如果配置檔名稱中沒有副檔名,則需要配置這一項
viper.AddConfigPath("/etc/appname/") // 新增viper去查詢配置檔案的路徑(可以新增多個)
viper.AddConfigPath(".") // 可以選擇在工作目錄中尋找配置。
if err != nil { // 處理讀取配置檔案的錯誤
    panic(fmt.Errorf("Fatal error config file: %w \n", err))
}

您可以處理找不到配置檔案的特定情況,如下所示:

if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // 未找到配置檔案;如果需要,忽略錯誤
    } else {
        // 找到了配置檔案,但產生了另一個錯誤
    }
}
// 找到併成功解析了配置檔案

注意 [自 1.6 起]:您還可以擁有一個沒有副檔名的檔案並以程式設計方式指定格式。對於那些位於使用者家中沒有任何副檔名的配置檔案,如 .bashrc

4.編寫配置檔案

從配置檔案中讀取很有用,但有時您希望儲存在執行時所做的所有修改。為此,有一堆命令可用,每個命令都有自己的目的:

  • WriteConfig - 將當前 viper 配置寫入預定義路徑(如果存在)將覆蓋當前的配置檔案;如果沒有預定義路徑,則會出錯。
  • SafeWriteConfig - 將當前 viper 配置寫入預定義路徑。如果存在,則不會覆蓋當前的配置檔案。如果沒有預定義路徑,則會出錯。
  • WriteConfigAs - 將當前 viper 配置寫入給定的檔案路徑。將覆蓋給定的檔案(如果存在)。
  • SafeWriteConfigAs - 將當前 viper 配置寫入給定的檔案路徑。不會覆蓋給定的檔案(如果存在)。

5.檢視和重新讀取配置檔案

Viper 支援讓您的應用程式在執行時實時讀取配置檔案的能力。
需要重新啟動伺服器才能使配置生效的日子已經一去不復返了,viper 驅動的應用程式可以在執行時讀取配置檔案的更新,而不會錯過任何一個節拍。
只需告訴 viper 例項 watchConfig。或者,您可以為 Viper 提供一個函式,以便在每次發生更改時執行。

原配置檔案內容:

server:
  port: 5201

程式碼示例:

package main

import (
    "fmt"
    "github.com/fsnotify/fsnotify"
    "github.com/spf13/viper"
)

func main(){
    viper.SetConfigName("application") //設定配置檔名稱

    viper.SetConfigType("yaml") //設定配置副檔名

    viper.AddConfigPath(".") //設定配置檔案path

    err := viper.ReadInConfig() //讀配置檔案

    beforeGet := viper.Get("server.port")

    fmt.Println(beforeGet) // 5201
    viper.OnConfigChange(func(fs fsnotify.Event) { //配置檔案變更鉤子函式
        fmt.Println("Config file changed:", fs.Name)
    })

    viper.WatchConfig() //開啟監聽

    if err != nil{
        fmt.Printf("config file read err %d \n",err)
    }
    if writeErr:= viper.WriteConfig();writeErr!=nil{
        fmt.Printf("write err %d \n",writeErr)
    }
    viper.Set("server.port",11112) //通過set去修改配置

    afterGet := viper.Get("server.port")

    fmt.Println(afterGet) // 11112
}

輸出結果:觸發了watchConfig配置檔案修改的hooks鉤子函式

5201
Config file changed: /Users/codehope/remote-es-server-code/vipper-demo/application.yaml
11112

6.從io.Reader 讀取配置

package main

import (
    "bytes"
    "fmt"
    "github.com/spf13/viper"
    "os"
)

func main() {
    viper.SetConfigType("yaml") // 這個也需要加上的,不然讀取的是nil 
    fileBuf, err := os.ReadFile("./application.yaml")
    if err != nil{
        fmt.Println(err)
        return
    }
    fmt.Println(string(fileBuf))
    //fmt.Printf("%T",fileBuf)
    err1 := viper.ReadConfig(bytes.NewBuffer(fileBuf))
    if err1 != nil {
        return
    }
    getName := viper.Get("name")
    getServer := viper.Get("server")
    fmt.Println(getName) //app-demo
    fmt.Println(getServer) //map[port:5201]
}

【注意(version:viper@v1.9.0):從io.Reader讀取 的話也需要設定配置檔案的型別SetConfigType,不然有可能讀出的值為nil

Vipper日誌庫的學習

7.配置覆蓋

這些可能來自一個命令列標誌,或來自你自己的應用邏輯。

package main

import (
    "bytes"
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    viper.SetConfigType("yaml")
    ioReadConfig := []byte(
        `name: "app-demo"`)
    err := viper.ReadConfig(bytes.NewBuffer(ioReadConfig))
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(viper.Get("name")) //app-demo
    viper.Set("name","app-demo2")
    fmt.Println(viper.Get("name")) //app-demo2
}

8.註冊和使用別名

別名允許一個值被多個鍵所引用

package main

import (
    "bytes"
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    viper.SetConfigType("yaml") 
    ioReadConfig := []byte(
        `name: "app-demo"`)
    err := viper.ReadConfig(bytes.NewBuffer(ioReadConfig))
    if err != nil {
        fmt.Println(err)
        return
    }
    viper.RegisterAlias("t_name","name") //name配置起個別名叫t_name
    fmt.Println(viper.Get("t_name")) //獲取t_name的配置值,實際去拿的 name的值 -> app-demo
    viper.Set("t_name","app_new_name") //設定t_name的新值,實際去設定 name的值
    fmt.Println(viper.Get("name")) //驗證是否通過別名修改成功 -> app_new_name
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
CunWang@Ch

相關文章