Viper
リポジトリ:spf13/viper: Go configuration with fangs (github.com)
ドキュメント:spf13/viper: Go configuration with fangs (github.com)
TIP
公式では Viper2 への移行について議論中です。興味のある方はこちらをご覧ください:Viper2
インストール
go get github.com/spf13/viper紹介
Viper は、Go アプリケーション向けの完全な設定ファイルソリューションです。ほぼすべてのタイプの設定ニーズとフォーマットを処理でき、プロジェクトの設定ファイルを簡単に管理できます。以下の特徴があります:
- デフォルト値の設定
- JSON、TOML、YAML、HCL、envfile、Java properties 形式のサポート
- 設定ファイルのリアルタイム監視とリロード
- 環境変数からの読み取り
- リモート設定システムからの設定読み取りと変更監視
- コマンドラインフラグの読み取り
- バッファからの読み取り
- 明示的な値の設定
公式によると、Viper はすべてのアプリケーション設定ニーズに対応でき、開発者はアプリケーションの構築に集中し、設定管理は Viper に任せることができます。多くの有名なプロジェクトで Viper が使用されています:
DANGER
Viper は設定ファイルの暗号化と復号化を担当しません。つまり、設定ファイルに対するセキュリティ処理は一切行いません。
読み込み順序
Viper は以下の優先順位で設定を読み込みます:
- 明示的に設定された値
- コマンドラインフラグ
- 環境変数
- 設定ファイル
- キーストア
- デフォルト値
TIP
Viper 設定のキーは大文字小文字を区別しません。今後の議論でオプションになる可能性があります。
デフォルト値
優れた設定システムはデフォルト値の設定をサポートすべきです。常に必要とは限りませんが、設定ファイルが設定されていない場合に非常に役立ちます。以下に例を示します。
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")設定ファイルの読み込み
Viper は最小限の設定で、設定ファイルの場所を把握できます。Viper は JSON、TOML、YAML、HCL、INI、envfile、JavaProperties ファイルをサポートしています。Viper は複数のパスを同時に検索できますが、現在のところ単一の Viper インスタンスは単一の設定ファイルのみをサポートしています。Viper はデフォルトで検索パスを設定しません。デフォルトの決定はアプリケーションに委ねられます。
以下は Viper を使用して設定ファイルを読み込む例です。完全なパスを指定する必要はありませんが、使用する際には少なくとも 1 つの設定ファイルを提供する必要があります。
func TestReadConfigFile(t *testing.T) {
viper.SetConfigName("config.yml") // config という名前の設定ファイルを読み込む、特定のファイル拡張子は設定されていない
viper.SetConfigType("yaml") // 特定のファイル拡張子が設定されていない場合、ファイルタイプを指定する必要がある
viper.AddConfigPath("./") // 現在のフォルダで検索
viper.AddConfigPath("$HOME/") // 変数を使用
viper.AddConfigPath(".") // ワーキングディレクトリで検索
err := viper.ReadInConfig() // 設定を読み込む
if err != nil {
log.Fatalln(err)
}
}設定ファイルが見つからない場合を個別に処理することもできます:
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// 設定ファイルが見つからない
} else {
// その他のタイプのエラー
}
}以下は設定にアクセスするためのすべての関数です:
Get(key string) : interface{}GetBool(key string) : boolGetFloat64(key string) : float64GetInt(key string) : intGetIntSlice(key string) : []intGetString(key string) : stringGetStringMap(key string) : map[string]interface{}GetStringMapString(key string) : map[string]stringGetStringSlice(key string) : []stringGetTime(key string) : time.TimeGetDuration(key string) : time.DurationIsSet(key string) : boolAllSettings() : map[string]interface{}
ネストされた設定にアクセスする場合は、. 区切り文字を使用してアクセスします。例:
{
"server":{
"database":{
"url": "mysql...."
}
}
}GetString("server.database.url") を使用してネストされたアクセスを行うことができます。
設定ファイルへの書き込み
Viper は、ランタイムに保存された設定を設定ファイルに書き込むための一連の関数を提供しています。
// WriteConfig 設定を元の設定ファイルに書き込みます。存在しない場合はエラーになり、存在する場合は上書きします
func WriteConfig() error { return v.WriteConfig() }
// SafeWriteConfig 設定を元の設定ファイルに安全に書き込みます。存在しない場合は書き込み、存在する場合は上書きしません
func SafeWriteConfig() error { return v.SafeWriteConfig() }
// WriteConfigAs 現在の設定を指定されたファイルに書き込みます。ファイルが存在しない場合はエラーを返し、存在する場合は元の設定を上書きします
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
// SafeWriteConfigAs 指定されたファイルが存在する場合は、元の設定ファイルを上書きしません。ファイルが存在する場合はエラーを返します
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }以下にいくつかの例を示します:
func TestWritingConfig(t *testing.T) {
viper.WriteConfig() // 設定を元の設定ファイルに書き込みます。これらの設定ファイルは事前に 'viper.AddConfigPath()' と 'viper.SetConfigName' で定義されている必要があります
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // 指定されたファイルが存在するため、エラーが返されます
viper.SafeWriteConfigAs("/path/to/my/.other_config")
}監視とリロード
Viper を使用すると、アプリケーションの実行中に設定ファイルを動的に読み込むことができます。つまり、アプリケーションを再起動せずに更新された設定を有効にでき、すべての変更を見逃しません。Viper インスタンスに設定の変更を監視するように指示するだけで、または変更が発生するたびに関数を実行するために viper に関数を提供できます。
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("設定ファイルが変更されました:", e.Name)
})
viper.WatchConfig()
}エイリアス
func TestAliases(t *testing.T) {
viper.RegisterAlias("a", "b")
viper.Set("a", 1)
viper.Set("b", 2) // a の設定を上書きします
fmt.Println(viper.GetInt("a"))
}サブ構造の抽出
前述したように、. 区切り文字を使用してネストされた設定にアクセスできますが、viper.Sub() 関数を使用してサブ構造を抽出することもできます。戻り値は Viper インスタンスです。以下に例を示します:
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // 存在しない場合は nil を返します
panic("cache1 設定が存在しません")
}ネスト区切り文字の設定
指定されたキーに . が含まれている場合は、誤解析を防ぐために別の区切り文字を手動で指定する必要があります。例:
viper.KeyDelimiter("/") // 区切り文字を / に設定逆シリアライズ
Viper は、設定を構造体または map に逆シリアライズするための 2 つの関数を提供しています。ネスト構造もサポートしています:
Unmarshal(rawVal interface{}) : errorUnmarshalKey(key string, rawVal interface{}) : error
type config struct {
Port int
Name string
PathMap string `mapstructure:"path_map"`
}
var C config
err := viper.Unmarshal(&C)
if err != nil {
t.Fatalf("構造体に逆シリアライズできません,%v", err)
}シリアライズ
現在の設定を特定の形式で文字列にシリアライズして、設定ファイルに保存します。通常、JSON、TOML、YAML、HCL、envfile、Java properties をサポートしています。
TIP
Viper はカスタムシリアライズ形式もサポートしています。Decoding custom formats with Viper - Márk Sági-Kazár (sagikazarmark.hu)
import (
yaml "gopkg.in/yaml.v2"
// ...
)
func yamlStringSettings() string {
c := viper.AllSettings()
bs, err := yaml.Marshal(c)
if err != nil {
log.Fatalf("設定を YAML にシリアライズできません:%v", err)
}
return string(bs)
}複数のインスタンス
通常、Viper が提供するグローバルインスタンスで十分ですが、1 つのインスタンスは 1 つの設定ファイルのみをマップできるため、より多くの操作を実現するために複数のインスタンスを自分で作成できます。例:
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...