Hlavní navigace

Zpracování konfiguračních souborů v Go s využitím knihovny Viper

Pavel Tišnovský

Ve čtyřicáté části seriálu o programovacím jazyce Go si popíšeme některé možnosti nabízené balíčkem Viper. Tento balíček slouží ke zdánlivě triviální úloze – načítání a zpracování konfigurace.

Doba čtení: 39 minut

Sdílet

11. Další datové typy podporované formátem TOML

12. Načtení konfigurace ze souboru ve formátu JSON

13. Načtení konfigurace ze souboru ve formátu YAML

14. Formát YAML se sekcemi

15. Načtení konfiguračních parametrů z proměnných prostředí

16. Automatická konverze jména proměnné prostředí

17. Mapování proměnných prostředí se zvoleným prefixem

18. Automatické mapování všech proměnných prostředí se zvoleným prefixem

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Zpracování konfiguračních souborů v Go s využitím knihovny Viper

V seriálu o programovacím jazyce Go jsme si popsali již velké množství různých standardních balíčků i balíčků externích, které je nutné doinstalovat, a to buď příkazem go get nebo s využitím systému modulů, který byl do Go oficiálně přidán ve verzi 1.11. Dnes si popíšeme balíček pojmenovaný Viper, jenž naleznete v GitHub repositáři umístěném na adrese https://github.com/spf13/viper. Tento balíček slouží k na první pohled triviální a snadno proveditelné operaci – načtení a zpracování konfiguračních souborů, popř. konfiguračních parametrů získaných z jiného prostředku (přes URL, ze secret atd.). Mohlo by se zdát, že pro tento úkol ani není nutné instalovat externí balíček, protože zpracování (načtení, parsing) konfiguračních souborů typu INI, popř. .properties atd. je na první pohled velmi jednoduché a bezproblematické.

Ve skutečnosti tomu tak není (ostatně co je vlastně v IT jednoduché a současně i bezproblematické?), a to zejména ve chvíli, kdy je zapotřebí použít složitější strukturu dat. To je dnes velmi častý požadavek, protože konfigurační soubory ve stylu „klíč=skalární_hodnota“ již zdaleka nedokážou pokrýt všechny potřeby vývojářů, devops i administrátorů. Příkladem mohou být mnohdy složitě strukturované konfigurace pro nasazování služeb (deploying), konfigurace, v nichž se používají strukturovaná data (pole, mapy), hodnoty reprezentující data či časová razítka atd. Ovšem někdy se setkáme i s požadavkem na možnost uložení libovolné hodnoty s plovoucí řádovou čárkou reprezentovatelnou nějakým formátem definovaným ve známé normě IEEE 754. Problém je, že mezi takové hodnoty patří i kladné a záporné nekonečno, popř. hodnota NaN (not a number), s nimiž si mnohé jednodušší formáty konfiguračních souborů a jejich parserů nemusí dobře poradit.

2. Konfigurační soubory používající formáty INI, .properties nebo XML

Podívejme se nejdříve na strukturu konzervativně pojatých konfiguračních souborů. Nejprve si ukážeme příklad konfiguračního souboru používajícího formát INI (zkráceno, skutečný konfigurační soubor je mnohem delší):

[Extensions]
Plugin Failed Warning=1
Ask Flash Download=0
Plugins=0
 
[Security Prefs]
Enable SSL v3=1
Enable TLS v1.0=1
Enable TLS v1.1=1
Password Lifetime=5
 
[State]
Accept License=1
Reading Plugins=0
Run=0
Poznámka: některé implementace umožňují vytvářet hierarchické struktury, i když nestandardním způsobem:
[Server.Settings]
url=1.2.3.4
port=8080
 
[Server.Policy]
...
...
...

Taktéž se můžeme setkat s podporou komentářů, které začínají středníkem.

Dále si ukážeme příklad konfiguračního používajícího formát .properties (jedná se o aplikaci naprogramovanou v Javě, v níž najdeme přímou podporu pro práci s těmito soubory):

#FreeMind 0.9.0
#Sat Sep 14 21:25:49 CEST 2019
antialias=antialias_none
standardrootnodestyle=bubble
standardselectednodecolor=\#d2d2d2
leftToolbarVisible=false
split_pane_position=423
appwindow_y=1
use_split_pane=false
appwindow_x=0
appwindow_state=0
remind_use_rich_text_in_new_long_nodes=true
lookandfeel=metal
antialiasEdges=false
toolbarVisible=false
appwindow_height=601
appwindow_width=1024
standardfont=SansSerif
OptionPanel_Window_Properties=<?xml version\="1.0" encoding\="UTF-8"?><option_panel_window_configuration_storage x\="-116" y\="-286" width\="879" height\="899" panel\="Appearance"/>
Poznámka: povšimněte si, že se pro složitější strukturu použilo vložené XML.

A nakonec následuje příklad konfiguračního souboru používajícího formát XML (opět se jedná o soubor určený pro aplikaci naprogramovanou v Javě):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JSpeccySettings xmlns="http://xml.netbeans.org/schema/JSpeccy">
    <SpectrumSettings>
        <Issue2>false</Issue2>
        <AYEnabled48k>false</AYEnabled48k>
        <mutedSound>false</mutedSound>
        <loadingNoise>true</loadingNoise>
        <ULAplus>false</ULAplus>
        <defaultModel>1</defaultModel>
        <framesInt>2</framesInt>
        <doubleSize>true</doubleSize>
        <multifaceEnabled>false</multifaceEnabled>
        <mf128on48K>false</mf128on48K>
        <hifiSound>false</hifiSound>
    </SpectrumSettings>
   <TapeSettings>
        <flashload>false</flashload>
        <accelerateLoading>true</accelerateLoading>
        <enableSaveTraps>true</enableSaveTraps>
        <highSamplingFreq>false</highSamplingFreq>
    </TapeSettings>
    <KeyboardJoystickSettings>
        <JoystickModel>0</JoystickModel>
    </KeyboardJoystickSettings>
    <AY8912Settings>
        <soundMode>0</soundMode>
    </AY8912Settings>
</JSpeccySettings>
Poznámka: datový typ hodnot zde není určen přímo, ale v závislosti na použitém schématu.

3. Lepší přístup k uložení konfigurace – formáty JSON, YAML a TOML

Složitější konfigurační parametry se z výše uvedených důvodů (zejména ve chvíli, kdy je nutné pracovat se strukturovanými daty a různými datovými typy) neukládají ani do souborů typu INI ani do .properties (ty se používají například ve světě Javy, ovšem jen v omezené míře). Namísto toho je možné využít například formáty:

  • JSON (JavaScript Object Notation) – pravděpodobně nejznámější formát, který byl sice určen pro přenosy dat (typicky mezi webovou službou/serverem a další službou nebo klientem), ovšem dnes se s tímto formátem setkáme i v dalších odvětvích – serializace, uložení strukturovaných dat do databáze a taktéž konfigurační soubory. Tento formát je projektem Viper podporován a proto ho použijeme v demonstračním příkladu. Základní informace o tomto formátu najdete na adrese http://json.org/.
  • YAML (YAML Ain't Markup Language) – je formátem, který se namísto použití závorek pro určení struktury spoléhá spíše na použití odsazení (podobně, jako je tomu v Pythonu) a popř. i speciálních znaků (-, #, [, ] atd.). S tímto formátem se setkáme ve světě Dockeru a Kubernetes, ovšem je ho možné použít i pro další účely. I tento formát je projektem Viper podporován a použijeme ho ve dvou demonstračních příkladech. Bližší informace o tomto velmi zajímavém formátu lze nalézt na adrese https://yaml.org/.
  • XML (Extensible Markup Language) – s tímto formátem pravděpodobně není nutné čtenáře tohoto článku podrobněji seznamovat. XML se pro uložení konfiguračních parametrů používá již delší dobu, i když se z některých důvodů nemusí vždy jednat o ideální řešení. Projekt Viper tento formát nepodporuje a ani to ve skutečnosti není nutné, protože pro zpracování XML existují i pro programovací jazyk Go jiné balíčky, například https://golang.org/pkg/encoding/xml/.
  • TOML (Tom's Obvious, Minimal Language) – formát TOML sice zdánlivě (alespoň na první pohled) vychází ze souborů typu INI, ovšem ve skutečnosti se jedná o odlišný, v mnoha ohledech vylepšený a především promyšlený formát, v němž byly odstraněny prakticky všechny nevýhody INI a přitom byla zachována čitelnost a snadnost úprav. Tento konfigurační formát budeme používat v některých demonstračních příkladech, takže se s jeho možnostmi (zdaleka ovšem ne se všemi!) seznámíme.
  • edn (Extensible Data Notation) – tento formát vychází ze syntaxe a sémantiky programovacího jazyka Clojure, je tedy založen na S-výrazech rozšířených o možnost zápisu map (slovníků) a vektorů. Formát edn je rozšířen právě v ekosystému jazyka Clojure, ale v ostatních oblastech se příliš nerozšířil, takže je dnes uveden spíše pro úplnost. Popis formátu edn (a tím pádem i popis syntaxe Clojure) naleznete na stránce https://github.com/edn-format/edn.

4. Balíček Viper

Dále popisovaný balíček Viper je určen především pro práci s výše zmíněnými formáty JSON, YAML a TOML, ovšem ve skutečnosti ho lze použít i pro další činnosti, například pro zpracování proměnných prostředí, načtení konfigurace ze síťového prostředku (tedy přes nějaký zdroj dostupný přes URL) atd. Možnosti tohoto balíčku budou ukázány na několika demonstračních příkladech, ovšem nejdříve je pochopitelně nutné si tento balíček nainstalovat. K dispozici máme dvě možnosti – buď použít klasický příkaz go get nebo využít systém modulů s importem balíčku (jeho stažení a instalace se provede po zadání příkazu go build). Použití první možnosti je snadné:

$ go get github.com/spf13/viper

Další možností je použít import v jakémkoli projektu, který obsahuje soubor go.mod:

import (
        ...
        ...
        ...
        "github.com/spf13/viper"
        ...
        ...
        ...
)

Instalace balíčku proběhne po první zadání příkazu:

$ go build

5. Inicializace knihovny Viper a načtení konfiguračního souboru

Pojďme si nyní ukázat některé základní operace, které nám balíček Viper poskytuje. Začneme tím nejjednodušším příkladem, v němž je provedena inicializace celé knihovny, specifikace, ve kterém adresáři se mají nalézt konfigurační soubory a z následného načtení zvoleného konfiguračního souboru. Důležité a zpočátku možná poněkud matoucí je, že se jméno konfiguračního souboru zadává bez přípony. V příkladu se bude načítat konfigurační soubor pojmenovaný config1.toml, jehož přípona jasně určuje i jeho vnitřní formát:

url="http://1.2.3.4"
port=8888

Inicializace knihovny a určení místa, v němž se má hledat konfigurační soubor. Tento soubor budeme hledat v adresáři, ve kterém byl projekt spuštěn:

viper.SetConfigName("config1")
viper.AddConfigPath(".")

Pokus o načtení konfiguračního souboru, který však může skončit s chybou:

err := viper.ReadInConfig()

Zpracování případných chyb, které mohou při načítání nastat:

if err != nil {
        log.Fatalf("Fatal error in config file: %s \n", err)
}

Užitečné je, že pokud při načítání konfiguračního souboru dojde k nějaké chybě, je uživateli vypsána poměrně přesná informace, ve kterém místě k chybě došlo. To je důležité, protože se mnohdy jedná o snadno opravitelné maličkosti – chybějící uvozovka, chybějící čárka atd. Ostatně si to můžeme snadno odzkoušet, pokud v konfiguračním souboru schválně zapomeneme jednu uvozovku:

url=http://1.2.3.4"
port=8888

Program by měl při pokusu o načtení takového souboru vypsat chybové hlášení:

2019/10/30 19:31:46 Reading configuration
2019/10/30 19:31:46 Fatal error in config file: While parsing config: (1, 5): keys cannot contain : character
exit status 1

Úplný zdrojový kód tohoto triviálního příkladu vypadá následovně:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config1")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
}

6. Načtení hodnot uložených v konfiguračních souborech

V dalším příkladu si vyzkoušíme, jak se vlastně načítají jednotlivé hodnoty uložené v konfiguračním souboru. Pro tento účel je k dispozici několik funkcí (a současně i stejně pojmenovaných metod, jak uvidíme dále), zejména pak funkce určené pro načtení hodnot typu celé číslo a řetězec, ovšem i funkce vracející hodnoty dalších typů:

func GetInt(key string) int
func GetInt32(key string) int32
func GetInt64(key string) int64
 
func GetUint(key string) uint
func GetUint32(key string) uint32
func GetUint64(key string) uint64
 
func GetBool(key string) bool
 
func GetDuration(key string) time.Duration
func GetTime(key string) time.Time
 
func GetFloat64(key string) float64
 
func GetString(key string) string
Poznámka: znovu si povšimněte, že formát TOML rozlišuje datové typy hodnot, tj. odliší celé číslo (popř. číslo s plovoucí čárkou) od řetězce atd.

Příklad, který načte hodnotu typu řetězec a další hodnotu typu celé číslo, může vypadat následovně:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config1")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        url := viper.GetString("url")
        port := viper.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

7. Zjištění, zda byla určitá hodnota skutečně zapsána

Knihovna Viper je navržena takovým způsobem, že pokus o přečtení hodnoty, která ve skutečnosti není v souboru uložena, neskončí s chybou. Namísto toho se vrátí nulová hodnota, která je zcela jednoznačně definována pro každý datový typ v programovacím jazyce Go. Pokud se tedy pokusíme načíst číslo portu či adresu, které v konfiguračním souboru nejsou, program nezhavaruje a bude pokračovat ve své činnosti s nulovými hodnotami (prázdný řetězec a číslo 0).

Samozřejmě však existuje možnost, jak zjistit, jestli se nějaká hodnota v konfiguračním souboru nachází či nikoli – postačuje použít následující funkci:

func IsSet(key string) bool

Použit této funkce v praxi může vypadat následovně:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config1")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        if !viper.IsSet("url") {
                log.Fatalf("URL is not specified in config file")
        }
 
        if !viper.IsSet("port") {
                log.Fatalf("port is not specified in config file")
        }
 
        url := viper.GetString("url")
        port := viper.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

8. Hierarchická struktura dat

Formát TOML umožňuje, ostatně podobně jako je tomu i v obou dvou dalších formátech, uložit data s hierarchickou strukturou (strom obsahující různé typy prvků). Nejjednodušším příkladem může být následující soubor, který obsahuje jednu (pod)sekci nazvanou service, přičemž tato podsekce obsahuje dvojice klíč=hodnota:

[service]
url="http://1.2.3.4"
port=8888

Existuje několik způsobů, jak takový konfigurační soubor načíst. Nejvíce přímočaré je použití funkce Sub, které se předá klíč (jméno podsekce) a vrátí se objekt/struktura typu Viper:

func Sub(key string) *Viper

Co to v praxi znamená? Všechny výše zmíněné funkce určené pro načtení řetězců, celých čísel atd., jsou současně dostupné i ve formě metod objektu typu Viper, takže můžeme nejdříve získat příslušnou sekci a následně z ní získat potřebné údaje:

serviceConfig := viper.Sub("service")
url := serviceConfig.GetString("url")
port := serviceConfig.GetInt("port")

Podívejme se nyní na úplný kód příkladu, v němž se tento postup používá:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config2")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        serviceConfig := viper.Sub("service")
        url := serviceConfig.GetString("url")
        port := serviceConfig.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

9. Práce se seznamy uloženými v konfiguračních souborech

V konfiguračních souborech se nemusí nacházet pouze skalární data, ale například i seznamy. V následujícím souboru jsou uloženy dva seznamy, jeden obsahující tři prvky a druhý jednoprvkový:

[service]
url="http://1.2.3.4"
port=8888
 
[users]
accepted=["qa", "devel", "manager"]
blacklisted=["cracker"]

Pro přímé načtení těchto seznamů lze použít funkci/metodu:

func (v *Viper) GetStringSlice(key string) []string

Podívejme se nyní na demonstrační příklad, ve kterém jsou oba výše zmíněné seznamy načteny a následně vypsány:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config3")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        serviceConfig := viper.Sub("service")
        url := serviceConfig.GetString("url")
        port := serviceConfig.GetInt("port")
 
        usersConfig := viper.Sub("users")
        accepted := usersConfig.GetStringSlice("accepted")
        blacklisted := usersConfig.GetStringSlice("blacklisted")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
        log.Printf("Accepted users: %v\n", accepted)
        log.Printf("Blacklisted users: %v\n", blacklisted)
}

Výsledek, jenž získáme po spuštění tohoto příkladu:

2019/10/29 19:02:40 Reading configuration
2019/10/29 19:02:40 Done
2019/10/29 19:02:40 Starting the service at address http://1.2.3.4:8888
2019/10/29 19:02:40 Accepted users: [qa devel manager]
2019/10/29 19:02:40 Blacklisted users: [cracker]

Podobně můžeme načíst seznam celých čísel:

func (v *Viper) GetIntSlice(key string) []int

Ze souboru:

[service]
url="http://1.2.3.4"
port=8888
 
[users]
accepted=["qa", "devel", "manager"]
blacklisted=["cracker"]
ids=[0,42,1000,1001]

Kód upraveného příkladu:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config4")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        serviceConfig := viper.Sub("service")
        url := serviceConfig.GetString("url")
        port := serviceConfig.GetInt("port")
 
        usersConfig := viper.Sub("users")
        accepted := usersConfig.GetStringSlice("accepted")
        blacklisted := usersConfig.GetStringSlice("blacklisted")
        ids := usersConfig.GetIntSlice("ids")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
        log.Printf("Accepted users: %v\n", accepted)
        log.Printf("Blacklisted users: %v\n", blacklisted)
        log.Printf("UIDs: %v\n", ids)
}

Výsledek upraveného příkladu:

2019/10/29 19:10:43 Reading configuration
2019/10/29 19:10:43 Done
2019/10/29 19:10:43 Starting the service at address http://1.2.3.4:8888
2019/10/29 19:10:43 Accepted users: [qa devel manager]
2019/10/29 19:10:43 Blacklisted users: [cracker]
2019/10/29 19:10:43 UIDs: [0 42 1000 1001]

10. Načtení celé sekce do mapy

V osmé kapitole jsme si ukázali, jakým způsobem je možné přistupovat k jednotlivým sekcím a podsekcím v konfiguračních souborech. Ve skutečnosti ale v některých případech můžeme celou sekci načíst do mapy, a to buď mapy řetězců nebo mapy obecných rozhraní:

func (v *Viper) GetStringMap(key string) map[string]interface{}
func (v *Viper) GetStringMapString(key string) map[string]string

Ukažme si použití funkce/metody GetStringMap pro načtení sekce service z následujícího souboru:

[service]
service.url="http://1.2.3.4"
service.port=8888
 
[users]
accepted=["qa", "devel", "manager"]
blacklisted=["cracker"]
ids=[0,42,1000,1001]

Upravený demonstrační příklad, v němž je zvýrazněn kód, ve kterém mapu získáme a dále s ní pracujeme:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config4")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        serviceConfig := viper.GetStringMap("service")
        url := serviceConfig["url"]
        port := serviceConfig["port"]
 
        usersConfig := viper.Sub("users")
        accepted := usersConfig.GetStringSlice("accepted")
        blacklisted := usersConfig.GetStringSlice("blacklisted")
        ids := usersConfig.GetIntSlice("ids")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
        log.Printf("Accepted users: %v\n", accepted)
        log.Printf("Blacklisted users: %v\n", blacklisted)
        log.Printf("UIDs: %v\n", ids)
}

Výsledek běhu tohoto příkladu:

2019/10/29 19:21:25 Reading configuration
2019/10/29 19:21:25 Done
2019/10/29 19:21:25 Starting the service at address http://1.2.3.4:8888
2019/10/29 19:21:25 Accepted users: [qa devel manager]
2019/10/29 19:21:25 Blacklisted users: [cracker]
2019/10/29 19:21:25 UIDs: [0 42 1000 1001]

11. Další datové typy podporované formátem TOML

Ve formátu TOML jsou kromě řetězců, celých čísel a seznamů podporovány i další datové typy – pravdivostní typ, čísla s plovoucí řádovou čárkou a zejména pak, což je v praxi velmi užitečné, typ „datum+čas“ neboli časové razítko. Konfigurační soubor obsahující hodnoty těchto typů může vypadat následovně:

integer1 = 1
integer2 = 0x2a
 
float1 = 3.14
float2 = -2e-5
float3 = -inf
float4 = nan
 
bool1 = true
bool2 = false
 
date1 = 2000-01-01 01:10:00Z
date2 = 2000-01-01 01:10:00-02:00
date3 = 2000-01-01T01:10:00Z
date4 = 2000-01-01T01:10:00+06:30
Poznámka: povšimněte si, že se nejedná o řetězce; dále pak stojí za povšimnutí podpora pro všechny hodnoty dle IEEE 754 (včetně nekonečen a NaN) a způsob zápisu časových razítek (s případným určením časové zóny).

Způsob načtení hodnot všech těchto typů (u časových razítek je typ time.Time, popř. time.Duration):

package main
 
import (
        "fmt"
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config6")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        fmt.Printf("integer1: %d\n", viper.GetInt("integer1"))
        fmt.Printf("integer2: %d\n", viper.GetInt("integer2"))
 
        fmt.Printf("float1: %f\n", viper.GetFloat64("float1"))
        fmt.Printf("float2: %f\n", viper.GetFloat64("float2"))
        fmt.Printf("float3: %f\n", viper.GetFloat64("float3"))
        fmt.Printf("float4: %f\n", viper.GetFloat64("float4"))
 
        fmt.Printf("bool1: %t\n", viper.GetBool("bool1"))
        fmt.Printf("bool2: %t\n", viper.GetBool("bool2"))
 
        fmt.Printf("date1: %s\n", viper.GetTime("date1").Format("Mon Jan 2 15:04:05 MST 2006"))
        fmt.Printf("date2: %s\n", viper.GetTime("date2").Format("Mon Jan 2 15:04:05 MST 2006"))
        fmt.Printf("date3: %s\n", viper.GetTime("date3").Format("Mon Jan 2 15:04:05 MST 2006"))
        fmt.Printf("date4: %s\n", viper.GetTime("date4").Format("Mon Jan 2 15:04:05 MST 2006"))
}

Příklad výstupu tohoto příkladu:

2019/10/30 20:23:43 Reading configuration
2019/10/30 20:23:43 Done
integer1: 1
integer2: 42
float1: 3.140000
float2: -0.000020
float3: -Inf
float4: NaN
bool1: true
bool2: false
date1: Sat Jan 1 01:10:00 UTC 2000
date2: Sat Jan 1 01:10:00 -0200 2000
date3: Sat Jan 1 01:10:00 UTC 2000
date4: Sat Jan 1 01:10:00 +0630 2000

12. Načtení konfigurace ze souboru ve formátu JSON

Jak již víme z úvodního textu, je možné v knihovně Viper pracovat i s formátem JSON. Původní konfigurační soubor:

url="http://1.2.3.4"
port=8888

Tedy můžeme uložit i do jiné podoby:

{
    "url" : "http://1.2.3.4",
    "port" : 8888
}

Důležité (a velmi praktické) je, že se samotný kód aplikací nemusí žádným způsobem měnit, což je ostatně patrné při porovnání zdrojového kódu umístěného pod tímto odstavcem s kódem, který byl ukázán v šesté kapitole:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config7")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        url := viper.GetString("url")
        port := viper.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

13. Načtení konfigurace ze souboru ve formátu YAML

Naprosto stejným způsobem lze zpracovat konfigurační soubor uložený ve formátu YAML. Ekvivalent předchozích dvou konfiguračních souborů vypadá při konverzi do YAMLu takto:

---
url: http://1.2.3.4
port: 8888

Opět platí, že samotný zdrojový kód příkladu zůstane stále stejný, bez nutnosti byť i jednořádkové změny:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config8")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        url := viper.GetString("url")
        port := viper.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

14. Formát YAML se sekcemi

Jen pro úplnost se podívejme, jak by vypadala konfigurace zapsaná ve formátu YAML se sekcí (nebo více sekcemi):

service:
  url: 'http://1.2.3.4'
  port: 8888

Kód pro načtení takového souboru se – opět – nijak neliší od příkladu, v němž jsme pracovali s formátem TOML:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config9")
        viper.AddConfigPath(".")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        serviceConfig := viper.Sub("service")
        url := serviceConfig.GetString("url")
        port := serviceConfig.GetInt("port")
 
        log.Printf("Starting the service at address %s:%d\n", url, port)
}

15. Načtení konfiguračních parametrů z proměnných prostředí

Balíček Viper podporuje i načítání konfiguračních parametrů z proměnných prostředí. Tyto parametry je možné sloučit s parametry načtenými z konfiguračních souborů a tak případně přepsat výchozí hodnoty. Základem pro práci s proměnnými prostředí je funkce:

viper.BindEnv("editor", "EDITOR")

Tato funkce zajistí, že se proměnná prostředí EDITOR (pokud je ovšem nastavena) namapuje do stávající konfigurace pod klíčem editor. Můžeme si to snadno otestovat:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config9")
        viper.AddConfigPath(".")
 
        viper.BindEnv("editor", "EDITOR")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        editor := viper.GetString("editor")
 
        log.Printf("Selected editor: %s\n", editor)
}

Výsledkem by měla být následující zpráva:

2019/10/30 13:26:09 Reading configuration
2019/10/30 13:26:09 Done
2019/10/30 13:26:09 Selected editor: vim
Poznámka: v případě, že se vám zobrazí odlišné jméno editoru, jedná se o závažnou chybu systému :-)

16. Automatická konverze jména proměnné prostředí

Pokud namísto:

viper.BindEnv("editor", "EDITOR")

použijeme pouze volání:

viper.BindEnv("editor")

bude Viper zpracovávat proměnnou prostředí EDITOR, kterou automaticky namapuje na klíč editor. Toto chování je logické, protože jména proměnných prostředí se většinou zapisují velkými písmeny, zatímco konfigurační parametry v souborech jsou typicky zapsány písmeny malými.

Předchozí příklad má tedy funkčně ekvivalentní podobu:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config9")
        viper.AddConfigPath(".")
 
        viper.BindEnv("editor")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        editor := viper.GetString("editor")
 
        log.Printf("Selected editor: %s\n", editor)
}

17. Mapování proměnných prostředí se zvoleným prefixem

Balíček Viper obsahuje ještě jednu velmi užitečnou funkcionalitu. Ve chvíli, kdy zapíšeme tyto tři řádky:

viper.SetEnvPrefix("XTERM")
viper.BindEnv("locale")
viper.BindEnv("shell")

vytvoří se tři konfigurační parametry nazvané „locale“ a „shell“. Hodnota prvního parametru se přitom získá z proměnné prostředí XTERM_LOCALE a druhá z proměnné prostředí XTERM_SHELL. Jinými slovy – jména parametrů předaná do BindEnv se převedou na velká písmena a připojí se před ně prefix, za nímž je navíc zapsáno podtržítko.

Opět si ukažme příklad:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config9")
        viper.AddConfigPath(".")
 
        viper.SetEnvPrefix("XTERM")
        viper.BindEnv("locale")
        viper.BindEnv("shell")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        log.Printf("Xterm locale: %s\n", viper.GetString("locale"))
        log.Printf("Xterm shell: %s\n", viper.GetString("shell"))
}

Výsledek běhu tohoto příkladu (na mém počítači):

2019/10/30 13:26:10 Reading configuration
2019/10/30 13:26:10 Done
2019/10/30 13:26:10 Xterm locale: en_US.UTF-8
2019/10/30 13:26:10 Xterm shell: /bin/bash

18. Automatické mapování všech proměnných prostředí se zvoleným prefixem

Můžeme však jít ještě dále a nechat si automaticky namapovat všechny proměnné prostředí, které začínají zadaným prefixem:

viper.AutomaticEnv()
viper.SetEnvPrefix("XTERM")

Výše uvedené dva řádky postačují na to, aby se všechny proměnné prostředí stylu XTERM_LOCALE atd. namapovaly na parametry, které ovšem budou mít klíče zapsány malými písmeny a nebudou používat žádný prefix. Namapují se tyto parametry (opět platí pro můj počítač a jeho aktuální konfiguraci):

$ set |grep ^XTERM
 
XTERM_LOCALE=en_US.UTF-8
XTERM_SHELL=/bin/bash
XTERM_VERSION='XTerm(297)'

Samotný příklad se od předchozího příkladu odlišuje pouze v zavolání výše zmíněné funkce AutomaticEnv:

package main
 
import (
        "github.com/spf13/viper"
        "log"
)
 
func main() {
        log.Println("Reading configuration")
 
        viper.SetConfigName("config9")
        viper.AddConfigPath(".")
 
        viper.AutomaticEnv()
        viper.SetEnvPrefix("XTERM")
 
        err := viper.ReadInConfig()
        if err != nil {
                log.Fatalf("Fatal error in config file: %s \n", err)
        }
        log.Println("Done")
 
        log.Printf("Xterm locale: %s\n", viper.GetString("locale"))
        log.Printf("Xterm shell: %s\n", viper.GetString("shell"))
}

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně čtyři megabajty), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 01_viper_init.go inicializace knihovny Viper a načtení konfiguračního souboru https://github.com/tisnik/go-root/blob/master/article40/01_vi­per_init.go
2 02_viper_read_toml.go načtení hodnot uložených v konfiguračních souborech https://github.com/tisnik/go-root/blob/master/article40/02_vi­per_read_toml.go
3 03_viper_check.go zjištění, zda byla určitá hodnota skutečně zapsána https://github.com/tisnik/go-root/blob/master/article40/03_vi­per_check.go
4 04_viper_section.go načtení hierarchické struktura dat (sekce, podsekce) https://github.com/tisnik/go-root/blob/master/article40/04_vi­per_section.go
5 05_toml_array.go načtení seznamů řetězců ze souborů typu TOML https://github.com/tisnik/go-root/blob/master/article40/05_tom­l_array.go
6 06_toml_int_array.go načtení seznamů čísel ze souborů typu TOML https://github.com/tisnik/go-root/blob/master/article40/06_tom­l_int_array.go
7 07_toml_map.go načtení celé sekce do mapy https://github.com/tisnik/go-root/blob/master/article40/07_tom­l_map.go
8 08_data_types.go práce s dalšími datovými typy podporovanými formátem TOML https://github.com/tisnik/go-root/blob/master/article40/08_da­ta_types.go
9 09_viper_read_json.go načtení konfiguračního souboru ve formátu JSON https://github.com/tisnik/go-root/blob/master/article40/09_vi­per_read_json.go
10 10_viper_read_yaml.go načtení konfiguračního souboru ve formátu YAML https://github.com/tisnik/go-root/blob/master/article40/10_vi­per_read_yaml.go
11 11_viper_yaml_section.go práce s formátem YAML se sekcemi https://github.com/tisnik/go-root/blob/master/article40/11_vi­per_yaml_section.go
12 12_env_variables.go načtení konfiguračních parametrů z proměnných prostředí https://github.com/tisnik/go-root/blob/master/article40/12_en­v_variables.go
13 13_env_variables_autoname.go automatická konverze jména proměnné prostředí https://github.com/tisnik/go-root/blob/master/article40/13_en­v_variables_autoname.go
14 14_env_variables_prefix.go mapování proměnných prostředí se zvoleným prefixem https://github.com/tisnik/go-root/blob/master/article40/14_en­v_variables_prefix.go
15 15_env_variables_autobind.go automatické mapování všech proměnných se zvoleným prefixem https://github.com/tisnik/go-root/blob/master/article40/15_en­v_variables_autobind.go

Potřebovat budete i několik konfiguračních souborů:

20. Odkazy na Internetu

  1. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  2. xml.org
    http://www.xml.org/
  3. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  4. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  5. JSON to YAML
    https://www.json2yaml.com/
  6. Data Format Converter
    https://toolkit.site/format.html
  7. Viper na GitHubu
    https://github.com/spf13/viper
  8. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  9. The fantastic ORM library for Golang
    http://gorm.io/
  10. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  11. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  12. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  13. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  14. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  15. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  16. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  17. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  18. Sémantické verzování
    https://semver.org/
  19. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  20. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  21. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  22. Modules
    https://github.com/golang/go/wi­ki/Modules
  23. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  24. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  25. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  26. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  27. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  28. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  29. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  30. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  31. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  32. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  33. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  34. gopls
    https://github.com/golang/go/wi­ki/gopls
  35. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  36. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  37. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  38. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  39. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  40. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  41. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  42. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  43. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  44. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  45. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  46. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  47. Package trace
    https://golang.org/pkg/runtime/trace/
  48. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  49. Command trace
    https://golang.org/cmd/trace/
  50. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  51. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  52. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  53. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  54. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  55. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  56. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  57. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  58. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  59. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  60. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  61. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  62. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  63. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  64. The LLDB Debugger
    http://lldb.llvm.org/
  65. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  66. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  67. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  68. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  69. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  70. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  71. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  72. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  73. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  74. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  75. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  76. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  77. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  78. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  79. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  80. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  81. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  82. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  83. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  84. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  85. go-cron
    https://github.com/rk/go-cron
  86. gocron
    https://github.com/jasonlvhit/gocron
  87. clockwork
    https://github.com/whiteShtef/cloc­kwork
  88. clockwerk
    https://github.com/onatm/clockwerk
  89. JobRunner
    https://github.com/bamzi/jobrunner
  90. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  91. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  92. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  93. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  94. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  95. go-prompt
    https://github.com/c-bata/go-prompt
  96. readline
    https://github.com/chzyer/readline
  97. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  98. go-readline
    https://github.com/fiorix/go-readline
  99. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  100. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  101. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  102. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  103. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  104. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  105. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  106. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  107. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  108. Editline Library (libedit)
    http://thrysoee.dk/editline/
  109. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  110. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  111. WinEditLine
    http://mingweditline.sourceforge.net/
  112. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  113. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  114. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  115. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  116. history(3) – Linux man page
    https://linux.die.net/man/3/history
  117. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  118. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  119. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  120. Balíček ogletest
    https://github.com/jacobsa/ogletest
  121. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  122. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  123. package testing
    https://golang.org/pkg/testing/
  124. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  125. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  126. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  127. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  128. GoConvey
    http://goconvey.co/
  129. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  130. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  131. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  132. package gg
    https://godoc.org/github.com/fo­gleman/gg
  133. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  134. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  135. The Go image package
    https://blog.golang.org/go-image-package
  136. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  137. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  138. YAML
    https://yaml.org/
  139. edn
    https://github.com/edn-format/edn
  140. Smile
    https://github.com/FasterXML/smile-format-specification
  141. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  142. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  143. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  144. Introducing JSON
    http://json.org/
  145. Package json
    https://golang.org/pkg/encoding/json/
  146. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  147. Go by Example: JSON
    https://gobyexample.com/json
  148. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  149. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  150. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  151. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  152. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  153. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  154. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  155. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  156. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  157. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  158. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  159. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  160. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  161. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  162. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  163. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  164. Algorithms to Go
    https://yourbasic.org/
  165. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  166. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  167. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  168. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  169. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  170. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  171. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  172. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  173. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  174. The Go Programming Language (home page)
    https://golang.org/
  175. GoDoc
    https://godoc.org/
  176. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  177. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  178. The Go Programming Language Specification
    https://golang.org/ref/spec
  179. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  180. Package builtin
    https://golang.org/pkg/builtin/
  181. Package fmt
    https://golang.org/pkg/fmt/
  182. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  183. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  184. Learning Go
    https://www.miek.nl/go/
  185. Go Bootcamp
    http://www.golangbootcamp.com/
  186. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  187. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  188. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  189. The Go Blog
    https://blog.golang.org/
  190. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  191. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  192. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  193. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  194. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  195. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  196. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  197. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  198. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  199. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  200. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  201. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  202. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  203. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  204. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  205. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  206. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  207. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  208. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  209. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  210. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  211. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  212. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  213. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  214. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  215. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  216. Go vs. Python
    https://www.peterbe.com/plog/govspy
  217. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  218. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  219. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  220. Go by Example: Slices
    https://gobyexample.com/slices
  221. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  222. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  223. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  224. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  225. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  226. nils In Go
    https://go101.org/article/nil.html
  227. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  228. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  229. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  230. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  231. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  232. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  233. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  234. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  235. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  236. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  237. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  238. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  239. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  240. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  241. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  242. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  243. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  244. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  245. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  246. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  247. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  248. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  249. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  250. Selectors
    https://golang.org/ref/spec#Selectors
  251. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  252. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  253. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  254. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  255. Part 21: Goroutines
    https://golangbot.com/goroutines/
  256. Part 22: Channels
    https://golangbot.com/channels/
  257. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  258. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  259. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  260. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  261. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  262. Control Structures
    https://www.golang-book.com/books/intro/5
  263. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  264. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  265. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  266. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  267. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  268. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  269. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  270. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  271. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  272. Effective Go
    https://golang.org/doc/ef­fective_go.html
  273. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  274. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  275. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  276. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  277. Nils in Go
    https://www.doxsey.net/blog/nils-in-go