mirror of
https://github.com/spf13/viper
synced 2024-12-22 19:47:01 +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
|
* 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
|
||||||
|
@ -78,7 +79,7 @@ to an application.
|
||||||
|
|
||||||
Here is an example of how to use Viper to search for and read a configuration file.
|
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
|
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
|
```go
|
||||||
viper.SetConfigName("config") // name of config file (without extension)
|
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
|
### Reading Config from io.Reader
|
||||||
|
|
||||||
Viper predefines many configuration sources such as files, environment
|
Viper predefines many configuration sources such as files, environment
|
||||||
|
@ -286,15 +307,15 @@ runtime_viper.Unmarshal(&runtime_conf)
|
||||||
go func(){
|
go func(){
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Second * 5) // delay after each request
|
time.Sleep(time.Second * 5) // delay after each request
|
||||||
|
|
||||||
// currently, only tested with etcd support
|
// currently, only tested with etcd support
|
||||||
err := runtime_viper.WatchRemoteConfig()
|
err := runtime_viper.WatchRemoteConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to read remote config: %v", err)
|
log.Errorf("unable to read remote config: %v", err)
|
||||||
continue
|
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
|
// to implement a signal to notify the system of the changes
|
||||||
runtime_viper.Unmarshal(&runtime_conf)
|
runtime_viper.Unmarshal(&runtime_conf)
|
||||||
}
|
}
|
||||||
|
|
50
viper.go
50
viper.go
|
@ -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) }
|
||||||
|
|
Loading…
Reference in a new issue