Obsah
1. Zpracování konfiguračních souborů v Go s využitím knihovny Viper
2. Konfigurační soubory používající formáty INI, .properties nebo XML
3. Lepší přístup k uložení konfigurace – formáty JSON, YAML a TOML
5. Inicializace knihovny Viper a načtení konfiguračního souboru
6. Načtení hodnot uložených v konfiguračních souborech
7. Zjištění, zda byla určitá hodnota skutečně zapsána
9. Práce se seznamy uloženými v konfiguračních souborech
10. Načtení celé sekce do mapy
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
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
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
[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"/>
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>
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
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
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
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:
Potřebovat budete i několik konfiguračních souborů:
# | Konfigurační soubor | Cesta |
---|---|---|
1 | config1.toml | https://github.com/tisnik/go-root/blob/master/article40/config1.toml |
2 | config2.toml | https://github.com/tisnik/go-root/blob/master/article40/config2.toml |
3 | config3.toml | https://github.com/tisnik/go-root/blob/master/article40/config3.toml |
4 | config4.toml | https://github.com/tisnik/go-root/blob/master/article40/config4.toml |
5 | config5.toml | https://github.com/tisnik/go-root/blob/master/article40/config5.toml |
6 | config6.toml | https://github.com/tisnik/go-root/blob/master/article40/config6.toml |
7 | config7.json | https://github.com/tisnik/go-root/blob/master/article40/config7.json |
8 | config8.yaml | https://github.com/tisnik/go-root/blob/master/article40/config8.yaml |
9 | config9.yaml | https://github.com/tisnik/go-root/blob/master/article40/config9.yaml |
20. Odkazy na Internetu
- Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - 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 - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 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/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 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 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - 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/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - 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 - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - 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/ - 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 - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - 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 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - 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/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go