Viper
Dirección del repositorio: spf13/viper: Go configuration with fangs (github.com)
Dirección de la documentación: spf13/viper: Go configuration with fangs (github.com)
TIP
Actualmente se está discutiendo la transición a Viper2, los interesados pueden consultar: Viper2
Instalación
go get github.com/spf13/viperIntroducción
Viper, es una solución completa de archivos de configuración para aplicaciones Go, puede manejar casi todos los tipos de necesidades y formatos de configuración, facilita la gestión de archivos de configuración del proyecto y tiene las siguientes características:
- Configuración de valores predeterminados
- Soporta formatos JSON, TOML, YAML, HCL, envfile, Java properties
- Soporta monitoreo en tiempo real y recarga de archivos de configuración
- Soporta lectura desde variables de entorno
- Soporta lectura desde sistemas de configuración remota y monitoreo de cambios
- Soporta lectura de marcadores de línea de comandos
- Soporta lectura desde búfer
- Soporta establecimiento explícito de valores
Los oficiales afirman que Viper puede satisfacer todas las necesidades de configuración de aplicaciones, los desarrolladores solo necesitan centrarse en construir aplicaciones y dejar que Viper se encargue de la gestión de configuración. Muchos proyectos conocidos usan Viper:
DANGER
Viper no es responsable del cifrado y descifrado de archivos de configuración, es decir, no realizará ningún procesamiento de seguridad en los archivos de configuración.
Orden de lectura
Viper usa la siguiente prioridad para leer configuración:
- Valores establecidos explícitamente
- Marcadores de línea de comandos
- Variables de entorno
- Archivos de configuración
- Almacenes de clave-valor
- Valores predeterminados
TIP
Las claves en la configuración de Viper no distinguen entre mayúsculas y minúsculas, se discutirá más adelante si esto será opcional.
Valores predeterminados
Un buen sistema de configuración debe soportar el establecimiento de valores predeterminados, aunque a veces no sea necesario, será muy útil cuando no se haya establecido un archivo de configuración. A continuación hay un ejemplo:
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")Leer archivos de configuración
Viper solo necesita muy poca configuración para saber dónde buscar los archivos de configuración. Viper soporta JSON, TOML, YAML, HCL, INI, envfile y archivos JavaProperties. Viper puede buscar en múltiples rutas simultáneamente, pero actualmente una sola instancia de Viper solo soporta un solo archivo de configuración. Viper no configura rutas de búsqueda por defecto, dejando la decisión predeterminada a la aplicación.
A continuación hay un ejemplo de uso de Viper para leer un archivo de configuración, no es necesario especificar una ruta completa, pero al menos se debe proporcionar un archivo de configuración al usarlo:
func TestReadConfigFile(t *testing.T) {
viper.SetConfigName("config.yml") // leer archivo de configuración llamado config, sin establecer una extensión de archivo específica
viper.SetConfigType("yaml") // cuando no se establece una extensión de archivo específica, es necesario especificar el tipo de archivo
viper.AddConfigPath("./") // buscar en el directorio actual
viper.AddConfigPath("$HOME/") // usar variable
viper.AddConfigPath(".") // buscar en el directorio de trabajo
err := viper.ReadInConfig() // leer configuración
if err != nil {
log.Fatalln(err)
}
}También se puede manejar por separado el caso en que no se encuentra el archivo de configuración:
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// archivo de configuración no encontrado
} else {
// otros tipos de errores
}
}A continuación están todas las funciones para acceder a la configuración:
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{}
Al acceder a configuraciones anidadas, se accede mediante el separador ., por ejemplo:
{
"server":{
"database":{
"url": "mysql...."
}
}
}Se puede acceder mediante GetString("server.database.url") para acceso anidado.
Escribir en archivos de configuración
Viper proporciona una serie de funciones para facilitar a los desarrolladores escribir la configuración almacenada en tiempo de ejecución en archivos de configuración.
// WriteConfig escribe la configuración en el archivo de configuración original, dará error si no existe, y lo sobrescribirá si existe
func WriteConfig() error { return v.WriteConfig() }
// SafeWriteConfig escribe de forma segura la configuración en el archivo de configuración original, escribirá si no existe, no sobrescribirá si existe
func SafeWriteConfig() error { return v.SafeWriteConfig() }
// WriteConfigAs escribe la configuración actual en un archivo especificado, devolverá un error si el archivo no existe, y sobrescribirá la configuración original si existe
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
// SafeWriteConfigAs si el archivo especificado existe, no sobrescribirá el archivo de configuración original, devolverá un error si el archivo existe
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }A continuación hay algunos ejemplos:
func TestWritingConfig(t *testing.T) {
viper.WriteConfig() // escribir configuración en el archivo de configuración original, estos archivos de configuración deben haber sido definidos previamente con 'viper.AddConfigPath()' y 'viper.SetConfigName'
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // porque el archivo especificado existe, devolverá un error
viper.SafeWriteConfigAs("/path/to/my/.other_config")
}Monitoreo y recarga de configuración
Viper permite a las aplicaciones leer dinámicamente un archivo de configuración en tiempo de ejecución, es decir, la configuración actualizada puede生效 sin necesidad de reiniciar la aplicación, y no pasará por alto cada detalle del cambio. Solo es necesario indicarle simplemente a la instancia de Viper que vigile los cambios de configuración, o se puede proporcionar una función a viper para que se ejecute cada vez que ocurra un cambio.
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("El archivo de configuración ha sido modificado:", e.Name)
})
viper.WatchConfig()
}Alias
func TestAliases(t *testing.T) {
viper.RegisterAlias("a", "b")
viper.Set("a", 1)
viper.Set("b", 2) // sobrescribirá la configuración de a
fmt.Println(viper.GetInt("a"))
}Extraer subestructuras
Anteriormente se mencionó acceder a configuraciones anidadas mediante el separador ., en realidad también se puede extraer una subestructura mediante la función viper.Sub(), cuyo valor de retorno es una instancia de Viper, como el siguiente ejemplo:
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // devuelve nil si no existe
panic("la configuración cache1 no existe")
}Establecer separador anidado
Cuando se desea que la clave especificada contenga ., es necesario especificar manualmente otro separador para evitar un análisis erróneo, por ejemplo:
viper.KeyDelimiter("/") // establecer el separador en /Deserialización
Viper proporciona dos funciones para deserializar la configuración en una estructura o map, también soporta estructuras anidadas:
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("no se puede deserializar a estructura, %v", err)
}Serialización
Serializa la configuración actual en una cadena en un formato específico para almacenarla en un archivo de configuración, normalmente soporta JSON, TOML, YAML, HCL, envfile, Java properties.
TIP
Viper también soporta formatos de serialización personalizados, 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("no se puede serializar la configuración a YAML: %v", err)
}
return string(bs)
}Múltiples instancias
Normalmente es suficiente usar la instancia global proporcionada por Viper, pero como una instancia solo puede mapear un archivo de configuración, se pueden crear múltiples instancias para realizar más operaciones, por ejemplo:
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...