Read configuration values¶
Assumed setup
Every snippet below assumes a store and a ctx:
ctx := context.Background()
store, err := config.NewStore(ctx,
config.WithFiles(config.OS(), "/etc/app/config.yaml"),
config.WithEnv("APP"),
)
if err != nil {
return err
}
See Load & merge configuration for the full set of options.
Every read goes through a View, which resolves values from one immutable snapshot and
performs no I/O. Taking one is a pointer copy, so take them freely:
Read a scalar¶
host := view.GetString("server.host")
port := view.GetInt("server.port")
debug := view.GetBool("log.debug")
wait := view.GetDuration("client.timeout") // "30s", "1h15m"
An absent key returns the zero value rather than an error. If you need to tell absent from set to zero, ask:
IsSet is the same question. SectionExists("server") asks it of a subtree without
decoding one.
Read the type you actually want¶
There is a named accessor for the common widths and shapes — GetInt32, GetInt64,
GetUint through GetUint64, GetFloat64, GetTime, GetStringSlice, GetIntSlice,
GetStringMap, GetStringMapString, GetStringMapStringSlice.
For anything else, use config.Value[T], which reads any type at all:
addr, err := config.Value[netip.Addr](view, "server.bind")
hosts, err := config.Value[[]string](view, "upstream.hosts")
limits, err := config.Value[map[string]int](view, "quotas")
It is a function rather than a method because Go does not allow type parameters on
methods — the same reason UnmarshalSection and ValidateStruct are functions. It takes
a Reader, so it works against a View, an Observed handed to an observer, or a mock.
MustValue[T] is the panic-on-error variant, for package initialisation and tests.
Types that decode without you doing anything¶
Beyond the Go built-ins, these decode from their ordinary written form:
| Written as | Decodes to |
|---|---|
30s, 1h15m |
time.Duration |
10.0.0.1 |
netip.Addr, net.IP |
10.0.0.0/8 |
netip.Prefix, *net.IPNet |
10.0.0.1:8080 |
netip.AddrPort |
https://example.com |
*url.URL (pointer form) |
Europe/London |
*time.Location |
a,b,c |
[]string |
And any type of your own implementing encoding.TextUnmarshaler. That is how Go
expresses an enum or a domain type, so your types decode without this module having heard
of them:
type Severity int
const (
Debug Severity = iota
Info
)
func (s *Severity) UnmarshalText(text []byte) error {
switch string(text) {
case "debug":
*s = Debug
case "info":
*s = Info
default:
return fmt.Errorf("unknown severity %q", text)
}
return nil
}
Then read it like anything else:
The same hooks are used when decoding a struct, so a Severity field inside a
typed section works the same way.
Read a size¶
GetSizeInBytes reads values written the way an operator writes them:
Units are binary — kb, mb, gb, tb are powers of 1024. A bare number is a count of
bytes. Anything unparseable reads as 0, as the other accessors return their zero value.
Scope reads to a subtree¶
Sub returns a view rooted at a key, so a component can be handed the part of the
configuration that concerns it and nothing else:
It returns nil when the key is absent — check it. Provenance still reports the full
path, so a scoped view does not lose track of where its values came from.
Decode a whole struct¶
type AppSettings struct {
Server ServerSettings `mapstructure:"server"`
Log LogSettings `mapstructure:"log"`
}
var settings AppSettings
if err := view.Unmarshal(&settings); err != nil {
return err
}
UnmarshalKey("server", &srv) does the same for one subtree. For a typed view that
stays current across reloads, use typed sections instead.
Enumerate what is there¶
Keys returns every leaf path, sorted, including keys whose value is an empty container.
store.Snapshot().Values() returns the whole merged tree as a map — a copy, so mutating
it cannot affect the store.
Keep a set of reads consistent¶
A View is already pinned to one snapshot, so reads through it cannot straddle a reload.
When several values must agree and you would otherwise take more than one view, pin one
explicitly:
var (
host string
port int
)
err := store.With(func(view *config.View) error {
host = view.GetString("server.host")
port = view.GetInt("server.port")
return nil
})
if err != nil {
return err
}
Related¶
- Use typed sections — decode a subtree onto your own struct
- Load & merge configuration — where the values come from
- Provenance — and why each one won