go-up! A simple configuration library with recursive placeholders resolution and no magic.

go-up! A simple configuration library with placeholders resolution and no magic.

Build Status codecov Go Report Card

go-up provides a simple way to configure an application from multiple sources — built in resources, property files, environment variables, and whatever else you like.

Some features:

  • Recursive placeholders resolution
  • Less than 10Kb
  • No external dependencies
  • Modular and extensible architecture

This library was started after the considerable disappointment I had when I noticed that the configuration library used by my application increased the binary file size from 3MB to 11MB!!! (See the Rationale section below for detailed info)

Go-Up is written to be lightweight and will always remain lean and straightforward.

Getting Started

  1. To get started, import go-up:

In your go file:

import (
	"github.com/ufoscout/go-up"
)

If you use VGO, add in your go.mod file:

require github.com/ufoscout/go-up v0.6.1
  1. Define some properties. You can use placeholders, for example, in config.properties:
# This line is a comment because it stats with '#'

server.port=9090
server.host=127.0.0.1

# Placeholders here, they will be resolved at runtime
server.url=http://${server.host}:${server.port}/

Keys and values in a properties file are automatically trimmed.

  1. Build a go-up object that your properties:
ignoreFileNotFound := false 
up, err := NewGoUp().
		AddFile("./confing.properties", ignoreFileNotFound).
		Build();
  1. Look up properties by key:
port := up.GetIntOrDefault("server.port", 8080); // returns 9090
serverUrl := up.GetString("server.url") // returns http://127.0.0.1:9090/
defaultVal := up.GetStringOrDefault("unknown_Key", "defaultValue") // returns defaultValue

Readers

In go-up a "Reader" is whatever source of properties. By default, go-up offers readers to load properties from:

  • properties files in the file system
  • Environment variables
  • Programmatically typed properties

Custom properties readers can be easily created implementing the github.com/ufoscout/go-up/reader/Reader interface.

Placeholders resolution

go-up resolves placeholders recursively. For example:

fileOne.properties:

server.url=http://${${environment}.server.host}:${server.port}
server.port=8080

fileTwo.properties:

environment=PROD
PROD.server.host=10.10.10.10
DEV.server.host=127.0.0.1

Go code:

up, err := go_up.NewGoUp().
 AddFile("./fileOne.properties", false).
 AddFile("./fileTwo.properties", false).
 Build()

fmt.Println(up.GetString("server.url")) // this prints 'http://10.10.10.10:8080'

By default ${ and } delimiters are used. Custom delimiters can be easily defined:

up, err := go_up.NewGoUp().
 Delimiters("%(", ")"). // using %( and ) as delimiters
 ... etc ...
 Build()

Default Values

Placeholders can have default values which are used if the key is not otherwise provided. Example:

config.properties:

# the default value "8080" is used if 'PORT_NUMBER' is not provided
server.port=${PORT_NUMBER:8080}
    
# default is 127.0.0.1
server.ip=${IP:127.0.0.1}

server.url=${server.ip}/${server.port}

Go code:

up, err := go_up.NewGoUp().
 AddFile("./config.properties", false).
 Build()

// this prints 'http://127.0.0.1:8080'
fmt.Println(up.GetString("server.url")) 

The default separator for the default value is ":". A custom value can be set through the 'DefaultValueSeparator()' method of the GoUpBuilder.

Readers priority -> Last one wins

Properties defined in later readers will override properties defined in earlier readers, in case of overlapping keys. Hence, make sure that the most specific readers are the last ones in the given list of locations.

For example:

fileOne.properties:

server.url=urlFromOne

fileTwo.properties:

server.url=urlFromTwo
up, err := go_up.NewGoUp().
 AddFile("./fileOne.properties", false). 
 AddFile("./fileTwo.properties", false).
 AddReader(go_up.NewEnvReader("", false, false)). // Loading environment variables
 Build()

// this prints 'urlFromTwo'
fmt.Println(up.GetString("server.url"))

BTW, due to the fact that we used go_up.NewEnvReader(...)` as last reader, if at runtime an Environment variable called "server.url" is found, it will override the other values.

Finally, it is possible to specify a custom priority:

up, err := go_up.NewGoUp().

 // load the properties from the file system and specify their priority
 AddFileWithPriority("./fileOne.properties", false, go_up.HighestPriority).

 AddFile("./fileTwo.properties", false). // loads file with default priority
 AddReader(go_up.NewEnvReader("", false, false)). // Loads environment variables
 Build()

// this prints 'urlFromOne'
fmt.Println(up.GetString("server.url"))

The default priority is 100. The highest priority is 0. As usual, when more readers have the same priority, the last one wins in case of overlapping keys.

Working with Environment Variables

Built in support for environment variables is provided through the EnvReader struct:

// These should be defined in your system, not in the code. Here only as example.
os.Setenv("KEY_FROM_ENV", "ValueFromEnv")
os.Setenv("CUSTOM_PREFIX_KEY_FROM_ENV", "ValueFromEnv_WithCustomPrefix")

// Loads only variables with this prefix. The prefix is removed at runtime.
prefix := "CUSTOM_PREFIX_" 
// if true, the Env variable key is converted to lower case
toLowerCase := true 
// if true, the underscores "_" of the env variable key are replaced by dots "."
underscoreToDot := true 

up, err := go_up.NewGoUp().
 AddReader(go_up.NewEnvReader(prefix, toLowerCase, underscoreToDot)). // Loading environment variables
 Build()

// this prints 'ValueFromEnv_WithCustomPrefix'
fmt.Println(up.GetString("key.from.env"))

Real life example

A typical real life configuration would look like:

up, err := go_up.NewGoUp().

 // load a file
 AddFile("./default.properties", false).

 // load another file
 AddFile("./config/config.properties", false).

 // Add a not mandatory file, resource not found errors are ignored.
 // Here I am adding properties requried only during testing.
 AddFile("./test/test.properties", true).

 // Load the Environment variables.
 // The are used as they are defined, e.g. ENV_VARIABLE=XXX
 AddReaderWithPriority(go_up.NewEnvReader("APP_PREFIX_", false, false)).

 // Load the Environment variables and convert their keys
 // from ENV_VARIABLE=XXX to env.variable=XXX
 // This could be desired to override default properties
 AddReaderWithPriority(go_up.NewEnvReader("APP_PREFIX_", true, true)).
 
 // build the go-up object
 .Build()

go-up API

go-up has a straightforward API that hopefully does not need detailed documentation.

Some examples:

up, err := go_up.NewGoUp().
 Add("programmatically.added.key", "12345"). // default values can be added programmatically
 AddFile("./myFile.properties", false).
 Build()

// get a String. The empty string value is returned if the key is not found
aString := up.GetString("key")

// get a String. "defaultValue" is returned if the key is not found
aStringOrDefault := up.GetStringOrDefault("key", "defaultValue")

// get a String. An error is returned if the key is not found
aStringOrError, error1 := up.GetStringOrFail("key")

// get a slice from the comma separated tokens of the property value
aslice := up.GetStringSlice("key", ",")

All available methods are defined in the GoUp interface:

type GoUp interface {

  Exists(key string) bool

  GetBool(key string) bool
  GetBoolOrDefault(key string, defaultValue bool) bool
  GetBoolOrFail(key string) (bool, error)
   
  GetFloat64(key string) float64
  GetFloat64OrDefault(key string, defaultValue float64) float64
  GetFloat64OrFail(key string) (float64, error)
    
  GetInt(key string) int
  GetIntOrDefault(key string, defaultValue int) int
  GetIntOrFail(key string) (int, error)
   
  GetString(key string) string
  GetStringOrDefault(key string, defaultValue string) string
  GetStringOrFail(key string) (string, error)

  GetStringSlice(key string, separator string) []string
  GetStringSliceOrDefault(key string, separator string, defaultValue []string) []string
  GetStringSliceOrFail(key string, separator string) ([]string, error)

}

GoDoc

See https://godoc.org/github.com/ufoscout/go-up

Rationale

Before Go-Up, I used to use Viper. Viper is great and I surely recommend it; nevertheless, all of its features are not for free. In fact, it adds tons of dependencies that make the application much heavier.

So, if like me you aspire to code which is as light as possible and you can live with a smaller set of features (BWT go-up has some unique aces in the hole like recursive placeholders resolution!), then you should probably take into account a lighter alternative like go-up.

To show the impact that a single library can have on your application, I created three small examples (see: go-up_examples ), these are:

  • goup : a Go application that, using go-up, reads "Hello World" from a config file and prints it
  • plain : a Go application that prints "Hello World"
  • viper : a Go application that, using viper, reads "Hello World" from a config file and prints it

When built, they produce surprising results:

Library Produced Binary Size
Go-Up 2.185 KB
Plain Go 2.000 KB
Viper 11.782 KB

The Viper based binary file is nearly 6 times bigger than the other implementations!

(Build performed with go1.13 linux/amd64 on Ubuntu 18.04)

Similar Resources

A dead simple configuration manager for Go applications

Store Store is a dead simple configuration manager for Go applications. I didn't like existing configuration management solutions like globalconf, tac

Dec 24, 2022

shops is a simple command-line tool written in Go that helps you simplify the way you manage configuration across a set of machines.

shops is a simple command-line tool written in Go that helps you simplify the way you manage configuration across a set of machines. shops is your configuration management tool of choice when Chef, Puppet, Ansible are all too complicated and all you really want to do is run a bunch of regular shell against a set of hosts.

Jul 5, 2021

goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configuration file.

goconfig goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configur

Dec 15, 2022

✨Clean and minimalistic environment configuration reader for Golang

Clean Env Minimalistic configuration reader Overview This is a simple configuration reading tool. It just does the following: reads and parses configu

Jan 8, 2023

An opinionated configuration loading framework for Containerized and Cloud-Native applications.

An opinionated configuration loading framework for Containerized and Cloud-Native applications.

Opinionated configuration loading framework for Containerized and 12-Factor compliant applications. Read configurations from Environment Variables, an

Dec 16, 2022

Harvest configuration, watch and notify subscriber

Harvester Harvester is a configuration library which helps setting up and monitoring configuration values in order to dynamically reconfigure your app

Dec 26, 2022

Manage local application configuration files using templates and data from etcd or consul

confd confd is a lightweight configuration management tool focused on: keeping local configuration files up-to-date using data stored in etcd, consul,

Dec 27, 2022

Config-loader - Minimal and safe way to load in configuration files without any extra boilerplate, made for my own personal usage

💕 config-loader Minimal and safe way to load in configuration files without any

Jul 4, 2022

12 factor configuration as a typesafe struct in as little as two function calls

Config Manage your application config as a typesafe struct in as little as two function calls. type MyConfig struct { DatabaseUrl string `config:"DAT

Dec 13, 2022
Comments
  • Viper dependency

    Viper dependency

    Hi,

    First of all, nice library!

    I've found the following issue while trying to use it from a docker image:

            /usr/local/go/src/github.com/spf13/viper (from $GOROOT)
            /go/src/github.com/spf13/viper (from $GOPATH)
    

    It seems the example is trying to force getting the viper module and I'm using this one because I don't want any viper ;)

    Do you mind skipping the build process for the examples or removing them for good?

    Thanks

  • Documentation not up to date?

    Documentation not up to date?

    Hi - I like the idea behind the project and was trying to use it in one of my projects. It seems like the documentation needs updating:

    1. the package needs to be imported with an explicit name "go_up"
    2. No function .build() but there is Build()
  • Allow same line comments

    Allow same line comments

    Awesome project, simple and useful! What doesn't work are comments at the end of a line, example: db.port=8080 # this is some port Of course, this would also mean that values may not contain hashtag. Maybe an option for the reader can be set whether to enable or disable this same-line comments feature?

Related tags
A flexible and composable configuration library for Go that doesn't suck

croconf A flexible and composable configuration library for Go that doesn't suck Ned's spec for Go configuration which doesn't suck: Fully testable: t

Nov 17, 2022
A flexible and composable configuration library for Go that doesn't suck

croconf A flexible and composable configuration library for Go Why? We know that there are plenty of other Go configuration and CLI libraries out ther

Nov 17, 2022
Golang library for reading properties from configuration files in JSON and YAML format or from environment variables.

go-config Golang library for reading properties from configuration files in JSON and YAML format or from environment variables. Usage Create config in

Aug 22, 2022
Small library to read your configuration from environment variables

envconfig envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct. See the example

Nov 3, 2022
A minimalist Go configuration library
A minimalist Go configuration library

fig fig is a tiny library for loading an application's config file and its environment into a Go struct. Individual fields can have default values def

Dec 23, 2022
go implementation of lightbend's HOCON configuration library https://github.com/lightbend/config

HOCON (Human-Optimized Config Object Notation) Configuration library for working with the Lightbend's HOCON format. HOCON is a human-friendly JSON sup

Dec 3, 2022
Golang library for managing configuration data from environment variables

envconfig import "github.com/kelseyhightower/envconfig" Documentation See godoc Usage Set some environment variables: export MYAPP_DEBUG=false export

Dec 26, 2022
Light weight, extensible configuration management library for Go. Built in support for JSON, TOML, YAML, env, command line, file, S3 etc. Alternative to viper.
Light weight, extensible configuration management library for Go. Built in support for JSON, TOML, YAML, env, command line, file, S3 etc. Alternative to viper.

koanf (pronounced conf; a play on the Japanese Koan) is a library for reading configuration from different sources in different formats in Go applicat

Jan 8, 2023
Cfginterpolator is an interpolate library in golang allowing to include data from external sources in your configuration

cfginterpolator cfginterpolator is an interpolate library in golang allowing to include data from external sources in your configuration cfginterpolat

Dec 14, 2021
Tinyini - Bare-bones Go library for reading INI-like configuration files

tinyini tinyini is a minimalistic library for parsing INI-like configuration files. example configuration file globalkey = globalvalue [section] key

Jan 10, 2022