Skip to content

Viper

Endereço do repositório: spf13/viper: Go configuration with fangs (github.com)

Endereço da documentação: spf13/viper: Go configuration with fangs (github.com)

TIP

A equipe está discutindo a transição para Viper2. Interessados podem saber mais: Viper2

Instalação

go get github.com/spf13/viper

Introdução

Viper é uma solução completa de arquivos de configuração para aplicações Go. Pode lidar com quase todos os tipos de necessidades e formatos de configuração, facilitando o gerenciamento de arquivos de configuração do projeto, e possui as seguintes características:

  • Definição de valores padrão
  • Suporta formatos JSON, TOML, YAML, HCL, envfile, Java properties
  • Suporta monitoramento em tempo real e recarregamento de arquivos de configuração
  • Suporta leitura de variáveis de ambiente
  • Suporta leitura do sistema de configuração remota e monitoramento de mudanças
  • Suporta leitura de flags de linha de comando
  • Suporta leitura de buffer
  • Suporta definição explícita de valores

Os desenvolvedores oficiais afirmam que Viper pode atender a todas as necessidades de configuração de aplicações. Os desenvolvedores só precisam se concentrar em construir a aplicação, deixando o Viper responsável pelo gerenciamento de configuração. Muitos projetos conhecidos usam Viper:

DANGER

Viper não é responsável por criptografia e descriptografia de arquivos de configuração, ou seja, não fará nenhum tratamento de segurança nos arquivos de configuração.

Ordem de Leitura

O Viper usa a seguinte prioridade para ler configurações:

  1. Definição explícita de valores
  2. Flags de linha de comando
  3. Variáveis de ambiente
  4. Arquivos de configuração
  5. Armazenamento chave-valor
  6. Valores padrão

TIP

As chaves nas configurações do Viper não diferenciam maiúsculas de minúsculas. Futuras discussões podem tornar isso opcional.

Valores Padrão

Um bom sistema de configuração deve suportar a definição de valores padrão. Embora nem sempre seja necessário, é muito útil quando nenhum arquivo de configuração está definido. Aqui está um exemplo:

go
viper.SetDefault("filePath", "./dir/img/usr")
viper.SetDefault("root", "123456")

Leitura de Arquivos de Configuração

O Viper precisa de pouca configuração para saber onde procurar arquivos de configuração. O Viper suporta JSON, TOML, YAML, HCL, INI, envfile e arquivos Java Properties. O Viper pode pesquisar em vários caminhos, mas atualmente uma única instância do Viper suporta apenas um arquivo de configuração. O Viper não configura caminhos de pesquisa por padrão, deixando a decisão padrão para a aplicação.

Abaixo está um exemplo de uso do Viper para ler um arquivo de configuração. Não é necessário especificar um caminho completo, mas pelo menos um arquivo de configuração deve ser fornecido durante o uso.

go
func TestReadConfigFile(t *testing.T) {
   viper.SetConfigName("config.yml") // Lê o arquivo de configuração chamado config, sem definir uma extensão específica
   viper.SetConfigType("yaml")       // Quando não há extensão específica, é necessário especificar o tipo de arquivo
   viper.AddConfigPath("./")         // Procura no diretório atual
   viper.AddConfigPath("$HOME/")     // Usa variável
   viper.AddConfigPath(".")          // Procura no diretório de trabalho
   err := viper.ReadInConfig() // Lê a configuração
   if err != nil {
      log.Fatalln(err)
   }
}

Também é possível lidar separadamente com o caso de arquivo de configuração não encontrado:

go
if err := viper.ReadInConfig(); err != nil {
  if _, ok := err.(viper.ConfigFileNotFoundError); ok {
    // Arquivo de configuração não encontrado
  } else {
    // Outros tipos de erro
  }
}

Abaixo estão todas as funções para acessar configurações:

  • 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{}

Ao acessar configurações aninhadas, use o separador . para acessar, por exemplo:

{
  "server":{
    "database":{
      "url": "mysql...."
    }
  }
}

Pode-se acessar aninhadamente usando GetString("server.database.url")

Gravação em Arquivos de Configuração

O Viper fornece uma série de funções para facilitar aos desenvolvedores a gravação da configuração armazenada em tempo de execução em arquivos de configuração.

go
// WriteConfig grava a configuração no arquivo de configuração original, retorna erro se não existir, sobrescreve se existir
func WriteConfig() error { return v.WriteConfig() }

// SafeWriteConfig grava com segurança a configuração no arquivo de configuração original, grava se não existir, não sobrescreve se existir
func SafeWriteConfig() error { return v.SafeWriteConfig() }

// WriteConfigAs grava a configuração atual em um arquivo especificado, retorna erro se o arquivo não existir, sobrescreve a configuração original se existir
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }

// SafeWriteConfigAs se o arquivo especificado existir, não sobrescreverá o arquivo de configuração original, retorna erro se o arquivo existir
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }

Abaixo estão alguns exemplos:

go
func TestWritingConfig(t *testing.T) {
   viper.WriteConfig() // Grava a configuração no arquivo de configuração original, estes arquivos de configuração devem ser definidos anteriormente por 'viper.AddConfigPath()' e 'viper.SetConfigName'
   viper.SafeWriteConfig()
   viper.WriteConfigAs("/path/to/my/.config")
   viper.SafeWriteConfigAs("/path/to/my/.config") // Retornará erro porque o arquivo especificado existe
   viper.SafeWriteConfigAs("/path/to/my/.other_config")
}

Monitoramento e Recarregamento de Configuração

O Viper permite que aplicações leiam dinamicamente um arquivo de configuração em tempo de execução, ou seja, as configurações atualizadas podem entrar em vigor sem precisar reiniciar a aplicação, sem perder nenhum detalhe de mudança. Basta simplesmente instruir a instância do Viper a monitorar mudanças na configuração, ou fornecer uma função ao viper para ser executada sempre que houver uma mudança.

go
func TestWatchingConfig(t *testing.T) {
  viper.OnConfigChange(func(e fsnotify.Event) {
    fmt.Println("Arquivo de configuração alterado:", e.Name)
  })
  viper.WatchConfig()
}

Aliases

go
func TestAliases(t *testing.T) {
   viper.RegisterAlias("a", "b")
   viper.Set("a", 1)
   viper.Set("b", 2) // Irá sobrescrever a configuração de a
   fmt.Println(viper.GetInt("a"))
}

Extração de Subestruturas

Foi mencionado anteriormente que se pode acessar configurações aninhadas através do separador .. Na verdade, também é possível extrair subestruturas usando a função viper.Sub(), que retorna uma instância do Viper, como no exemplo a seguir:

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 { // Retorna nil se não existir
  panic("Configuração cache1 não existe")
}

Definição de Separadores Aninhados

Quando se deseja que a chave especificada contenha ., é necessário especificar manualmente outro separador para evitar análise incorreta, por exemplo:

go
viper.KeyDelimiter("/") // Define o separador como /

Desserialização

O Viper fornece duas funções para desserializar a configuração em uma estrutura ou map, suportando também estruturas aninhadas:

  • 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("Não foi possível desserializar para estrutura, %v", err)
}

Serialização

Serializa a configuração atual em uma string em um formato específico para armazenar em um arquivo de configuração. Normalmente suporta JSON, TOML, YAML, HCL, envfile, Java properties.

TIP

O Viper também suporta formatos de serialização 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("Não foi possível serializar a configuração para YAML: %v", err)
  }
  return string(bs)
}

Múltiplas Instâncias

Normalmente, usar a instância global fornecida pelo Viper é suficiente. No entanto, como uma instância só pode mapear um arquivo de configuração, é possível criar múltiplas instâncias para realizar mais operações, por exemplo:

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

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

//...

Golang por www.golangdev.cn edit