github.com/Billyyoyo/xviper@v1.15.1/README.md (about) 1 ### 改写了Viper增加了一点小功能 2 1. 支持多配置中心文件加载 3 2. 支持配置中心文件动态修改配置对象 4 3. 支持配置文件中使用占位符,仅支持值来自系统环境变量 5 6 --- 7 8 > ## Viper v2 feedback 9 > Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9 10 > 11 > **Thank you!** 12 13 ![Viper](.github/logo.png?raw=true) 14 15 [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) 16 [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) 17 18 [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) 19 [![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) 20 [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) 21 ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) 22 [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) 23 24 **Go configuration with fangs!** 25 26 Many Go projects are built using Viper including: 27 28 * [Hugo](http://gohugo.io) 29 * [EMC RexRay](http://rexray.readthedocs.org/en/stable/) 30 * [Imgur’s Incus](https://github.com/Imgur/incus) 31 * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) 32 * [Docker Notary](https://github.com/docker/Notary) 33 * [BloomApi](https://www.bloomapi.com/) 34 * [doctl](https://github.com/digitalocean/doctl) 35 * [Clairctl](https://github.com/jgsqware/clairctl) 36 * [Mercure](https://mercure.rocks) 37 38 39 ## Install 40 41 ```shell 42 go get github.com/spf13/viper 43 ``` 44 45 **Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. 46 47 48 ## What is Viper? 49 50 Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors). 51 It is designed to work within an application, and can handle all types of configuration needs 52 and formats. It supports: 53 54 * setting defaults 55 * reading from JSON, TOML, YAML, HCL, envfile and Java properties config files 56 * live watching and re-reading of config files (optional) 57 * reading from environment variables 58 * reading from remote config systems (etcd or Consul), and watching changes 59 * reading from command line flags 60 * reading from buffer 61 * setting explicit values 62 63 Viper can be thought of as a registry for all of your applications configuration needs. 64 65 66 ## Why Viper? 67 68 When building a modern application, you don’t want to worry about 69 configuration file formats; you want to focus on building awesome software. 70 Viper is here to help with that. 71 72 Viper does the following for you: 73 74 1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats. 75 2. Provide a mechanism to set default values for your different configuration options. 76 3. Provide a mechanism to set override values for options specified through command line flags. 77 4. Provide an alias system to easily rename parameters without breaking existing code. 78 5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default. 79 80 Viper uses the following precedence order. Each item takes precedence over the item below it: 81 82 * explicit call to `Set` 83 * flag 84 * env 85 * config 86 * key/value store 87 * default 88 89 **Important:** Viper configuration keys are case insensitive. 90 There are ongoing discussions about making that optional. 91 92 93 ## Putting Values into Viper 94 95 ### Establishing Defaults 96 97 A good configuration system will support default values. A default value is not 98 required for a key, but it’s useful in the event that a key hasn't been set via 99 config file, environment variable, remote configuration or flag. 100 101 Examples: 102 103 ```go 104 viper.SetDefault("ContentDir", "content") 105 viper.SetDefault("LayoutDir", "layouts") 106 viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"}) 107 ``` 108 109 ### Reading Config Files 110 111 Viper requires minimal configuration so it knows where to look for config files. 112 Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but 113 currently a single Viper instance only supports a single configuration file. 114 Viper does not default to any configuration search paths leaving defaults decision 115 to an application. 116 117 Here is an example of how to use Viper to search for and read a configuration file. 118 None of the specific paths are required, but at least one path should be provided 119 where a configuration file is expected. 120 121 ```go 122 viper.SetConfigName("config") // name of config file (without extension) 123 viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name 124 viper.AddConfigPath("/etc/appname/") // path to look for the config file in 125 viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths 126 viper.AddConfigPath(".") // optionally look for config in the working directory 127 err := viper.ReadInConfig() // Find and read the config file 128 if err != nil { // Handle errors reading the config file 129 panic(fmt.Errorf("fatal error config file: %w", err)) 130 } 131 ``` 132 133 You can handle the specific case where no config file is found like this: 134 135 ```go 136 if err := viper.ReadInConfig(); err != nil { 137 if _, ok := err.(viper.ConfigFileNotFoundError); ok { 138 // Config file not found; ignore error if desired 139 } else { 140 // Config file was found but another error was produced 141 } 142 } 143 144 // Config file found and successfully parsed 145 ``` 146 147 *NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc` 148 149 ### Writing Config Files 150 151 Reading from config files is useful, but at times you want to store all modifications made at run time. 152 For that, a bunch of commands are available, each with its own purpose: 153 154 * WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists. 155 * SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists. 156 * WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists. 157 * SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists. 158 159 As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate. 160 161 A small examples section: 162 163 ```go 164 viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName' 165 viper.SafeWriteConfig() 166 viper.WriteConfigAs("/path/to/my/.config") 167 viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written 168 viper.SafeWriteConfigAs("/path/to/my/.other_config") 169 ``` 170 171 ### Watching and re-reading config files 172 173 Viper supports the ability to have your application live read a config file while running. 174 175 Gone are the days of needing to restart a server to have a config take effect, 176 viper powered applications can read an update to a config file while running and 177 not miss a beat. 178 179 Simply tell the viper instance to watchConfig. 180 Optionally you can provide a function for Viper to run each time a change occurs. 181 182 **Make sure you add all of the configPaths prior to calling `WatchConfig()`** 183 184 ```go 185 viper.OnConfigChange(func(e fsnotify.Event) { 186 fmt.Println("Config file changed:", e.Name) 187 }) 188 viper.WatchConfig() 189 ``` 190 191 ### Reading Config from io.Reader 192 193 Viper predefines many configuration sources such as files, environment 194 variables, flags, and remote K/V store, but you are not bound to them. You can 195 also implement your own required configuration source and feed it to viper. 196 197 ```go 198 viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") 199 200 // any approach to require this configuration into your program. 201 var yamlExample = []byte(` 202 Hacker: true 203 name: steve 204 hobbies: 205 - skateboarding 206 - snowboarding 207 - go 208 clothing: 209 jacket: leather 210 trousers: denim 211 age: 35 212 eyes : brown 213 beard: true 214 `) 215 216 viper.ReadConfig(bytes.NewBuffer(yamlExample)) 217 218 viper.Get("name") // this would be "steve" 219 ``` 220 221 ### Setting Overrides 222 223 These could be from a command line flag, or from your own application logic. 224 225 ```go 226 viper.Set("Verbose", true) 227 viper.Set("LogFile", LogFile) 228 ``` 229 230 ### Registering and Using Aliases 231 232 Aliases permit a single value to be referenced by multiple keys 233 234 ```go 235 viper.RegisterAlias("loud", "Verbose") 236 237 viper.Set("verbose", true) // same result as next line 238 viper.Set("loud", true) // same result as prior line 239 240 viper.GetBool("loud") // true 241 viper.GetBool("verbose") // true 242 ``` 243 244 ### Working with Environment Variables 245 246 Viper has full support for environment variables. This enables 12 factor 247 applications out of the box. There are five methods that exist to aid working 248 with ENV: 249 250 * `AutomaticEnv()` 251 * `BindEnv(string...) : error` 252 * `SetEnvPrefix(string)` 253 * `SetEnvKeyReplacer(string...) *strings.Replacer` 254 * `AllowEmptyEnv(bool)` 255 256 _When working with ENV variables, it’s important to recognize that Viper 257 treats ENV variables as case sensitive._ 258 259 Viper provides a mechanism to try to ensure that ENV variables are unique. By 260 using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from 261 the environment variables. Both `BindEnv` and `AutomaticEnv` will use this 262 prefix. 263 264 `BindEnv` takes one or more parameters. The first parameter is the key name, the 265 rest are the name of the environment variables to bind to this key. If more than 266 one are provided, they will take precedence in the specified order. The name of 267 the environment variable is case sensitive. If the ENV variable name is not provided, then 268 Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter), 269 it **does not** automatically add the prefix. For example if the second parameter is "id", 270 Viper will look for the ENV variable "ID". 271 272 One important thing to recognize when working with ENV variables is that the 273 value will be read each time it is accessed. Viper does not fix the value when 274 the `BindEnv` is called. 275 276 `AutomaticEnv` is a powerful helper especially when combined with 277 `SetEnvPrefix`. When called, Viper will check for an environment variable any 278 time a `viper.Get` request is made. It will apply the following rules. It will 279 check for an environment variable with a name matching the key uppercased and 280 prefixed with the `EnvPrefix` if set. 281 282 `SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env 283 keys to an extent. This is useful if you want to use `-` or something in your 284 `Get()` calls, but want your environmental variables to use `_` delimiters. An 285 example of using it can be found in `viper_test.go`. 286 287 Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function. 288 Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic. 289 290 By default empty environment variables are considered unset and will fall back to 291 the next configuration source. To treat empty environment variables as set, use 292 the `AllowEmptyEnv` method. 293 294 #### Env example 295 296 ```go 297 SetEnvPrefix("spf") // will be uppercased automatically 298 BindEnv("id") 299 300 os.Setenv("SPF_ID", "13") // typically done outside of the app 301 302 id := Get("id") // 13 303 ``` 304 305 ### Working with Flags 306 307 Viper has the ability to bind to flags. Specifically, Viper supports `Pflags` 308 as used in the [Cobra](https://github.com/spf13/cobra) library. 309 310 Like `BindEnv`, the value is not set when the binding method is called, but when 311 it is accessed. This means you can bind as early as you want, even in an 312 `init()` function. 313 314 For individual flags, the `BindPFlag()` method provides this functionality. 315 316 Example: 317 318 ```go 319 serverCmd.Flags().Int("port", 1138, "Port to run Application server on") 320 viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) 321 ``` 322 323 You can also bind an existing set of pflags (pflag.FlagSet): 324 325 Example: 326 327 ```go 328 pflag.Int("flagname", 1234, "help message for flagname") 329 330 pflag.Parse() 331 viper.BindPFlags(pflag.CommandLine) 332 333 i := viper.GetInt("flagname") // retrieve values from viper instead of pflag 334 ``` 335 336 The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude 337 the use of other packages that use the [flag](https://golang.org/pkg/flag/) 338 package from the standard library. The pflag package can handle the flags 339 defined for the flag package by importing these flags. This is accomplished 340 by a calling a convenience function provided by the pflag package called 341 AddGoFlagSet(). 342 343 Example: 344 345 ```go 346 package main 347 348 import ( 349 "flag" 350 "github.com/spf13/pflag" 351 ) 352 353 func main() { 354 355 // using standard library "flag" package 356 flag.Int("flagname", 1234, "help message for flagname") 357 358 pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 359 pflag.Parse() 360 viper.BindPFlags(pflag.CommandLine) 361 362 i := viper.GetInt("flagname") // retrieve value from viper 363 364 // ... 365 } 366 ``` 367 368 #### Flag interfaces 369 370 Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. 371 372 `FlagValue` represents a single flag. This is a very simple example on how to implement this interface: 373 374 ```go 375 type myFlag struct {} 376 func (f myFlag) HasChanged() bool { return false } 377 func (f myFlag) Name() string { return "my-flag-name" } 378 func (f myFlag) ValueString() string { return "my-flag-value" } 379 func (f myFlag) ValueType() string { return "string" } 380 ``` 381 382 Once your flag implements this interface, you can simply tell Viper to bind it: 383 384 ```go 385 viper.BindFlagValue("my-flag-name", myFlag{}) 386 ``` 387 388 `FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface: 389 390 ```go 391 type myFlagSet struct { 392 flags []myFlag 393 } 394 395 func (f myFlagSet) VisitAll(fn func(FlagValue)) { 396 for _, flag := range flags { 397 fn(flag) 398 } 399 } 400 ``` 401 402 Once your flag set implements this interface, you can simply tell Viper to bind it: 403 404 ```go 405 fSet := myFlagSet{ 406 flags: []myFlag{myFlag{}, myFlag{}}, 407 } 408 viper.BindFlagValues("my-flags", fSet) 409 ``` 410 411 ### Remote Key/Value Store Support 412 413 To enable remote support in Viper, do a blank import of the `viper/remote` 414 package: 415 416 `import _ "github.com/spf13/viper/remote"` 417 418 Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path 419 in a Key/Value store such as etcd or Consul. These values take precedence over 420 default values, but are overridden by configuration values retrieved from disk, 421 flags, or environment variables. 422 423 Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve 424 configuration from the K/V store, which means that you can store your 425 configuration values encrypted and have them automatically decrypted if you have 426 the correct gpg keyring. Encryption is optional. 427 428 You can use remote configuration in conjunction with local configuration, or 429 independently of it. 430 431 `crypt` has a command-line helper that you can use to put configurations in your 432 K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. 433 434 ```bash 435 $ go get github.com/bketelsen/crypt/bin/crypt 436 $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json 437 ``` 438 439 Confirm that your value was set: 440 441 ```bash 442 $ crypt get -plaintext /config/hugo.json 443 ``` 444 445 See the `crypt` documentation for examples of how to set encrypted values, or 446 how to use Consul. 447 448 ### Remote Key/Value Store Example - Unencrypted 449 450 #### etcd 451 ```go 452 viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") 453 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 454 err := viper.ReadRemoteConfig() 455 ``` 456 457 #### etcd3 458 ```go 459 viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json") 460 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 461 err := viper.ReadRemoteConfig() 462 ``` 463 464 #### Consul 465 You need to set a key to Consul key/value storage with JSON value containing your desired config. 466 For example, create a Consul key/value store key `MY_CONSUL_KEY` with value: 467 468 ```json 469 { 470 "port": 8080, 471 "hostname": "myhostname.com" 472 } 473 ``` 474 475 ```go 476 viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY") 477 viper.SetConfigType("json") // Need to explicitly set this to json 478 err := viper.ReadRemoteConfig() 479 480 fmt.Println(viper.Get("port")) // 8080 481 fmt.Println(viper.Get("hostname")) // myhostname.com 482 ``` 483 484 #### Firestore 485 486 ```go 487 viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") 488 viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml" 489 err := viper.ReadRemoteConfig() 490 ``` 491 492 Of course, you're allowed to use `SecureRemoteProvider` also 493 494 ### Remote Key/Value Store Example - Encrypted 495 496 ```go 497 viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") 498 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 499 err := viper.ReadRemoteConfig() 500 ``` 501 502 ### Watching Changes in etcd - Unencrypted 503 504 ```go 505 // alternatively, you can create a new viper instance. 506 var runtime_viper = viper.New() 507 508 runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") 509 runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 510 511 // read from remote config the first time. 512 err := runtime_viper.ReadRemoteConfig() 513 514 // unmarshal config 515 runtime_viper.Unmarshal(&runtime_conf) 516 517 // open a goroutine to watch remote changes forever 518 go func(){ 519 for { 520 time.Sleep(time.Second * 5) // delay after each request 521 522 // currently, only tested with etcd support 523 err := runtime_viper.WatchRemoteConfig() 524 if err != nil { 525 log.Errorf("unable to read remote config: %v", err) 526 continue 527 } 528 529 // unmarshal new config into our runtime config struct. you can also use channel 530 // to implement a signal to notify the system of the changes 531 runtime_viper.Unmarshal(&runtime_conf) 532 } 533 }() 534 ``` 535 536 ## Getting Values From Viper 537 538 In Viper, there are a few ways to get a value depending on the value’s type. 539 The following functions and methods exist: 540 541 * `Get(key string) : interface{}` 542 * `GetBool(key string) : bool` 543 * `GetFloat64(key string) : float64` 544 * `GetInt(key string) : int` 545 * `GetIntSlice(key string) : []int` 546 * `GetString(key string) : string` 547 * `GetStringMap(key string) : map[string]interface{}` 548 * `GetStringMapString(key string) : map[string]string` 549 * `GetStringSlice(key string) : []string` 550 * `GetTime(key string) : time.Time` 551 * `GetDuration(key string) : time.Duration` 552 * `IsSet(key string) : bool` 553 * `AllSettings() : map[string]interface{}` 554 555 One important thing to recognize is that each Get function will return a zero 556 value if it’s not found. To check if a given key exists, the `IsSet()` method 557 has been provided. 558 559 Example: 560 ```go 561 viper.GetString("logfile") // case-insensitive Setting & Getting 562 if viper.GetBool("verbose") { 563 fmt.Println("verbose enabled") 564 } 565 ``` 566 ### Accessing nested keys 567 568 The accessor methods also accept formatted paths to deeply nested keys. For 569 example, if the following JSON file is loaded: 570 571 ```json 572 { 573 "host": { 574 "address": "localhost", 575 "port": 5799 576 }, 577 "datastore": { 578 "metric": { 579 "host": "127.0.0.1", 580 "port": 3099 581 }, 582 "warehouse": { 583 "host": "198.0.0.1", 584 "port": 2112 585 } 586 } 587 } 588 589 ``` 590 591 Viper can access a nested field by passing a `.` delimited path of keys: 592 593 ```go 594 GetString("datastore.metric.host") // (returns "127.0.0.1") 595 ``` 596 597 This obeys the precedence rules established above; the search for the path 598 will cascade through the remaining configuration registries until found. 599 600 For example, given this configuration file, both `datastore.metric.host` and 601 `datastore.metric.port` are already defined (and may be overridden). If in addition 602 `datastore.metric.protocol` was defined in the defaults, Viper would also find it. 603 604 However, if `datastore.metric` was overridden (by a flag, an environment variable, 605 the `Set()` method, …) with an immediate value, then all sub-keys of 606 `datastore.metric` become undefined, they are “shadowed” by the higher-priority 607 configuration level. 608 609 Viper can access array indices by using numbers in the path. For example: 610 611 ```jsonc 612 { 613 "host": { 614 "address": "localhost", 615 "ports": [ 616 5799, 617 6029 618 ] 619 }, 620 "datastore": { 621 "metric": { 622 "host": "127.0.0.1", 623 "port": 3099 624 }, 625 "warehouse": { 626 "host": "198.0.0.1", 627 "port": 2112 628 } 629 } 630 } 631 632 GetInt("host.ports.1") // returns 6029 633 634 ``` 635 636 Lastly, if there exists a key that matches the delimited key path, its value 637 will be returned instead. E.g. 638 639 ```jsonc 640 { 641 "datastore.metric.host": "0.0.0.0", 642 "host": { 643 "address": "localhost", 644 "port": 5799 645 }, 646 "datastore": { 647 "metric": { 648 "host": "127.0.0.1", 649 "port": 3099 650 }, 651 "warehouse": { 652 "host": "198.0.0.1", 653 "port": 2112 654 } 655 } 656 } 657 658 GetString("datastore.metric.host") // returns "0.0.0.0" 659 ``` 660 661 ### Extracting a sub-tree 662 663 When developing reusable modules, it's often useful to extract a subset of the configuration 664 and pass it to a module. This way the module can be instantiated more than once, with different configurations. 665 666 For example, an application might use multiple different cache stores for different purposes: 667 668 ```yaml 669 cache: 670 cache1: 671 max-items: 100 672 item-size: 64 673 cache2: 674 max-items: 200 675 item-size: 80 676 ``` 677 678 We could pass the cache name to a module (eg. `NewCache("cache1")`), 679 but it would require weird concatenation for accessing config keys and would be less separated from the global config. 680 681 So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration: 682 683 ```go 684 cache1Config := viper.Sub("cache.cache1") 685 if cache1Config == nil { // Sub returns nil if the key cannot be found 686 panic("cache configuration not found") 687 } 688 689 cache1 := NewCache(cache1Config) 690 ``` 691 692 **Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found. 693 694 Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: 695 696 ```go 697 func NewCache(v *Viper) *Cache { 698 return &Cache{ 699 MaxItems: v.GetInt("max-items"), 700 ItemSize: v.GetInt("item-size"), 701 } 702 } 703 ``` 704 705 The resulting code is easy to test, since it's decoupled from the main config structure, 706 and easier to reuse (for the same reason). 707 708 709 ### Unmarshaling 710 711 You also have the option of Unmarshaling all or a specific value to a struct, map, 712 etc. 713 714 There are two methods to do this: 715 716 * `Unmarshal(rawVal interface{}) : error` 717 * `UnmarshalKey(key string, rawVal interface{}) : error` 718 719 Example: 720 721 ```go 722 type config struct { 723 Port int 724 Name string 725 PathMap string `mapstructure:"path_map"` 726 } 727 728 var C config 729 730 err := viper.Unmarshal(&C) 731 if err != nil { 732 t.Fatalf("unable to decode into struct, %v", err) 733 } 734 ``` 735 736 If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter), 737 you have to change the delimiter: 738 739 ```go 740 v := viper.NewWithOptions(viper.KeyDelimiter("::")) 741 742 v.SetDefault("chart::values", map[string]interface{}{ 743 "ingress": map[string]interface{}{ 744 "annotations": map[string]interface{}{ 745 "traefik.frontend.rule.type": "PathPrefix", 746 "traefik.ingress.kubernetes.io/ssl-redirect": "true", 747 }, 748 }, 749 }) 750 751 type config struct { 752 Chart struct{ 753 Values map[string]interface{} 754 } 755 } 756 757 var C config 758 759 v.Unmarshal(&C) 760 ``` 761 762 Viper also supports unmarshaling into embedded structs: 763 764 ```go 765 /* 766 Example config: 767 768 module: 769 enabled: true 770 token: 89h3f98hbwf987h3f98wenf89ehf 771 */ 772 type config struct { 773 Module struct { 774 Enabled bool 775 776 moduleConfig `mapstructure:",squash"` 777 } 778 } 779 780 // moduleConfig could be in a module specific package 781 type moduleConfig struct { 782 Token string 783 } 784 785 var C config 786 787 err := viper.Unmarshal(&C) 788 if err != nil { 789 t.Fatalf("unable to decode into struct, %v", err) 790 } 791 ``` 792 793 Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. 794 795 ### Decoding custom formats 796 797 A frequently requested feature for Viper is adding more value formats and decoders. 798 For example, parsing character (dot, comma, semicolon, etc) separated strings into slices. 799 800 This is already available in Viper using mapstructure decode hooks. 801 802 Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/). 803 804 ### Marshalling to string 805 806 You may need to marshal all the settings held in viper into a string rather than write them to a file. 807 You can use your favorite format's marshaller with the config returned by `AllSettings()`. 808 809 ```go 810 import ( 811 yaml "gopkg.in/yaml.v2" 812 // ... 813 ) 814 815 func yamlStringSettings() string { 816 c := viper.AllSettings() 817 bs, err := yaml.Marshal(c) 818 if err != nil { 819 log.Fatalf("unable to marshal config to YAML: %v", err) 820 } 821 return string(bs) 822 } 823 ``` 824 825 ## Viper or Vipers? 826 827 Viper comes ready to use out of the box. There is no configuration or 828 initialization needed to begin using Viper. Since most applications will want 829 to use a single central repository for their configuration, the viper package 830 provides this. It is similar to a singleton. 831 832 In all of the examples above, they demonstrate using viper in its singleton 833 style approach. 834 835 ### Working with multiple vipers 836 837 You can also create many different vipers for use in your application. Each will 838 have its own unique set of configurations and values. Each can read from a 839 different config file, key value store, etc. All of the functions that viper 840 package supports are mirrored as methods on a viper. 841 842 Example: 843 844 ```go 845 x := viper.New() 846 y := viper.New() 847 848 x.SetDefault("ContentDir", "content") 849 y.SetDefault("ContentDir", "foobar") 850 851 //... 852 ``` 853 854 When working with multiple vipers, it is up to the user to keep track of the 855 different vipers. 856 857 858 ## Q & A 859 860 ### Why is it called “Viper”? 861 862 A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) 863 to [Cobra](https://github.com/spf13/cobra). While both can operate completely 864 independently, together they make a powerful pair to handle much of your 865 application foundation needs. 866 867 ### Why is it called “Cobra”? 868 869 Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)? 870 871 ### Does Viper support case sensitive keys? 872 873 **tl;dr:** No. 874 875 Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars). 876 In order to provide the best experience when using multiple sources, the decision has been made to make all keys case insensitive. 877 878 There has been several attempts to implement case sensitivity, but unfortunately it's not that trivial. We might take a stab at implementing it in [Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial noise, it does not seem to be requested that much. 879 880 You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9 881 882 ### Is it safe to concurrently read and write to a viper? 883 884 No, you will need to synchronize access to the viper yourself (for example by using the `sync` package). Concurrent reads and writes can cause a panic. 885 886 ## Troubleshooting 887 888 See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).