mirror of
https://github.com/spf13/viper
synced 2024-12-22 11:37:02 +00:00
Add dynamic reading of config file support
This commit is contained in:
parent
c374c6d0a9
commit
e37b56e207
2 changed files with 75 additions and 4 deletions
29
README.md
29
README.md
|
@ -13,6 +13,7 @@ and formats. It supports:
|
|||
|
||||
* setting defaults
|
||||
* reading from JSON, TOML, and YAML config files
|
||||
* live watching and re-reading of config files (optional)
|
||||
* reading from environment variables
|
||||
* reading from remote config systems (Etcd or Consul), and watching changes
|
||||
* reading from command line flags
|
||||
|
@ -78,7 +79,7 @@ to an application.
|
|||
|
||||
Here is an example of how to use Viper to search for and read a configuration file.
|
||||
None of the specific paths are required, but at least one path should be provided
|
||||
where a configuration file is expected.
|
||||
where a configuration file is expected.
|
||||
|
||||
```go
|
||||
viper.SetConfigName("config") // name of config file (without extension)
|
||||
|
@ -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
|
||||
|
||||
Viper predefines many configuration sources such as files, environment
|
||||
|
@ -286,15 +307,15 @@ runtime_viper.Unmarshal(&runtime_conf)
|
|||
go func(){
|
||||
for {
|
||||
time.Sleep(time.Second * 5) // delay after each request
|
||||
|
||||
|
||||
// currently, only tested with etcd support
|
||||
err := runtime_viper.WatchRemoteConfig()
|
||||
if err != nil {
|
||||
log.Errorf("unable to read remote config: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// unmarshal new config into our runtime config struct. you can also use channel
|
||||
|
||||
// unmarshal new config into our runtime config struct. you can also use channel
|
||||
// to implement a signal to notify the system of the changes
|
||||
runtime_viper.Unmarshal(&runtime_conf)
|
||||
}
|
||||
|
|
50
viper.go
50
viper.go
|
@ -24,6 +24,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
@ -35,6 +36,7 @@ import (
|
|||
"github.com/spf13/cast"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/fsnotify.v1"
|
||||
)
|
||||
|
||||
var v *Viper
|
||||
|
@ -151,6 +153,8 @@ type Viper struct {
|
|||
env map[string]string
|
||||
aliases map[string]string
|
||||
typeByDefValue bool
|
||||
|
||||
onConfigChange func(fsnotify.Event)
|
||||
}
|
||||
|
||||
// Returns an initialized Viper instance.
|
||||
|
@ -219,6 +223,52 @@ var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties
|
|||
// Universally supported remote providers.
|
||||
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
|
||||
// Viper will use this and not check any of the config paths
|
||||
func SetConfigFile(in string) { v.SetConfigFile(in) }
|
||||
|
|
Loading…
Reference in a new issue