Add dynamic reading of config file support

This commit is contained in:
spf13 2015-11-09 23:22:04 -05:00
parent c374c6d0a9
commit e37b56e207
2 changed files with 75 additions and 4 deletions

View file

@ -13,6 +13,7 @@ and formats. It supports:
* setting defaults * setting defaults
* reading from JSON, TOML, and YAML config files * reading from JSON, TOML, and YAML config files
* live watching and re-reading of config files (optional)
* reading from environment variables * reading from environment variables
* reading from remote config systems (Etcd or Consul), and watching changes * reading from remote config systems (Etcd or Consul), and watching changes
* reading from command line flags * reading from command line flags
@ -91,6 +92,26 @@ if err != nil { // Handle errors reading the config file
} }
``` ```
### Watching and re-reading config files
Viper supports the ability to have your application live read a config file while running.
Gone are the days of needing to restart a server to have a config take effect,
viper powered applications can read an update to a config file while running and
not miss a beat.
Simply tell the viper instance to watchConfig.
Optionally you can provide a function for Viper to run each time a change occurs.
**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
```go
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
```
### Reading Config from io.Reader ### Reading Config from io.Reader
Viper predefines many configuration sources such as files, environment Viper predefines many configuration sources such as files, environment

View file

@ -24,6 +24,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -35,6 +36,7 @@ import (
"github.com/spf13/cast" "github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"gopkg.in/fsnotify.v1"
) )
var v *Viper var v *Viper
@ -151,6 +153,8 @@ type Viper struct {
env map[string]string env map[string]string
aliases map[string]string aliases map[string]string
typeByDefValue bool typeByDefValue bool
onConfigChange func(fsnotify.Event)
} }
// Returns an initialized Viper instance. // Returns an initialized Viper instance.
@ -219,6 +223,52 @@ var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties
// Universally supported remote providers. // Universally supported remote providers.
var SupportedRemoteProviders []string = []string{"etcd", "consul"} var SupportedRemoteProviders []string = []string{"etcd", "consul"}
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
v.onConfigChange = run
}
func WatchConfig() { v.WatchConfig() }
func (v *Viper) WatchConfig() {
go func() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
err := v.ReadInConfig()
if err != nil {
log.Println("error:", err)
}
v.onConfigChange(event)
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}()
if v.configFile != "" {
watcher.Add(v.configFile)
} else {
for _, x := range v.configPaths {
err = watcher.Add(x)
if err != nil {
log.Fatal(err)
}
}
}
<-done
}()
}
// Explicitly define the path, name and extension of the config file // Explicitly define the path, name and extension of the config file
// Viper will use this and not check any of the config paths // Viper will use this and not check any of the config paths
func SetConfigFile(in string) { v.SetConfigFile(in) } func SetConfigFile(in string) { v.SetConfigFile(in) }