Como lidar com a configuração no Go [fechado]

284

Eu sou novo na programação Go e me pergunto: qual é a maneira preferida de lidar com parâmetros de configuração para um programa Go (o tipo de coisa que se pode usar arquivos de propriedades ou arquivos ini para, em outros contextos)?

theglauber
fonte
Eu também iniciei um tópico de golang-nuts que tem algumas idéias adicionais.
theglauber
2
Eu costumo usar scripts de shell e variáveis ​​de ambiente.
rightfold 12/09/15
3
Dediquei uma postagem no blog Persistindo a configuração do aplicativo no Go, onde expliquei como fazê-lo com exemplos para os dois formatos mais populares: json e YAML. Os exemplos estão prontos para produção.
upitau
Apenas para constar, existe o HCL da HashiCorp, que suporta comentários e é compatível com JSON e UCL. github.com/hashicorp/hcl
Kaveh Shahbazian

Respostas:

244

O formato JSON funcionou muito bem para mim. A biblioteca padrão oferece métodos para escrever a estrutura de dados recuada, portanto é bastante legível.

Veja também este fio de nozes golang .

Os benefícios do JSON são que é bastante simples analisar e legível / editável por humanos, oferecendo semântica para listas e mapeamentos (que podem se tornar bastante úteis), o que não é o caso de muitos analisadores de configuração do tipo ini.

Exemplo de uso:

conf.json :

{
    "Users": ["UserA","UserB"],
    "Groups": ["GroupA"]
}

Programa para ler a configuração

import (
    "encoding/json"
    "os"
    "fmt"
)

type Configuration struct {
    Users    []string
    Groups   []string
}

file, _ := os.Open("conf.json")
defer file.Close()
decoder := json.NewDecoder(file)
configuration := Configuration{}
err := decoder.Decode(&configuration)
if err != nil {
  fmt.Println("error:", err)
}
fmt.Println(configuration.Users) // output: [UserA, UserB]
nemo
fonte
6
Parece que o JSON é o menos ruim das alternativas atuais. Eu olhei para go-yaml e é um esforço valioso, mas tomei a falta de documentação como uma indicação de que eu deveria procurar em outro lugar. O goini parece ser uma biblioteca simples e fácil de manipular arquivos ini do Windows . Um novo formato chamado TOML foi proposto, mas também tem problemas . Neste ponto, eu iria ficar com JSON ou ini .
theglauber
6
O YAML suporta comentários, se você quiser adicionar notas em qualquer lugar do arquivo de configuração.
Ivan Black
42
Para aqueles que leem e seguem esse caminho, cuidado: a falta de comentários dos JSON torna inadequado para um arquivo de configuração utilizável por humanos (imo). É um formato de intercâmbio de dados - você pode achar que perder a capacidade de escrever comentários úteis / descritivos em arquivos de configuração pode prejudicar a manutenção ("por que essa configuração está ativada?", "O que faz?", "Quais são os valores válidos para ela?" ? "etc).
Darian Moody
6
Ahhh - tentei isso no meu código e esqueci de definir os atributos struct com letras maiúsculas (não exportadas) - isso me custou uma hora da minha vida. Talvez outros cometam o mesmo erro> seja avisado; D
JohnGalt
6
Você provavelmente deveria, defer file.Close()depois de verificar o erro em aberto
Gabriel
97

Outra opção é usar o TOML , que é um formato semelhante ao INI criado por Tom Preston-Werner. Eu construído um analisador Vá para ele que é extensivamente testado . Você pode usá-lo como outras opções propostas aqui. Por exemplo, se você tiver esses dados TOML emsomething.toml

Age = 198
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z

Em seguida, você pode carregá-lo no seu programa Go com algo como

type Config struct {
    Age int
    Cats []string
    Pi float64
    Perfection []int
    DOB time.Time
}

var conf Config
if _, err := toml.DecodeFile("something.toml", &conf); err != nil {
    // handle error
}
BurntSushi5
fonte
18
Eu gosto do TOML porque ele permite escrever comentários em novas linhas ou no final de uma configuração de linha. Eu não posso fazer isso com JSON.
sergserg
Cada atualização de configuração requer atualização no código, o que é muito irritante.
Hywak 23/05
4
Toda abordagem para configuração faz. De que outra forma seu programa estaria ciente da nova configuração?
BurntSushi5
@ BurntSushi5 pode haver campos extras no arquivo Toml com os quais o código não se importa? Quero dizer, uma versão mais recente do arquivo de configuração pode ser usada com a versão mais antiga do código? Não há problema em meu caso ignorar opções de configuração não utilizadas.
precisa saber é o seguinte
2
eu gosto disso. Bom trabalho. Pessoalmente, acho que é mais fácil para administradores ou clientes alterar um arquivo TOML do que um JSON.
blndev
49

O Viper é um sistema de gerenciamento de configuração golang que funciona com JSON, YAML e TOML. Parece bem interessante.

Micah
fonte
1
Especialmente viável para aplicações 12factor 12factor.net
DerKnorr
Use o gonfig para a configuração JSON no Go. github.com/eduardbcom/gonfig
Eduard Bondarenko
1
Não use o Viper, não é um thread-safe que quase me demitiu.
igonejack
@igonejack Por favor, forneça um exemplo onde o Viper o mordeu?
precisa saber é
1
@ Dr.eel Tente separar viper.GetBool ("abc") e Viper.Set ("abc", false) em diferentes goroutines.
igonejack
44

Eu costumo usar JSON para estruturas de dados mais complicadas. A desvantagem é que você acaba facilmente com um monte de código para informar ao usuário onde estava o erro, vários casos extremos e quais não.

Para a configuração básica (chaves api, números de portas, ...), tive muita sorte com o pacote gcfg . É baseado no formato de configuração do git.

A partir da documentação:

Exemplo de configuração:

; Comment line
[section]
name = value # Another comment
flag # implicit value for bool is true

Vá struct:

type Config struct {
    Section struct {
            Name string
            Flag bool
    }
}

E o código necessário para lê-lo:

var cfg Config
err := gcfg.ReadFileInto(&cfg, "myconfig.gcfg")

Ele também suporta valores de fatia, para que você possa especificar uma chave várias vezes e outros recursos interessantes como esse.

Ask Bjørn Hansen
fonte
4
O autor original do gcfg interrompeu o projeto e iniciou outro sconf relacionado .
Iwat
39

Basta usar sinalizadores padrão com iniflags .

Os sinalizadores padrão go têm os seguintes benefícios:

  • Idiomatic.
  • Fácil de usar. Os sinalizadores podem ser facilmente adicionados e espalhados por pacotes arbitrários usados ​​pelo seu projeto.
  • Os sinalizadores têm suporte pronto para uso para valores e descrição padrão.
  • Os sinalizadores fornecem saída padrão de 'ajuda' com valores e descrição padrão.

O único problema de sinalização padrão dos sinalizadores - são os problemas de gerenciamento quando o número de sinalizadores usados ​​no seu aplicativo se torna muito grande.

O Iniflags resolve elegantemente esse problema: apenas modifique duas linhas no seu pacote principal e magicamente ganha suporte para a leitura de valores de flag do arquivo ini. Os sinalizadores dos arquivos ini podem ser substituídos passando novos valores na linha de comando.

Consulte também https://groups.google.com/forum/#!topic/golang-nuts/TByzyPgoAQE para obter detalhes.

valyala
fonte
Comecei a usar sinalizadores para um projeto em que estava trabalhando (meu primeiro projeto golang do zero), mas estou imaginando como lidar com coisas como testes? Por exemplo, este é um cliente da API e eu gostaria de usar sinalizadores, mas parece que complicaria demais meus testes ( go testnão me deixa passar sinalizadores) enquanto um arquivo de configuração não.
precisa saber é
definir sinalizadores de testes é fácil:*FlagName = value
Steven Soroka 28/05
9
seria muito útil se houvesse código de exemplo detalhado aqui mostrando um exemplo de trabalho :)
zero_cool
Não é uma boa ideia quando você precisa compartilhar a configuração com outras partes do aplicativo escritas em outros idiomas.
Kirzilla
sugeriria usar pflags em vez de sinalizadores. pflags está usando o posix-standard
Fjolnir Dvorak
12

Comecei a usar o Gcfg, que usa arquivos semelhantes ao Ini. É simples - se você quiser algo simples, é uma boa escolha.

Aqui está o código de carregamento que estou usando atualmente, que possui configurações padrão e permite sinalizadores de linha de comando (não mostrados) que substituem algumas das minhas configurações:

package util

import (
    "code.google.com/p/gcfg"
)

type Config struct {
    Port int
    Verbose bool
    AccessLog string
    ErrorLog string
    DbDriver string
    DbConnection string
    DbTblPrefix string
}

type configFile struct {
    Server Config
}

const defaultConfig = `
    [server]
    port = 8000
    verbose = false
    accessLog = -
    errorLog  = -
    dbDriver     = mysql
    dbConnection = testuser:TestPasswd9@/test
    dbTblPrefix  =
`

func LoadConfiguration(cfgFile string, port int, verbose bool) Config {
    var err error
    var cfg configFile

    if cfgFile != "" {
        err = gcfg.ReadFileInto(&cfg, cfgFile)
    } else {
        err = gcfg.ReadStringInto(&cfg, defaultConfig)
    }

    PanicOnError(err)

    if port != 0 {
        cfg.Server.Port = port
    }
    if verbose {
        cfg.Server.Verbose = true
    }

    return cfg.Server
}
Rick-777
fonte
2
Não é exatamente isso que Ask já mencionou?
Nemo
8

dê uma olhada no gonfig

// load
config, _ := gonfig.FromJson(myJsonFile)
// read with defaults
host, _ := config.GetString("service/host", "localhost")
port, _ := config.GetInt("service/port", 80)
test, _ := config.GetBool("service/testing", false)
rate, _ := config.GetFloat("service/rate", 0.0)
// parse section into target structure
config.GetAs("service/template", &template)
Christian Westman
fonte
Este é bom, desde que eu não precise redefinir toda a estrutura de configuração em go
thanhpk
5

Eu escrevi uma biblioteca simples de configuração ini em Golang.

https://github.com/c4pt0r/cfg

seguro para goroutine, fácil de usar

package cfg
import (
    "testing"
)

func TestCfg(t *testing.T) {
    c := NewCfg("test.ini")
    if err := c.Load() ; err != nil {
        t.Error(err)
    }
    c.WriteInt("hello", 42)
    c.WriteString("hello1", "World")

    v, err := c.ReadInt("hello", 0)
    if err != nil || v != 42 {
        t.Error(err)
    }

    v1, err := c.ReadString("hello1", "")
    if err != nil || v1 != "World" {
        t.Error(err)
    }

    if err := c.Save(); err != nil {
        t.Error(err)
    }
}

=================== Atualizar =======================

Recentemente, preciso de um analisador INI com suporte à seção e escrevo um pacote simples:

github.com/c4pt0r/cfg

você pode analisar INI como usar o pacote "flag":

package main

import (
    "log"
    "github.com/c4pt0r/ini"
)

var conf = ini.NewConf("test.ini")

var (
    v1 = conf.String("section1", "field1", "v1")
    v2 = conf.Int("section1", "field2", 0)
)

func main() {
    conf.Parse()

    log.Println(*v1, *v2)
}
c4pt0r
fonte
4

Você também pode estar interessado em go-libucl , um conjunto de ligações Go para UCL, a Linguagem de Configuração Universal. A UCL é um pouco como JSON, mas com melhor suporte para humanos: suporta comentários e construções legíveis por humanos, como multiplicadores de SI (10k, 40M, etc.) e possui um pouco menos de clichê (por exemplo, aspas em torno das teclas). Na verdade, é bem parecido com o formato do arquivo de configuração do nginx, se você já está familiarizado com isso.

trombonehero
fonte
2

Eu concordo com o nemo e escrevi uma pequena ferramenta para tornar tudo muito fácil.

bitbucket.org/gotamer/cfg é um pacote de configuração do json

  • Você define seus itens de configuração em seu aplicativo como uma estrutura.
  • Um modelo de arquivo de configuração json da sua estrutura é salvo na primeira execução
  • Você pode salvar modificações de tempo de execução na configuração

Veja doc.go para um exemplo

RoboTamer
fonte
1

Eu tentei JSON. Funcionou. Mas eu odeio ter que criar a estrutura dos campos e tipos exatos que posso estar definindo. Para mim isso foi uma dor. Percebi que era o método usado por todas as opções de configuração que pude encontrar. Talvez minha formação em linguagens dinâmicas me torne cego para os benefícios de tal verbosidade. Criei um novo formato de arquivo de configuração simples e uma biblioteca mais dinâmica para lê-lo.

https://github.com/chrisftw/ezconf

Sou muito novo no mundo Go, por isso pode não ser o caminho. Mas funciona, é bem rápido e super simples de usar.

Prós

  • Super simples
  • Menos código

Contras

  • Sem matrizes ou tipos de mapa
  • Formato de arquivo muito simples
  • Arquivos conf não padrão
  • Tem uma pequena convenção embutida, que agora eu desaprovo em geral na comunidade Go. (Procura pelo arquivo de configuração no diretório de configuração)
chrisftw
fonte