Skip to content

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/viper

Introducció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:

  1. Valores establecidos explícitamente
  2. Marcadores de línea de comandos
  3. Variables de entorno
  4. Archivos de configuración
  5. Almacenes de clave-valor
  6. 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:

go
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:

go
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:

go
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) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : 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.

go
// 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:

go
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.

go
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

go
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:

yaml
cache:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80
go
cache1Config := 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:

go
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{}) : error
  • UnmarshalKey(key string, rawVal interface{}) : error
go
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)

go
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:

go
x := viper.New()
y := viper.New()

x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")

//...

Golang editado por www.golangdev.cn