Go Config is a pluggable dynamic config library
Most config in applications are statically configured or include complex logic to load from multiple sources. Go-config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.
- Dynamic config
- Pluggable sources
- Source merging
- Default values
- Config watcher
The following sources for config are supported
- consul - read from consul
- envvar - read from environment variables
- file - read from file
- flag - read from flags
- memory - read from memory
The interface is very simple. It supports multiple config sources, watching and default fallback values.
type Config interface {
Close() error
Bytes() []byte
Get(path ...string) Value
Load(source ...source.Source) error
Watch(path ...string) (Watcher, error)
}
A Source is the source of config.
It can be env vars, a file, a key value store. Anything which conforms to the Source interface.
// Source is the source from which config is loaded
type Source interface {
Read() (*ChangeSet, error)
Watch() (Watcher, error)
String() string
}
// ChangeSet represents a set of changes from a source
type ChangeSet struct {
Data []byte
Checksum string
Timestamp time.Time
Source string
}
Sources should return config in JSON format to operate with the default config reader
The Reader defaults to json but can be swapped out to any other format.
{
"path": {
"to": {
"key": ["foo", "bar"]
}
}
}
Assuming the following config file
{
"hosts": {
"database": {
"address": "10.0.0.1",
"port": 3306
},
"cache": {
"address": "10.0.0.2",
"port": 6379
}
}
}
import "github.com/micro/go-config/source/file"
// Create new config
conf := config.NewConfig()
// Load file source
conf.Load(file.NewSource(
file.WithPath("/tmp/config.json"),
))
type Host struct {
Address string `json:"address"`
Port int `json:"port"`
}
var host Host
conf.Get("hosts", "database").Scan(&host)
// 10.0.0.1 3306
fmt.Println(host.Address, host.Port)
// Get address. Set default to localhost as fallback
address := conf.Get("hosts", "database", "address").String("localhost")
// Get port. Set default to 3000 as fallback
port := conf.Get("hosts", "database", "port").Int(3000)
Watch a path for changes. When the file changes the new value will be made available.
w, err := conf.Watch("hosts", "database")
if err != nil {
// do something
}
// wait for next value
v, err := w.Next()
if err != nil {
// do something
}
var host Host
v.Scan(&host)
Multiple sources can be loaded and merged. Merging priority is in reverse order.
conf := config.NewConfig()
conf.Load(
// base config from env
envvar.NewSource(),
// override env with flags
flag.NewSource(),
// override flags with file
file.NewSource(
file.WithPath("/tmp/config.json"),
),
)
Viper and go-config are solving the same problem. Go-config provides a different interface and is part of the larger micro ecosystem of tooling.