9 [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) |
9 [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) |
10 |
10 |
11 [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) |
11 [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) |
12 [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) |
12 [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) |
13 [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) |
13 [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) |
14 ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.14-61CFDD.svg?style=flat-square) |
14 ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg?style=flat-square) |
15 [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) |
15 [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) |
16 |
16 |
17 **Go configuration with fangs!** |
17 **Go configuration with fangs!** |
18 |
18 |
19 Many Go projects are built using Viper including: |
19 Many Go projects are built using Viper including: |
125 |
125 |
126 You can handle the specific case where no config file is found like this: |
126 You can handle the specific case where no config file is found like this: |
127 |
127 |
128 ```go |
128 ```go |
129 if err := viper.ReadInConfig(); err != nil { |
129 if err := viper.ReadInConfig(); err != nil { |
130 if _, ok := err.(viper.ConfigFileNotFoundError); ok { |
130 if _, ok := err.(viper.ConfigFileNotFoundError); ok { |
131 // Config file not found; ignore error if desired |
131 // Config file not found; ignore error if desired |
132 } else { |
132 } else { |
133 // Config file was found but another error was produced |
133 // Config file was found but another error was produced |
134 } |
134 } |
135 } |
135 } |
136 |
136 |
137 // Config file found and successfully parsed |
137 // Config file found and successfully parsed |
138 ``` |
138 ``` |
139 |
139 |
173 Optionally you can provide a function for Viper to run each time a change occurs. |
173 Optionally you can provide a function for Viper to run each time a change occurs. |
174 |
174 |
175 **Make sure you add all of the configPaths prior to calling `WatchConfig()`** |
175 **Make sure you add all of the configPaths prior to calling `WatchConfig()`** |
176 |
176 |
177 ```go |
177 ```go |
178 viper.WatchConfig() |
|
179 viper.OnConfigChange(func(e fsnotify.Event) { |
178 viper.OnConfigChange(func(e fsnotify.Event) { |
180 fmt.Println("Config file changed:", e.Name) |
179 fmt.Println("Config file changed:", e.Name) |
181 }) |
180 }) |
|
181 viper.WatchConfig() |
182 ``` |
182 ``` |
183 |
183 |
184 ### Reading Config from io.Reader |
184 ### Reading Config from io.Reader |
185 |
185 |
186 Viper predefines many configuration sources such as files, environment |
186 Viper predefines many configuration sources such as files, environment |
501 runtime_viper.Unmarshal(&runtime_conf) |
501 runtime_viper.Unmarshal(&runtime_conf) |
502 |
502 |
503 // open a goroutine to watch remote changes forever |
503 // open a goroutine to watch remote changes forever |
504 go func(){ |
504 go func(){ |
505 for { |
505 for { |
506 time.Sleep(time.Second * 5) // delay after each request |
506 time.Sleep(time.Second * 5) // delay after each request |
507 |
507 |
508 // currently, only tested with etcd support |
508 // currently, only tested with etcd support |
509 err := runtime_viper.WatchRemoteConfig() |
509 err := runtime_viper.WatchRemoteConfig() |
510 if err != nil { |
510 if err != nil { |
511 log.Errorf("unable to read remote config: %v", err) |
511 log.Errorf("unable to read remote config: %v", err) |
512 continue |
512 continue |
513 } |
513 } |
514 |
514 |
515 // unmarshal new config into our runtime config struct. you can also use channel |
515 // unmarshal new config into our runtime config struct. you can also use channel |
516 // to implement a signal to notify the system of the changes |
516 // to implement a signal to notify the system of the changes |
517 runtime_viper.Unmarshal(&runtime_conf) |
517 runtime_viper.Unmarshal(&runtime_conf) |
518 } |
518 } |
519 }() |
519 }() |
520 ``` |
520 ``` |
521 |
521 |
522 ## Getting Values From Viper |
522 ## Getting Values From Viper |
544 |
544 |
545 Example: |
545 Example: |
546 ```go |
546 ```go |
547 viper.GetString("logfile") // case-insensitive Setting & Getting |
547 viper.GetString("logfile") // case-insensitive Setting & Getting |
548 if viper.GetBool("verbose") { |
548 if viper.GetBool("verbose") { |
549 fmt.Println("verbose enabled") |
549 fmt.Println("verbose enabled") |
550 } |
550 } |
551 ``` |
551 ``` |
552 ### Accessing nested keys |
552 ### Accessing nested keys |
553 |
553 |
554 The accessor methods also accept formatted paths to deeply nested keys. For |
554 The accessor methods also accept formatted paths to deeply nested keys. For |
667 So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration: |
667 So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration: |
668 |
668 |
669 ```go |
669 ```go |
670 cache1Config := viper.Sub("cache.cache1") |
670 cache1Config := viper.Sub("cache.cache1") |
671 if cache1Config == nil { // Sub returns nil if the key cannot be found |
671 if cache1Config == nil { // Sub returns nil if the key cannot be found |
672 panic("cache configuration not found") |
672 panic("cache configuration not found") |
673 } |
673 } |
674 |
674 |
675 cache1 := NewCache(cache1Config) |
675 cache1 := NewCache(cache1Config) |
676 ``` |
676 ``` |
677 |
677 |
679 |
679 |
680 Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: |
680 Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: |
681 |
681 |
682 ```go |
682 ```go |
683 func NewCache(v *Viper) *Cache { |
683 func NewCache(v *Viper) *Cache { |
684 return &Cache{ |
684 return &Cache{ |
685 MaxItems: v.GetInt("max-items"), |
685 MaxItems: v.GetInt("max-items"), |
686 ItemSize: v.GetInt("item-size"), |
686 ItemSize: v.GetInt("item-size"), |
687 } |
687 } |
688 } |
688 } |
689 ``` |
689 ``` |
690 |
690 |
691 The resulting code is easy to test, since it's decoupled from the main config structure, |
691 The resulting code is easy to test, since it's decoupled from the main config structure, |
692 and easier to reuse (for the same reason). |
692 and easier to reuse (for the same reason). |
724 |
724 |
725 ```go |
725 ```go |
726 v := viper.NewWithOptions(viper.KeyDelimiter("::")) |
726 v := viper.NewWithOptions(viper.KeyDelimiter("::")) |
727 |
727 |
728 v.SetDefault("chart::values", map[string]interface{}{ |
728 v.SetDefault("chart::values", map[string]interface{}{ |
729 "ingress": map[string]interface{}{ |
729 "ingress": map[string]interface{}{ |
730 "annotations": map[string]interface{}{ |
730 "annotations": map[string]interface{}{ |
731 "traefik.frontend.rule.type": "PathPrefix", |
731 "traefik.frontend.rule.type": "PathPrefix", |
732 "traefik.ingress.kubernetes.io/ssl-redirect": "true", |
732 "traefik.ingress.kubernetes.io/ssl-redirect": "true", |
733 }, |
733 }, |
734 }, |
734 }, |
735 }) |
735 }) |
736 |
736 |
737 type config struct { |
737 type config struct { |
738 Chart struct{ |
738 Chart struct{ |
739 Values map[string]interface{} |
739 Values map[string]interface{} |
740 } |
740 } |
741 } |
741 } |
742 |
742 |
743 var C config |
743 var C config |
744 |
744 |
745 v.Unmarshal(&C) |
745 v.Unmarshal(&C) |
776 } |
776 } |
777 ``` |
777 ``` |
778 |
778 |
779 Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. |
779 Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. |
780 |
780 |
|
781 ### Decoding custom formats |
|
782 |
|
783 A frequently requested feature for Viper is adding more value formats and decoders. |
|
784 For example, parsing character (dot, comma, semicolon, etc) separated strings into slices. |
|
785 |
|
786 This is already available in Viper using mapstructure decode hooks. |
|
787 |
|
788 Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/). |
|
789 |
781 ### Marshalling to string |
790 ### Marshalling to string |
782 |
791 |
783 You may need to marshal all the settings held in viper into a string rather than write them to a file. |
792 You may need to marshal all the settings held in viper into a string rather than write them to a file. |
784 You can use your favorite format's marshaller with the config returned by `AllSettings()`. |
793 You can use your favorite format's marshaller with the config returned by `AllSettings()`. |
785 |
794 |
786 ```go |
795 ```go |
787 import ( |
796 import ( |
788 yaml "gopkg.in/yaml.v2" |
797 yaml "gopkg.in/yaml.v2" |
789 // ... |
798 // ... |
790 ) |
799 ) |
791 |
800 |
792 func yamlStringSettings() string { |
801 func yamlStringSettings() string { |
793 c := viper.AllSettings() |
802 c := viper.AllSettings() |
794 bs, err := yaml.Marshal(c) |
803 bs, err := yaml.Marshal(c) |
795 if err != nil { |
804 if err != nil { |
796 log.Fatalf("unable to marshal config to YAML: %v", err) |
805 log.Fatalf("unable to marshal config to YAML: %v", err) |
797 } |
806 } |
798 return string(bs) |
807 return string(bs) |
799 } |
808 } |
800 ``` |
809 ``` |
801 |
810 |
802 ## Viper or Vipers? |
811 ## Viper or Vipers? |
803 |
812 |