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/viperIntroduçã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:
- Definição explícita de valores
- Flags de linha de comando
- Variáveis de ambiente
- Arquivos de configuração
- Armazenamento chave-valor
- 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:
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.
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:
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) : 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{}
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.
// 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:
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.
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Arquivo de configuração alterado:", e.Name)
})
viper.WatchConfig()
}Aliases
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:
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := 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:
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{}) : 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("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)
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:
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...