Skip to content
forked from nil-go/konf

The simplest config loader for Go that reads/watches from file, env, flag and clouds (AWS, Azure, GCP).

License

Notifications You must be signed in to change notification settings

LiamMartens/konf

 
 

Repository files navigation

The simplest config loader for Go

Go Version Go Reference Mentioned in Awesome Go Go Report Card Build Coverage

konf offers an(other) opinion on how Go programs can read configuration without becoming coupled to a particular configuration source.

Features

Benchmarks

The following benchmarks compare the performance of konf with spf13/viper and knadh/koanf, which are inspiration of konf.

Unmarshal (ns/op) Unmarshal (allocs/op) Get1 (ns/op) Get (allocs/op)
Konf 41.09 4 16.71 1
Viper 614.8 22 104.9 3
Koanf 15949 657 7.898 1

Usage

Somewhere, early in an application's life, it will make a decision about which configuration source(s) (implementation) it actually wants to use. Something like:

    //go:embed config
    var config embed.FS

    func main() {
        var config konf.Config

        // Load configuration from embed file system.
        if err := config.Load(fs.New(config, "config/config.json")); err != nil {
            // Handle error here.
        }
        // Load configuration from environment variables.
        if err := config.Load(env.New(env.WithPrefix("server"))); err != nil {
            // Handle error here.
        }

        // Watch the changes of configuration.
        go func() {
          if err := config.Watch(ctx); err != nil {
            // Handle error here.
          }
        }

        konf.SetDefault(config)

        // ... other setup code ...
    }

Outside of this early setup, no other packages need to know about the choice of configuration source(s). They read configuration in terms of functions in package konf:

    func (app *appObject) Run() {
        // Server configuration with default values.
        serverConfig := struct {
            Host string
            Port int
        }{
            Host: "localhost",
            Port: "8080",
        }
        // Read the server configuration.
        if err := konf.Unmarshal("server", &serverConfig);  err != nil {
            // Handle error here.
        }

        // Register callbacks while server configuration changes.
        konf.OnChange(func() {
          // Reconfig the application object.
        }, "server")

        // ... use cfg in app code ...
    }

Design

It contains two APIs with two different sets of users:

  • The Config type is intended for application authors. It provides a relatively small API which can be used everywhere you want to read configuration. It defers the actual configuration loading to the Loader interface.
  • The Loader and Watcher interface is intended for configuration source library implementers. They are pure interfaces which can be implemented to provide the actual configuration.

This decoupling allows application developers to write code in terms of *konf.Config while the configuration source(s) is managed "up stack" (e.g. in or near main()). Application developers can then switch configuration sources(s) as necessary.

Understand the configuration

While the configuration is loaded from multiple sources, static like environments or dynamic like AWS AppConfig, it's hard to understand where a final value comes from. The Config.Explain method provides information about how Config resolve each value from loaders for the given path. One example explanation is like:

config.nest has value [map] is loaded by map.
Here are other value(loader)s:
  - env(env)

Even more, the Config.Explain blurs sensitive information (e.g. password, secret, api keys).

Observability

For watching the changes of configuration, it uses slog.Default() for logging. You can change the logger via option konf.WithLogHandler. Furthermore, you also can register onStatus via option konf.WithOnStatus to monitor the status of configuration loading/watching, e.g. recording metrics.

Configuration Providers

There are providers for the following configuration sources.

Loader Load From Watch Changes
env environment variables
fs fs.FS
file file
flag flag
pflag spf13/pflag
appconfig AWS AppConfig
s3 AWS S3
azappconfig Azure App Configuration
azblob Azure Blob Storage
secretmanager GCP Secret Manager
gcs GCP Cloud Storage

Custom Configuration Providers

You can Custom provider by implementing the Loader for static configuration loader (e.g fs) or both Loader and Watcher for dynamic configuration loader (e.g. appconfig).

Footnotes

  1. Comparing to Get in both Viper and Koanf only can get the primitive types, Get in Konf can get any type since it's just a wrapper of Unmarshal. So for complex struct, it only needs single Konf Get but may needs multiple Get(s) in Viper and Koanf.

About

The simplest config loader for Go that reads/watches from file, env, flag and clouds (AWS, Azure, GCP).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 100.0%