I'm new at Go programming, and I'm wondering: what is the preferred way to handle configuration parameters for a Go program (the kind of stuff one might use properties files or ini files for, in other contexts)?
913 Answers
The JSON format worked for me quite well. The standard library offers methods to write the data structure indented, so it is quite readable.
See also this golang-nuts thread.
The benefits of JSON are that it is fairly simple to parse and human readable/editable while offering semantics for lists and mappings (which can become quite handy), which is not the case with many ini-type config parsers.
Example usage:
conf.json:
{ "Users": ["UserA","UserB"], "Groups": ["GroupA"] } Program to read the configuration
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] 14Another option is to use TOML, which is an INI-like format created by Tom Preston-Werner. I built a Go parser for it that is extensively tested. You can use it like other options proposed here. For example, if you have this TOML data in something.toml
Age = 198 Cats = [ "Cauchy", "Plato" ] Pi = 3.14 Perfection = [ 6, 28, 496, 8128 ] DOB = 1987-07-05T05:45:00Z Then you can load it into your Go program with something like
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 } 6Viper is a golang configuration management system that works with JSON, YAML, and TOML. It looks pretty interesting.
7I usually use JSON for more complicated data structures. The downside is that you easily end up with a bunch of code to tell the user where the error was, various edge cases and what not.
For base configuration (api keys, port numbers, ...) I've had very good luck with the gcfg package. It is based on the git config format.
From the documentation:
Sample config:
; Comment line [section] name = value # Another comment flag # implicit value for bool is true Go struct:
type Config struct { Section struct { Name string Flag bool } } And the code needed to read it:
var cfg Config err := gcfg.ReadFileInto(&cfg, "myconfig.gcfg") It also supports slice values, so you can allow specifying a key multiple times and other nice features like that.
1Just use standard go flags with iniflags.
go flags
import "flag" var nFlag = flag.Int("n", 1234, "help message for flag n")
iniflags
package main import ( "flag" ... "" ... ) var ( flag1 = flag.String("flag1", "default1", "Description1") ... flagN = flag.Int("flagN", 123, "DescriptionN") ) func main() { iniflags.Parse() // use instead of flag.Parse() }
Standard go flags have the following benefits:
- Idiomatic.
- Easy to use. Flags can be easily added and scattered across arbitrary packages your project uses.
- Flags have out-of-the-box support for default values and description.
- Flags provide standard 'help' output with default values and description.
The only drawback standard go flags have - is management problems when the number of flags used in your app becomes too large.
Iniflags elegantly solves this problem: just modify two lines in your main package and it magically gains support for reading flag values from ini file. Flags from ini files can be overriden by passing new values in command-line.
6I have started using Gcfg which uses Ini-like files. It's simple - if you want something simple, this is a good choice.
Here's the loading code I am using currently, which has default settings and allows command line flags (not shown) that override some of my config:
package util import ( "" ) 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 } 1have a look at 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) 1and are a pretty good libraries for configuration files.
1Use toml like this article Reading config files the Go way
I wrote a simple ini config library in golang.
goroutine-safe, easy to use
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) } } ===================Update=======================
Recently I need an INI parser with section support, and I write a simple package:
u can parse INI like using "flag" package:
package main import ( "log" "" ) 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) } You might also be interested in go-libucl, a set of Go bindings for UCL, the Universal Configuration Language. UCL is a bit like JSON, but with better support for humans: it supports comments and human-readable constructs like SI multipliers (10k, 40M, etc.) and has a little bit less boilerplate (e.g., quotes around keys). It's actually pretty close to the nginx configuration file format, if you're already familiar with that.
I agree with nemo and I wrote a little tool to make it all real easy.
is a json configuration package
- You define your config items in your application as a struct.
- A json config file template from your struct is saved on the first run
- You can save runtime modifications to the config
See doc.go for an example
I tried JSON. It worked. But I hate having to create the struct of the exact fields and types I might be setting. To me that was a pain. I noticed it was the method used by all the configuration options I could find. Maybe my background in dynamic languages makes me blind to the benefits of such verboseness. I made a new simple config file format, and a more dynamic-ish lib for reading it out.
I am pretty new to the Go world, so it might not be the Go way. But it works, it is pretty quick, and super simple to use.
Pros
- Super simple
- Less code
Cons
- No Arrays or Map types
- Very flat file format
- Non-standard conf files
- Does have a little convention built-in, which I now if frowned upon in general in Go community. (Looks for config file in the config directory)