Viper
Indirizzo repository: spf13/viper: Go configuration with fangs (github.com)
Indirizzo documentazione: spf13/viper: Go configuration with fangs (github.com)
TIP
È in corso una discussione sulla transizione a Viper2, per maggiori informazioni: Viper2
Installazione
go get github.com/spf13/viperIntroduzione
Viper è una soluzione completa per i file di configurazione per applicazioni Go, può gestire quasi tutti i tipi di requisiti e formati di configurazione, facilitando la gestione dei file di configurazione del progetto e offrendo le seguenti caratteristiche:
- Impostazione dei valori predefiniti
- Supporto per formati JSON, TOML, YAML, HCL, envfile, Java properties
- Supporto per monitoraggio in tempo reale e ricarica dei file di configurazione
- Supporto per la lettura dalle variabili d'ambiente
- Supporto per la lettura dal sistema di configurazione remota e monitoraggio delle modifiche
- Supporto per la lettura da flag della riga di comando
- Supporto per la lettura da buffer
- Supporto per l'impostazione esplicita dei valori
Secondo l'ufficialità, Viper può soddisfare tutte le esigenze di configurazione delle applicazioni. Gli sviluppatori devono solo concentrarsi sulla costruzione dell'applicazione, lasciando a Viper la gestione della configurazione. Molti progetti famosi utilizzano Viper:
DANGER
Viper non si occupa della crittografia e decrittografia dei file di configurazione, ovvero non esegue alcun tipo di elaborazione di sicurezza sui file di configurazione.
Ordine di Lettura
Viper utilizza la seguente priorità per leggere la configurazione:
- Impostazione esplicita del valore
- Flag della riga di comando
- Variabili d'ambiente
- File di configurazione
- Archivi chiave-valore
- Valori predefiniti
TIP
Le chiavi nella configurazione di Viper non fanno distinzione tra maiuscole e minuscole. Questa potrebbe diventare un'opzione futura.
Valori Predefiniti
Un buon sistema di configurazione dovrebbe supportare l'impostazione di valori predefiniti. Anche se non sempre è necessario, sarà molto utile quando non è impostato un file di configurazione. Di seguito è riportato un esempio.
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")Lettura del File di Configurazione
Viper richiede pochissima configurazione per sapere dove cercare i file di configurazione. Viper supporta JSON, TOML, YAML, HCL, INI, envfile e file JavaProperties. Viper può cercare in più percorsi, ma attualmente una singola istanza Viper supporta solo un singolo file di configurazione. Viper non configura i percorsi di ricerca per impostazione predefinita, lasciando la decisione predefinita all'applicazione.
Di seguito è riportato un esempio di utilizzo di Viper per leggere un file di configurazione. Non è necessario specificare un percorso completo, ma durante l'uso è necessario fornire almeno un file di configurazione.
func TestReadConfigFile(t *testing.T) {
viper.SetConfigName("config.yml") // Legge un file di configurazione chiamato config, senza impostare un'estensione specifica
viper.SetConfigType("yaml") // Quando non è impostata un'estensione specifica, è necessario specificare il tipo di file
viper.AddConfigPath("./") // Cerca nella cartella corrente
viper.AddConfigPath("$HOME/") // Utilizza una variabile
viper.AddConfigPath(".") // Cerca nella directory di lavoro
err := viper.ReadInConfig() // Legge la configurazione
if err != nil {
log.Fatalln(err)
}
}È anche possibile gestire separatamente il caso in cui il file di configurazione non viene trovato
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// File di configurazione non trovato
} else {
// Altri tipi di errori
}
}Di seguito sono riportate tutte le funzioni per accedere alla configurazione:
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{}
Quando si accede a configurazioni annidate, si utilizza il separatore ., ad esempio:
{
"server":{
"database":{
"url": "mysql...."
}
}
}È possibile accedere tramite GetString("server.database.url")
Scrittura del File di Configurazione
Viper offre una serie di funzioni per consentire agli sviluppatori di scrivere la configurazione memorizzata durante l'esecuzione nel file di configurazione.
// WriteConfig scrive la configurazione nel file di configurazione originale, restituisce un errore se non esiste, altrimenti sovrascrive
func WriteConfig() error { return v.WriteConfig() }
// SafeWriteConfig scrive in modo sicuro la configurazione nel file di configurazione originale, scrive se non esiste, non sovrascrive se esiste
func SafeWriteConfig() error { return v.SafeWriteConfig() }
// WriteConfigAs scrive la configurazione corrente in un file specificato, restituisce un errore se il file non esiste, altrimenti sovrascrive la configurazione originale
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
// SafeWriteConfigAs se il file specificato esiste, non sovrascriverà il file di configurazione originale, restituisce un errore se il file esiste
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }Di seguito sono riportati alcuni esempi:
func TestWritingConfig(t *testing.T) {
viper.WriteConfig() // Scrive la configurazione nel file di configurazione originale, questi file di configurazione dovrebbero essere definiti in anticipo con 'viper.AddConfigPath()' e 'viper.SetConfigName'
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // Poiché il file specificato esiste, restituirà un errore
viper.SafeWriteConfigAs("/path/to/my/.other_config")
}Monitoraggio e Ricarica della Configurazione
Viper consente alle applicazioni di leggere dinamicamente un file di configurazione durante l'esecuzione, ovvero è possibile rendere effettive le configurazioni aggiornate senza riavviare l'applicazione, senza perdere alcun dettaglio di modifica. È sufficiente dire semplicemente all'istanza Viper di monitorare le modifiche alla configurazione, oppure è possibile fornire una funzione a viper da eseguire ogni volta che si verifica una modifica.
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("File di configurazione modificato:", e.Name)
})
viper.WatchConfig()
}Alias
func TestAliases(t *testing.T) {
viper.RegisterAlias("a", "b")
viper.Set("a", 1)
viper.Set("b", 2) // sovrascriverà la configurazione di a
fmt.Println(viper.GetInt("a"))
}Estrazione di Sottostrutture
Come menzionato in precedenza, è possibile accedere alle configurazioni annidate tramite il separatore ., ma è anche possibile estrarre una sottostruttura tramite la funzione viper.Sub(), che restituisce un'istanza Viper, come nell'esempio seguente:
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // Restituisce nil se non esiste
panic("La configurazione cache1 non esiste")
}Impostazione del Separatore Annidato
Quando si desidera che la chiave specificata contenga ., è necessario specificare manualmente un altro separatore per evitare un'analisi errata, ad esempio:
viper.KeyDelimiter("/") // Imposta il separatore su /Deserializzazione
Viper fornisce due funzioni per deserializzare la configurazione in una struttura o mappa, supportando anche strutture annidate:
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("Impossibile deserializzare in struttura, %v", err)
}Serializzazione
Serializza la configurazione corrente in una stringa in un formato specifico per memorizzarla nel file di configurazione. Normalmente supporta JSON, TOML, YAML, HCL, envfile, Java properties.
TIP
Viper supportra anche formati di serializzazione personalizzati, 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("Impossibile serializzare la configurazione in YAML: %v", err)
}
return string(bs)
}Multiple Istanze
Normalmente l'istanza globale fornita da Viper è sufficiente per l'uso, ma poiché un'istanza può mappare solo un file di configurazione, è possibile creare più istanze per eseguire più operazioni, ad esempio:
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...