Viper
Adresse du dépôt : spf13/viper: Go configuration with fangs (github.com)
Adresse de la documentation : spf13/viper: Go configuration with fangs (github.com)
TIP
L'équipe officielle discute actuellement d'une transition vers Viper2, pour en savoir plus : Viper2
Installation
go get github.com/spf13/viperIntroduction
Viper est une solution complète de fichiers de configuration pour les applications Go, capable de gérer presque tous les types de besoins et formats de configuration, facilitant la gestion des fichiers de configuration de projet. Il possède les caractéristiques suivantes :
- Définition de valeurs par défaut
- Support des formats JSON, TOML, YAML, HCL, envfile, Java properties
- Support de la surveillance en temps réel et du rechargement des fichiers de configuration
- Support de la lecture depuis les variables d'environnement
- Support de la lecture de configuration depuis des systèmes de configuration distants et surveillance des changements
- Support de la lecture des indicateurs de ligne de commande
- Support de la lecture depuis un tampon
- Support de la définition explicite de valeurs
L'équipe officielle affirme que Viper peut répondre à tous les besoins de configuration d'applications. Les développeurs peuvent se concentrer sur la construction de l'application, laissant Viper gérer la configuration. De nombreux projets connus utilisent Viper
DANGER
Viper ne gère pas le chiffrement et le déchiffrement des fichiers de configuration, c'est-à-dire qu'il n'effectue aucun traitement de sécurité sur les fichiers de configuration.
Ordre de lecture
Viper utilise l'ordre de priorité suivant pour lire la configuration :
- Définition explicite de valeurs
- Indicateurs de ligne de commande
- Variables d'environnement
- Fichier de configuration
- Stockage clé-valeur
- Valeurs par défaut
TIP
Les clés dans la configuration Viper ne sont pas sensibles à la casse, les discussions futures pourraient rendre cela optionnel.
Valeurs par défaut
Un bon système de configuration devrait supporter la définition de valeurs par défaut. Bien que cela ne soit pas toujours nécessaire, cela s'avère très utile lorsqu'aucun fichier de configuration n'est défini. Voici un exemple :
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")Lecture des fichiers de configuration
Viper nécessite très peu de configuration pour savoir où chercher les fichiers de configuration. Viper supporte les fichiers JSON, TOML, YAML, HCL, INI, envfile et JavaProperties. Viper peut rechercher dans plusieurs chemins simultanément, mais une seule instance de Viper ne supporte actuellement qu'un seul fichier de configuration. Viper ne définit pas de chemin de recherche par défaut, laissant ce choix à l'application.
Voici un exemple d'utilisation de Viper pour lire un fichier de configuration. Il n'est pas nécessaire de spécifier un chemin complet, mais au moins un fichier de configuration doit être fourni.
func TestReadConfigFile(t *testing.T) {
viper.SetConfigName("config.yml") // Lit le fichier de configuration nommé config, sans extension spécifique
viper.SetConfigType("yaml") // Lorsque l'extension n'est pas spécifiée, le type de fichier doit être précisé
viper.AddConfigPath("./") // Recherche dans le dossier courant
viper.AddConfigPath("$HOME/") // Utilise une variable
viper.AddConfigPath(".") // Recherche dans le répertoire de travail
err := viper.ReadInConfig() // Lit la configuration
if err != nil {
log.Fatalln(err)
}
}Il est également possible de traiter séparément le cas où le fichier de configuration n'est pas trouvé
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Fichier de configuration non trouvé
} else {
// Autre type d'erreur
}
}Voici toutes les fonctions pour accéder à la configuration
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{}
Pour accéder aux configurations imbriquées, utilisez le séparateur ., par exemple :
{
"server":{
"database":{
"url": "mysql...."
}
}
}Vous pouvez utiliser GetString("server.database.url") pour un accès imbriqué
Écriture des fichiers de configuration
Viper fournit une série de fonctions pour faciliter l'écriture de la configuration stockée en runtime dans des fichiers de configuration.
// WriteConfig écrit la configuration dans le fichier de configuration original, retourne une erreur si inexistant, écrase s'il existe
func WriteConfig() error { return v.WriteConfig() }
// SafeWriteConfig écrit la configuration de manière sécurisée dans le fichier original, crée si inexistant, n'écrase pas s'il existe
func SafeWriteConfig() error { return v.SafeWriteConfig() }
// WriteConfigAs écrit la configuration actuelle dans le fichier spécifié, retourne une erreur si le fichier n'existe pas, écrase s'il existe
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
// SafeWriteConfigAs si le fichier spécifié existe, n'écrasera pas le fichier de configuration original, retourne une erreur s'il existe
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }Voici quelques exemples :
func TestWritingConfig(t *testing.T) {
viper.WriteConfig() // Écrit la configuration dans le fichier original, ces fichiers doivent être définis au préalable par 'viper.AddConfigPath()' et 'viper.SetConfigName'
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // Retourne une erreur car le fichier spécifié existe
viper.SafeWriteConfigAs("/path/to/my/.other_config")
}Surveillance et rechargement de configuration
Viper permet aux applications de lire dynamiquement un fichier de configuration en runtime, c'est-à-dire sans redémarrer l'application pour que la configuration mise à jour prenne effet, sans manquer aucun détail de changement. Il suffit de dire à l'instance Viper de surveiller les changements de configuration, ou vous pouvez fournir une fonction à viper pour qu'elle s'exécute à chaque changement.
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Le fichier de configuration a été modifié:", e.Name)
})
viper.WatchConfig()
}Alias
func TestAliases(t *testing.T) {
viper.RegisterAlias("a", "b")
viper.Set("a", 1)
viper.Set("b", 2) // Écrasera la configuration de a
fmt.Println(viper.GetInt("a"))
}Extraction de sous-structures
Nous avons mentionné précédemment l'accès aux configurations imbriquées via le séparateur .. Vous pouvez également utiliser la fonction viper.Sub() pour extraire une sous-structure. La valeur de retour est une instance Viper, comme dans l'exemple suivant :
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // Retourne nil si inexistant
panic("La configuration cache1 n'existe pas")
}Définition du séparateur d'imbrication
Lorsque vous souhaitez spécifier une clé contenant un ., vous devez manuellement spécifier un autre séparateur pour éviter les erreurs d'analyse, par exemple :
viper.KeyDelimiter("/") // Définit le séparateur sur /Désérialisation
Viper fournit deux fonctions pour désérialiser la configuration dans une structure ou une map, supportant également les structures imbriquées :
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("Impossible de désérialiser en structure, %v", err)
}Sérialisation
Sérialise la configuration actuelle dans une chaîne selon un format spécifique pour l'enregistrer dans un fichier de configuration. Supporte généralement JSON, TOML, YAML, HCL, envfile, Java properties.
TIP
Viper supporte également les formats de sérialisation personnalisés, 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("Impossible de sérialiser la configuration en YAML: %v", err)
}
return string(bs)
}Instances multiples
En général, l'instance globale fournie par Viper est suffisante, mais comme une instance ne peut mapper qu'un seul fichier de configuration, vous pouvez créer plusieurs instances pour effectuer plus d'opérations, par exemple :
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...