Hlavní navigace

Monitoring služeb a mikroslužeb psaných v Go nástrojem Prometheus

Pavel Tišnovský

Dnes si ukážeme, jakým způsobem lze zajistit zveřejnění různých metrik službami či mikroslužbami, které jsou naprogramovány v Go. Metriky mohou být zaznamenány a zpracovány systémem Prometheus a vizualizovány pomocí Grafany.

Doba čtení: 46 minut

Sdílet

11. Metrika reprezentující libovolnou číselnou hodnotu

12. Metrika založená na sledování sekvence číselných hodnot

13. Kumulativní histogram

14. Intervaly s rozdílnou šířkou

15. Mapa čítačů a její použití pro sledování počtu přístupů na stránky

16. Výpočet doby trvání vytvoření odpovědi HTTP serveru

17. Komplikovanější příklad – HTTP server poskytující více metrik

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

19. Odkazy na Internetu

1. Monitoring služeb a mikroslužeb psaných v Go nástrojem Prometheus

„Java is 20 years old, mature, and comes with unbeatable tooling and monitoring capabilities. At the very beginning, Java already incorporated microservice concepts with the Jini / JXTA frameworks mixed with no-SQL databases like e.g. JavaSpaces. As often – Java was just 15 years too early. The market was not ready for the technology back then. However, all the design principles from 1999 still do apply today. We don't have re-invent the wheel.“

Ve třicáté sedmé části seriálu o programovacím jazyce Go se seznámíme s některými možnostmi nabízenými balíčkem github.com/prometheus/cli­ent_golang/prometheus/. Již samotný název tohoto balíčku naznačuje, že se jedná o klienta určeného pro nástroj Prometheus, jenž se v současnosti velmi často používá pro sbírání metrik z běžících služeb, mikroslužeb atd. Tyto metriky (metrikou může být počet připojených klientů, aktuálně obsazená kapacita haldy – heapu, atd.) je následně možné filtrovat a analyzovat, přičemž výsledky mohou být vizualizovány například nástrojem Grafana (propojení Prometheus + Grafana je ostatně taktéž velmi časté). Jedno z typických použití Promethea je sledování (mikro)služby nasazené například v clusteru. Taková služba kromě své vlastní funkcionality nabízí jednoduché rozhraní REST API (HTTP/HTTPS) s typicky jediným koncovým bodem nazvaným /metrics. Nástroj Prometheus z tohoto koncového bodu metriky načítá a následně je nakonfigurovaným způsobem zpracovává.

Samozřejmě je možné implementovat nabídku metrik pro výše zmíněný koncový bod /metrics ručně, přesněji řečeno explicitně naprogramovaným handlerem HTTP serveru, ovšem použití klienta github.com/prometheus/cli­ent_golang/prometheus/ je mnohem jednodušší, umožňuje vytvářet histogramy a další složitější mechanismy a navíc je ve světě Go dnes již téměř idiomatické, takže zdrojovým kódům budou dobře rozumět i další vývojáři.

Poznámka: na tomto místě je možná dobré upozornit na fakt, že samotný Prometheus je taktéž naprogramován v jazyku Go, o čemž se ostatně lze snadno přesvědčit z jeho zdrojových kódů.

Obrázek 1: Logo nástroje Prometheus.

2. Nástroj Prometheus

Systém Prometheus používá databázi, do které se ukládají prakticky libovolné (číselné) hodnoty, které jsou opatřeny časovým razítkem, kromě toho i jménem metriky (ta musí být unikátní, navíc by neměla obsahovat mezery) a návěštím (label) umožňujícím podrobnější dělení hodnot, například podle toho, v jakém prostředí je měření prováděno, jakého koncového bodu REST API se měření týká atd. To znamená, že pro zvolenou metriku, popř. pro metriku a návěští je možné získat celou časovou posloupnost s hodnotami, vracet se do minulosti, získat informace pro zvolené časové období apod. Samotné hodnoty jsou interně zpracovávány jako datový typ double/float64 (konkrétní jednotka již záleží na interpretaci dat) a časová razítka mají milisekundovou přesnost, což by mělo být pro účely tohoto nástroje dostačující, už jen z toho důvodu, že samotné pořízení záznamu přes API klienta Promethea má určitou časovou složitost a zpoždění.

Prometheus se od některých podobně koncipovaných nástrojů odlišuje zejména tím, že používá takzvaný PULL model: jednotlivé služby pouze vystavují své metriky na určeném a nakonfigurovaném koncovém bodě REST API (typicky se jedná o koncový bod /metrics zmíněný v úvodní kapitole) a Prometheus sám aktivně tyto metriky čte. To je poměrně důležitá vlastnost, protože zjednodušuje tvorbu samotné aplikace – ta totiž může být pasivní, nemusí se jí v konfiguračním souboru nebo v proměnné prostředí předávat adresa Promethea, nemusí se řešit situace, kdy není Prometheus dostupný atd. Rozdíl mezi PUSH a PULL modelem jsou stručně popsány v článku Go App Monitoring: expvar, Prometheus and StatsD.

Pro nástroj Prometheus v současnosti existují čtyři oficiálně podporovaní klienti:

  1. Go
  2. Java, popř. Scala (a tím pádem i další jazyky nad JVM, včetně Clojure)
  3. Python
  4. Ruby

Dnes se zaměříme na popis klienta určeného pro programovací jazyk Go.

Kromě toho však existuje i velké množství sice oficiálně nepodporovaných, ale většinou plně funkčních klientů určených pro další programovací jazyky:

  1. BASH
  2. C++
  3. Common Lisp
  4. Elixir
  5. Erlang
  6. Haskell
  7. Lua
  8. .NET / C#
  9. Node.js
  10. Perl
  11. PHP
  12. Rust
Poznámka: seznam je seřazen abecedně, ne podle preferencí autora.

3. Dotazovací jazyk PromQL

Důležitou součástí nástroje Prometheus je i PromQL, což je relativně snadno použitelný dotazovací jazyk určený pro získání potřebných metrik, agregaci výsledků přečtených z těchto metrik apod. Můžeme si ostatně uvést příklad jednoduchého dotazu vytvořeného v tomto jazyce, který vrátí časovou posloupnost hodnot trvání přípravy odpovědi na HTTP požadavky (předpokládejme, že jméno této metriky je „http_requests_total“):

http_requests_total

Celkovou dobu trvání všech požadavků a průměrnou dobu vytvoření jednoho požadavku získáme stejně snadno:

sum(http_requests_total)
avg(http_requests_total)

V dotazu ovšem můžeme provést i jemnější dělení, například podle návěští:

http_requests_total{job="prometheus",group="canary"}

V jazyku PromQL je možné využívat například i regulární výrazy, což nám umožňuje získat časy odpovědí na HTTP dotazy typu GET, ovšem pouze pro zvolená prostředí (staging, testing, development):

http_requests_total{environment=~"staging|testing|development",method!="GET"}

Dotazovací jazyk PromQL je primárně určen pro práci s časovými řadami, takže nepřekvapí ani dobrá podpora specifikace časového období, pro které potřebujeme data získat. Výsledky trvání vyřízení HTTP dotazů typu GET za posledních pět minut by se získaly takto:

http_requests_total{job="prometheus"}[5m]

Výsledky za posledních třicet minut, ovšem s rozlišením jedné minuty, přečteme následujícím dotazem:

rate(http_requests_total[5m])[30m:1m]
Poznámka: pěkné shrnutí základních vlastností a možností jazyka PromQL naleznete na stránce PromQL for Humans a PromQL tutorial for beginners and humans. Další možnosti si ukážeme v samostatném článku věnovaném samotnému systému Promethea.

4. Konfigurace dashboardů s výsledky monitoringu

Kromě dotazů zapisovaných v jazyku PromQL je podporován již zmíněný výstup ve formě plně konfigurovatelných grafů, z nichž se posléze vytváří různé dashboardy, které sledují stav celého systému či jeho jednotlivých částí. Pro tento účel se používá Grafana. Pokud chcete vidět, jak může vypadat výstup z kombinace nástrojů Prometheus+Grafana, můžete se podívat například na obrázek na adrese https://prometheus.io/asset­s/grafana_prometheus.png, popř. na celou galerii různých dashboardů na adrese https://duckduckgo.com/?q=gra­fana+board&t=ffab&iax=ima­ges&ia=images.

Poznámka: možnosti Grafany si podrobněji popíšeme v samostatném článku.

Obrázek 2: Logo nástroje Grafana.

Další podporovanou možností výstupu (reportu) představují standardní šablony Go (https://golang.org/pkg/tex­t/template/).

5. Upozornění v případě, že se detekuje problém

Možnost zobrazení záznamů (časových řad s hodnotami) je samozřejmě velmi užitečná, ovšem v praxi mnohdy potřebujeme, aby byli administrátoři nebo tým devops varováni ve chvíli, kdy dojde k určité události, například když klesne hodnota volné RAM, markantně se zvýší čas odezvy serveru atd. Pro tento účel používá systém Prometheus další komponentu nazvanou příhodně Alertmanager, kterou je možné nakonfigurovat takovým způsobem, aby na naprogramované události (či mnohem častěji na jejich souběh) nějakým vhodným způsobem reagovala. Samozřejmě je možné zvolit například poslání zprávy přes připravená rozhraní (Slack, HipChat, včetně běžného e-mailu) nebo lze nakonfigurovat poslání obecnějšího webhooku do prakticky libovolné služby.

I Alertmanager je, podobně jako samotný Prometheus, vyvinut v programovacím jazyce Go – viz též repositář tohoto projektu.

6. Základní konfigurace služby poskytující standardní metriky

Nyní se konečně podíváme na demonstrační příklady ukazující, jakým způsobem je možné metriky určené pro Prometheus nabídnout (vystavit) ze služby, resp. přesněji řečeno z libovolného procesu, vyvinutého v programovacím jazyce Go. Nejdříve je pochopitelně nutné získat samotný balíček, který nám umožní do služby přidat nový koncový bod REST API s metrikami. Instalaci tohoto balíčku zajistíme jednoduše příkazem:

$ go get -v github.com/prometheus/client_golang/prometheus
 
github.com/golang/protobuf (download)
github.com/prometheus/client_model (download)
github.com/prometheus/common (download)
github.com/matttproud/golang_protobuf_extensions (download)
github.com/prometheus/procfs (download)
github.com/beorn7/perks/quantile
github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
github.com/golang/protobuf/proto
github.com/prometheus/common/model
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
github.com/prometheus/procfs
github.com/matttproud/golang_protobuf_extensions/pbutil
github.com/prometheus/client_model/go
github.com/prometheus/client_golang/prometheus/internal
github.com/prometheus/common/expfmt
github.com/prometheus/client_golang/prometheus
Poznámka: jak je z předchozího výpisu patrné, nainstalují se ve skutečnosti i balíčky samotného Promethea.

Aplikace, která chce metriky nabízet, tento balíček nejdříve importuje:

import "github.com/prometheus/client_golang/prometheus/promhttp"

Následně je nutné zaregistrovat handler HTTP serveru, který bude obsluhovat dotazy posílané na koncový bod /metrics:

http.Handle("/metrics", promhttp.Handler())

A nakonec aplikace musí spustit samotný HTTP server, popř. zareagovat na situaci, kdy se server z nějakého důvodu nespustí:

err := http.ListenAndServe(":8080", nil)
if err != nil {
        fmt.Println(err)
        os.Exit(2)
}

Na zmíněném koncovém bodě nyní budou dostupné základní metriky automaticky poskytované klientem Promethea.

Poznámka: základními možnostmi nabízenými balíčkem http jsme se již zabývali v článku Vývoj síťových aplikací v programovacím jazyku Go. Dnes budeme pouze potřebovat spustit HTTP server na určeném portu a zaregistrovat handler či několik handlerů.

7. První varianta služby poskytující standardní metriky

Nyní již máme k dispozici všechny informace potřebné pro to, abychom vytvořili triviální aplikaci, která na koncovém bodě /metrics bude nabízet metriky zpracovatelné (mj. i) nástrojem Prometheus. Úplný zdrojový kód této aplikace vypadá následovně:

package main
 
import (
        "fmt"
        "net/http"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "os"
)
 
func main() {
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Aplikaci přeložíme a spustíme:

$ go run 01_basic_metrics.go

Aplikace by nyní pouze měla vypsat, že se inicializoval HTTP server. Ten naslouchá na portu 8080, takže z dalšího terminálu zkusíme načíst všechny metriky poskytované klientem Promethea. Použijeme přitom standardní nástroj curl:

$ curl localhost:8080/metrics

Na standardní výstup by se po spuštění tohoto příkazu mělo vypsat množství řádků s komentáři i jednotlivými metrikami. Tyto metriky se týkají především samotného běžícího procesu, práce automatického správce paměti atd. Povšimněte si, že se jedná o velmi jednoduchý a současně i relativně snadno parsovatelný formát:

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 7
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.13"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 460480
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 460480
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 2724
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 95
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
# TYPE go_memstats_gc_cpu_fraction gauge
go_memstats_gc_cpu_fraction 0
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 2.240512e+06
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
# TYPE go_memstats_heap_alloc_bytes gauge
go_memstats_heap_alloc_bytes 460480
# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
# TYPE go_memstats_heap_idle_bytes gauge
go_memstats_heap_idle_bytes 6.53312e+07
# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
# TYPE go_memstats_heap_inuse_bytes gauge
go_memstats_heap_inuse_bytes 1.35168e+06
# HELP go_memstats_heap_objects Number of allocated objects.
# TYPE go_memstats_heap_objects gauge
go_memstats_heap_objects 1760
# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
# TYPE go_memstats_heap_released_bytes gauge
go_memstats_heap_released_bytes 6.53312e+07
# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
# TYPE go_memstats_heap_sys_bytes gauge
go_memstats_heap_sys_bytes 6.668288e+07
# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
# TYPE go_memstats_last_gc_time_seconds gauge
go_memstats_last_gc_time_seconds 0
# HELP go_memstats_lookups_total Total number of pointer lookups.
# TYPE go_memstats_lookups_total counter
go_memstats_lookups_total 0
# HELP go_memstats_mallocs_total Total number of mallocs.
# TYPE go_memstats_mallocs_total counter
go_memstats_mallocs_total 1855
# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
# TYPE go_memstats_mcache_inuse_bytes gauge
go_memstats_mcache_inuse_bytes 13888
# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
# TYPE go_memstats_mcache_sys_bytes gauge
go_memstats_mcache_sys_bytes 16384
# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
# TYPE go_memstats_mspan_inuse_bytes gauge
go_memstats_mspan_inuse_bytes 18632
# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
# TYPE go_memstats_mspan_sys_bytes gauge
go_memstats_mspan_sys_bytes 32768
# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
# TYPE go_memstats_next_gc_bytes gauge
go_memstats_next_gc_bytes 4.473924e+06
# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
# TYPE go_memstats_other_sys_bytes gauge
go_memstats_other_sys_bytes 789852
# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
# TYPE go_memstats_stack_inuse_bytes gauge
go_memstats_stack_inuse_bytes 425984
# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
# TYPE go_memstats_stack_sys_bytes gauge
go_memstats_stack_sys_bytes 425984
# HELP go_memstats_sys_bytes Number of bytes obtained from system.
# TYPE go_memstats_sys_bytes gauge
go_memstats_sys_bytes 7.0191104e+07
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 8
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1024
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 10
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 8.06912e+06
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.57052836543e+09
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 5.04848384e+08
# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.
# TYPE process_virtual_memory_max_bytes gauge
process_virtual_memory_max_bytes -1
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 0
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0
Poznámka: samotný formát je popsán na stránce Text-based format.

8. Přidání čítače (counter) do množiny sledovaných metrik

Nyní se pokusíme možnosti předchozí aplikace vylepšit. Přidáme do ní jeden čítač, který se bude postupně každou sekundu zvyšovat. Samotný čítač se vytvoří konstruktorem prometheus.NewCounter, kterému je nutné předat atributy čítače. Mezi základní atributy patří jméno metriky tak, jak ji bude vidět systém Prometheus. Nastavit můžeme i popis či nápovědu k dané metrice. I tuto nápovědu bude možné dále zpracovat:

var counter = prometheus.NewCounter(prometheus.CounterOpts{
        Name: "number_of_ticks",
        Help: "The total number of ticks since the app is started",
})

Čítač, který je představován rozhraním Counter, podporuje pouze dvě metody:

  1. Inc() – zvýšení čítače o jedničku
  2. Add(float64) – zvýšení čítače o zadanou hodnotu (offset), změna musí být větší než nula

Nově upravená aplikace by mohla vypadat následovně:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "net/http"
        "os"
        "time"
)
 
var counter = prometheus.NewCounter(prometheus.CounterOpts{
        Name: "number_of_ticks",
        Help: "The total number of ticks since the app is started",
})
 
func tick() {
        for {
                counter.Inc()
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

9. Registrace nového čítače

Pokud upravenou aplikaci spustíme a pokusíme se nástrojem curl získat hodnotu čítače, nebudeme příliš úspěšní. Je tomu tak z jednoho prostého důvodu – samotné vytvoření čítače ještě neznamená, že hodnota tohoto čítače bude zveřejněna společně s ostatními metrikami. Aby tomu tak bylo, je nutné čítač zaregistrovat, a to například funkcí MustRegister:

func init() {
        prometheus.MustRegister(counter)
}

Povšimněte si, že jsme funkci MustRegister() zavolali z funkce init(), která je zavolána automaticky při inicializaci balíčku.

Nová podoba aplikace, jejíž zdrojový kód naleznete zde, již bude plně funkční:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "net/http"
        "os"
        "time"
)
 
var counter = prometheus.NewCounter(prometheus.CounterOpts{
        Name: "number_of_ticks",
        Help: "The total number of ticks since the app is started",
})
 
func tick() {
        for {
                counter.Inc()
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func init() {
        prometheus.MustRegister(counter)
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Pokud nyní aplikaci spustíme a kombinací nástrojů curl a grep vyfiltrujeme řádky obsahující řetězec „number_of_ticks“, zjistíme, že čítač pracuje bez problémů a jeho hodnota je začleněna do ostatních metrik:

# HELP number_of_ticks The total number of ticks since the app is started
# TYPE number_of_ticks counter
number_of_ticks 54

10. Automatická registrace čítače

Aby se nezapomnělo na registraci nějaké metriky, existuje v balíčku github.com/prometheus/cli­ent_golang/prometheus/pro­mauto několik funkcí, které nejenom že určitou metriku (čítač, měřítko, histogram atd.) vytvoří, ale současně ho i zaregistrují. Při použití tohoto balíčku je možné čítač vytvořit a zaregistrovat takto:

var counter = promauto.NewCounter(prometheus.CounterOpts{
        Name: "number_of_ticks",
        Help: "The total number of ticks since the app is started",
})

Celá aplikace se zjednoduší, neboť se již nemusíme starat o registraci čítače ve funkci init():

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "net/http"
        "os"
        "time"
)
 
var counter = promauto.NewCounter(prometheus.CounterOpts{
        Name: "number_of_ticks",
        Help: "The total number of ticks since the app is started",
})
 
func tick() {
        for {
                counter.Inc()
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Výsledky při běhu budou stejné, jako tomu bylo u předchozí varianty aplikace:

# HELP number_of_ticks The total number of ticks since the app is started
# TYPE number_of_ticks counter
number_of_ticks 100

11. Metrika reprezentující libovolnou číselnou hodnotu

V předchozím textu jsme si řekli, že hodnotu čítače lze pouze zvyšovat, a to metodami Inc() a Add(). Velmi často ovšem musíme zveřejnit i informace o metrice, která může nabývat libovolné hodnoty (tedy může se i snižovat). Taková metrika je reprezentována objektem Gauge se šesti metodami určenými pro změnu hodnoty:

  1. Inc() – zvýšení o jedničku
  2. Dec() – snížení o jedničku
  3. Set(float64) – nastavení na zadanou hodnotu
  4. Add(float64) – změna o zadanou hodnotu
  5. Sub(float64) – změna o zadanou hodnotu
  6. SetToCurrentTime() – nastavení na Unix time (v sekundách)

V dalším ukázkovém příkladu je Gauge použit pro poskytnutí informací o počtu aktuálně běžících gorutin. Tuto informaci získáme snadno s využitím funkce NumGoroutine() z balíčku runtime:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "net/http"
        "os"
        "runtime"
        "time"
)
 
var goroutines = promauto.NewGauge(prometheus.GaugeOpts{
        Name: "number_of_goroutines",
        Help: "Number of goroutines in the process",
})
 
func tick() {
        for {
                goroutines.Set(float64(runtime.NumGoroutine()))
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Výsledek poskytnutý aplikací přes /metrics:

# HELP number_of_goroutines Number of goroutines in the process
# TYPE number_of_goroutines gauge
number_of_goroutines 2

12. Metrika založená na sledování sekvence číselných hodnot

Dalším užitečným typem metriky je metrika představovaná objektem nazvaným Summary. Tento objekt má jedinou metodu Observe(), které lze předat libovolnou číselnou hodnotu. Na základě postupně získávaných hodnot se vypočítají základní statistické informace – počet zaznamenaných hodnot, jejich průměr, kvantily (míra polohy rozdělení pravděpodobnosti měřené veličiny). Další demonstrační příklad bude dosti umělý, protože budeme zaznamenávat náhodná čísla generovaná funkcí rand.Float64() a nikoli reálnou měřenou hodnotu:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "math/rand"
        "net/http"
        "os"
        "time"
)
 
var durations = promauto.NewSummary(prometheus.SummaryOpts{
        Name:       "request_durations",
        Help:       "Durations of requests.",
        Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
})
 
func tick() {
        for {
                v := rand.Float64()
                durations.Observe(v)
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Pokud po určité době (přibližně po čtvrt minutě) přečteme nástrojem curl metriky, můžeme získat následující údaje:

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# HELP request_durations Durations of requests.
# TYPE request_durations summary
request_durations{quantile="0.5"} 0.4246374970712657
request_durations{quantile="0.9"} 0.8136399609900968
request_durations{quantile="0.99"} 0.9405090880450124
request_durations_sum 7.089663510425493
request_durations_count 16

13. Kumulativní histogram

Posledním důležitým typem metriky je kumulativní histogram s volitelným počtem a šířkou intervalů. Histogram je představován objektem typu Histogram, který taktéž implementuje metodu Observe, které lze předat libovolnou číselnou hodnotu. Tato hodnota je do histogramu začleněna a celý histogram (počet hodnot, které padly do daného intervalu a intervalů nižších) je nabízen jako metrika. Podívejme se nejdříve na kód příkladu, posléze si vysvětlíme, proč se jedná o kumulativní histogram:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "math/rand"
        "net/http"
        "os"
        "time"
)
 
var durations = promauto.NewHistogram(prometheus.HistogramOpts{
        Name:    "request_durations",
        Help:    "Durations of requests.",
        Buckets: prometheus.LinearBuckets(0, 0.1, 10),
})
 
func tick() {
        for {
                v := rand.Float64()
                fmt.Println(v)
                durations.Observe(v)
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Zajímavé budou výsledky. Povšimněte si, že se provedlo devatenáct zápisů do histogramu a že samotný histogram obsahuje jedenáct intervalů, přičemž každý interval obsahuje počet hodnot menších, než je mezní hodnota intervalu (tedy nejedná se o o interval „od-do“, ale jen „do“):

# HELP request_durations Durations of requests.
# TYPE request_durations histogram
request_durations_bucket{le="0"} 0
request_durations_bucket{le="0.1"} 2
request_durations_bucket{le="0.2"} 3
request_durations_bucket{le="0.30000000000000004"} 6
request_durations_bucket{le="0.4"} 9
request_durations_bucket{le="0.5"} 12
request_durations_bucket{le="0.6"} 13
request_durations_bucket{le="0.7"} 17
request_durations_bucket{le="0.7999999999999999"} 17
request_durations_bucket{le="0.8999999999999999"} 18
request_durations_bucket{le="+Inf"} 19
request_durations_sum 8.34488419486297
request_durations_count 19

Výsledek po dalších dvanácti měřeních:

# HELP request_durations Durations of requests.
# TYPE request_durations histogram
request_durations_bucket{le="0"} 0
request_durations_bucket{le="0.1"} 2
request_durations_bucket{le="0.2"} 3
request_durations_bucket{le="0.30000000000000004"} 11
request_durations_bucket{le="0.4"} 15
request_durations_bucket{le="0.5"} 18
request_durations_bucket{le="0.6"} 21
request_durations_bucket{le="0.7"} 26
request_durations_bucket{le="0.7999999999999999"} 27
request_durations_bucket{le="0.8999999999999999"} 30
request_durations_bucket{le="+Inf"} 31
request_durations_sum 14.195887244852525
request_durations_count 31

Možná se nyní ptáte, proč se používá kumulativní histogram. Jeho velkou výhodou je fakt, že je možné operativně (podle paměťových aj. nároků) zmenšit počet intervalů, aniž by došlo k degradaci ostatních, resp. předchozích hodnot.

14. Intervaly s rozdílnou šířkou

Typické histogramy mají shodnou šířku všech intervalů (0.1, 0.2, 0.3, 0.4, …), což ovšem nemusí být pro všechny měřené veličiny ideální nastavení. Z tohoto důvodu je možné vytvořit i histogram, v němž se šířka intervalů postupně zvyšuje či naopak snižuje o určité měřítko. Podívejme se na následující příklad:

var durations = promauto.NewHistogram(prometheus.HistogramOpts{
        Name:    "request_durations",
        Help:    "Durations of requests.",
        Buckets: prometheus.ExponentialBuckets(0.01, 2, 10),
})

Výsledkem budou intervaly 0.01, 0.02 (první vynásobený dvěma) atd.:

request_durations_bucket{le="0.01"} 0
request_durations_bucket{le="0.02"} 0
request_durations_bucket{le="0.04"} 0
request_durations_bucket{le="0.08"} 0
request_durations_bucket{le="0.16"} 0
request_durations_bucket{le="0.32"} 0
request_durations_bucket{le="0.64"} 1
request_durations_bucket{le="1.28"} 1
request_durations_bucket{le="2.56"} 1
request_durations_bucket{le="5.12"} 1
request_durations_bucket{le="+Inf"} 1
request_durations_sum 0.6046602879796196
request_durations_count 1

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

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "math/rand"
        "net/http"
        "os"
        "time"
)
 
var durations = promauto.NewHistogram(prometheus.HistogramOpts{
        Name:    "request_durations",
        Help:    "Durations of requests.",
        Buckets: prometheus.ExponentialBuckets(0.01, 2, 10),
})
 
func tick() {
        for {
                v := rand.Float64()
                fmt.Println(v)
                durations.Observe(v)
                time.Sleep(time.Second)
        }
}
 
func recordMetrics() {
        fmt.Println("Starting recording metrics")
        go tick()
}
 
func main() {
        recordMetrics()
        fmt.Println("Initializing HTTP server")
        http.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Další výsledky běhu tohoto příkladu se zveřejněnými metrikami:

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# HELP request_durations Durations of requests.
# TYPE request_durations histogram
request_durations_bucket{le="0.01"} 0
request_durations_bucket{le="0.02"} 0
request_durations_bucket{le="0.04"} 0
request_durations_bucket{le="0.08"} 1
request_durations_bucket{le="0.16"} 2
request_durations_bucket{le="0.32"} 2
request_durations_bucket{le="0.64"} 5
request_durations_bucket{le="1.28"} 8
request_durations_bucket{le="2.56"} 8
request_durations_bucket{le="5.12"} 8
request_durations_bucket{le="+Inf"} 8
request_durations_sum 3.9810604603187447
request_durations_count 8

Výsledek po záznamu 133 náhodných hodnot:

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# HELP request_durations Durations of requests.
# TYPE request_durations histogram
request_durations_bucket{le="0.01"} 3
request_durations_bucket{le="0.02"} 3
request_durations_bucket{le="0.04"} 7
request_durations_bucket{le="0.08"} 11
request_durations_bucket{le="0.16"} 19
request_durations_bucket{le="0.32"} 46
request_durations_bucket{le="0.64"} 88
request_durations_bucket{le="1.28"} 133
request_durations_bucket{le="2.56"} 133
request_durations_bucket{le="5.12"} 133
request_durations_bucket{le="+Inf"} 133
request_durations_sum 65.22517940569107
request_durations_count 133

15. Mapa čítačů a její použití pro sledování počtu přístupů na stránky

Často se setkáme se situací, kdy potřebujeme vytvořit několik čítačů, které sice měří stejnou veličinu, ale pro různé stavy aplikace. Například budeme chtít spočítat přístupy k jednotlivým stránkám, která aplikace vytváří a posílá klientům. Řešením je použít takzvaný vektor čítačů, přičemž každý čítač bude uveden svým jménem (řetězec):

var pageRequests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "page_requests",
        Help: "The total number page/URL requests",
},
        []string{"url"})

Aplikace již bude složitější, neboť se bude jednat o HTTP server s několika koncovými body (včetně /metrics). A pro každý koncový bod se zaznamená přístup následující funkcí (povšimněte si především určení konkrétního čítače):

func countEndpoint(request *http.Request) {
        url := request.URL.String()
        fmt.Printf("Request URL: %s\n", url)
        pageRequests.With(prometheus.Labels{"url": url}).Inc()
}

Funkci budeme volat ze všech handlerů, což v tom nejjednodušším (nikoli nejkratším) případě může vypadat následovně:

func mainEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "Hello world!\n")
}
 
func fooEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "FOO!\n")
}
 
func barEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "BAR!\n")
}

Podívejme se nyní na úplný zdrojový kód této aplikace:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "io"
        "net/http"
        "os"
)
 
var pageRequests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "page_requests",
        Help: "The total number page/URL requests",
},
        []string{"url"})
 
func countEndpoint(request *http.Request) {
        url := request.URL.String()
        fmt.Printf("Request URL: %s\n", url)
        pageRequests.With(prometheus.Labels{"url": url}).Inc()
}
 
func mainEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "Hello world!\n")
}
 
func fooEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "FOO!\n")
}
 
func barEndpoint(writer http.ResponseWriter, request *http.Request) {
        countEndpoint(request)
        io.WriteString(writer, "BAR!\n")
}
 
func main() {
        fmt.Println("Initializing HTTP server")
 
        http.HandleFunc("/", mainEndpoint)
        http.HandleFunc("/foo", fooEndpoint)
        http.HandleFunc("/bar", barEndpoint)
        http.Handle("/metrics", promhttp.Handler())
 
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Aplikaci spustíme a po několika přístupech na jednotlivé stránky si zobrazíme metriky. Mezi nimi nalezneme i čítače stránek:

# HELP page_requests The total number page/URL requests
# TYPE page_requests counter
page_requests{url="/"} 10
page_requests{url="/bar"} 1
page_requests{url="/bardsgdfs"} 1
page_requests{url="/foo"} 2

16. Výpočet doby trvání vytvoření odpovědi HTTP serveru

V dnešním posledním demonstračním příkladu budeme měřit dobu trvání vytváření odpovědi HTTP serveru. Pro jednoduchost se bude měřit jen doba strávená v handleru, ovšem ve skutečnosti je pochopitelně celková doba větší, protože musíme znát i čas odeslání posledního bajtu atd. atd. Ovšem pro základní ukázku bude postačovat jednoduché měření ve stylu:

func countEndpoint(request *http.Request, start time.Time) {
        url := request.URL.String()
        fmt.Printf("Request URL: %s\n", url)
        duration := time.Since(start)
        fmt.Printf("Time to serve the page: %s\n", duration)
 
        // uprava citacu stranek
        pageRequests.With(prometheus.Labels{"url": url}).Inc()
 
        // uprava histogramu
        histogram.With(prometheus.Labels{"url": url}).Observe(float64(duration.Microseconds()))
}
Poznámka: časový interval v mikrosekundách je reprezentován celým číslem, které převedeme na hodnotu float64.

Tuto funkci budeme volat na konci každého handleru a navíc jí budeme muset přidat i čas, kdy se do handleru vstoupilo:

func mainEndpoint(writer http.ResponseWriter, request *http.Request) {
        start := time.Now()
        io.WriteString(writer, "Hello world!\n")
        countEndpoint(request, start)
}

17. Komplikovanější příklad – HTTP server poskytující více metrik

V příkladu budeme používat jak čítače přístupu na jednotlivé stránky, tak i histogram s dobami, které program stráví při generování odpovědí:

var pageRequests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "page_requests",
        Help: "The total number page/URL requests",
}, []string{"url"})
 
var histogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "response_time",
        Help:    "Response time",
        Buckets: prometheus.LinearBuckets(0, 5, 20),
}, []string{"url"})

Úplný zdrojový kód příkladu může vypadat následovně:

package main
 
import (
        "fmt"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/prometheus/client_golang/prometheus/promauto"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "io"
        "net/http"
        "os"
        "time"
)
 
var pageRequests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "page_requests",
        Help: "The total number page/URL requests",
}, []string{"url"})
 
var histogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "response_time",
        Help:    "Response time",
        Buckets: prometheus.LinearBuckets(0, 5, 20),
}, []string{"url"})
 
func countEndpoint(request *http.Request, start time.Time) {
        url := request.URL.String()
        fmt.Printf("Request URL: %s\n", url)
        duration := time.Since(start)
        fmt.Printf("Time to serve the page: %s\n", duration)
 
        // uprava citacu stranek
        pageRequests.With(prometheus.Labels{"url": url}).Inc()
 
        // uprava histogramu
        histogram.With(prometheus.Labels{"url": url}).Observe(float64(duration.Microseconds()))
}
 
func mainEndpoint(writer http.ResponseWriter, request *http.Request) {
        start := time.Now()
        io.WriteString(writer, "Hello world!\n")
        countEndpoint(request, start)
}
 
func fooEndpoint(writer http.ResponseWriter, request *http.Request) {
        start := time.Now()
        countEndpoint(request, start)
        io.WriteString(writer, "FOO!\n")
}
 
func barEndpoint(writer http.ResponseWriter, request *http.Request) {
        start := time.Now()
        io.WriteString(writer, "BAR!\n")
        countEndpoint(request, start)
}
 
func main() {
        fmt.Println("Initializing HTTP server")
 
        http.HandleFunc("/", mainEndpoint)
        http.HandleFunc("/foo", fooEndpoint)
        http.HandleFunc("/bar", barEndpoint)
        http.Handle("/metrics", promhttp.Handler())
 
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(2)
        }
}

Výsledky – počet přístupů na stránku:

# HELP page_requests The total number page/URL requests
# TYPE page_requests counter
page_requests{url="/"} 10
page_requests{url="/bar"} 1
page_requests{url="/foo"} 2

A doby vytváření stránek (resp. přesněji řečeno odpovědí). Histogram je vytvořen pro každou stránku zvlášť, což je přesně stav, který potřebujeme:

# HELP response_time Response time
# TYPE response_time histogram
response_time_bucket{url="/",le="0"} 0
response_time_bucket{url="/",le="5"} 0
response_time_bucket{url="/",le="10"} 0
response_time_bucket{url="/",le="15"} 6
response_time_bucket{url="/",le="20"} 6
response_time_bucket{url="/",le="25"} 6
response_time_bucket{url="/",le="30"} 7
response_time_bucket{url="/",le="35"} 8
response_time_bucket{url="/",le="40"} 8
response_time_bucket{url="/",le="45"} 9
response_time_bucket{url="/",le="50"} 10
response_time_bucket{url="/",le="55"} 10
response_time_bucket{url="/",le="60"} 10
response_time_bucket{url="/",le="65"} 10
response_time_bucket{url="/",le="70"} 10
response_time_bucket{url="/",le="75"} 10
response_time_bucket{url="/",le="80"} 10
response_time_bucket{url="/",le="85"} 10
response_time_bucket{url="/",le="90"} 10
response_time_bucket{url="/",le="95"} 10
response_time_bucket{url="/",le="+Inf"} 10
response_time_sum{url="/"} 228
response_time_count{url="/"} 10
response_time_bucket{url="/bar",le="0"} 0
response_time_bucket{url="/bar",le="5"} 0
response_time_bucket{url="/bar",le="10"} 0
response_time_bucket{url="/bar",le="15"} 0
response_time_bucket{url="/bar",le="20"} 0
response_time_bucket{url="/bar",le="25"} 0
response_time_bucket{url="/bar",le="30"} 1
response_time_bucket{url="/bar",le="35"} 1
response_time_bucket{url="/bar",le="40"} 1
response_time_bucket{url="/bar",le="45"} 1
response_time_bucket{url="/bar",le="50"} 1
response_time_bucket{url="/bar",le="55"} 1
response_time_bucket{url="/bar",le="60"} 1
response_time_bucket{url="/bar",le="65"} 1
response_time_bucket{url="/bar",le="70"} 1
response_time_bucket{url="/bar",le="75"} 1
response_time_bucket{url="/bar",le="80"} 1
response_time_bucket{url="/bar",le="85"} 1
response_time_bucket{url="/bar",le="90"} 1
response_time_bucket{url="/bar",le="95"} 1
response_time_bucket{url="/bar",le="+Inf"} 1
response_time_sum{url="/bar"} 29
response_time_count{url="/bar"} 1
response_time_bucket{url="/foo",le="0"} 0
response_time_bucket{url="/foo",le="5"} 0
response_time_bucket{url="/foo",le="10"} 1
response_time_bucket{url="/foo",le="15"} 2
response_time_bucket{url="/foo",le="20"} 2
response_time_bucket{url="/foo",le="25"} 2
response_time_bucket{url="/foo",le="30"} 2
response_time_bucket{url="/foo",le="35"} 2
response_time_bucket{url="/foo",le="40"} 2
response_time_bucket{url="/foo",le="45"} 2
response_time_bucket{url="/foo",le="50"} 2
response_time_bucket{url="/foo",le="55"} 2
response_time_bucket{url="/foo",le="60"} 2
response_time_bucket{url="/foo",le="65"} 2
response_time_bucket{url="/foo",le="70"} 2
response_time_bucket{url="/foo",le="75"} 2
response_time_bucket{url="/foo",le="80"} 2
response_time_bucket{url="/foo",le="85"} 2
response_time_bucket{url="/foo",le="90"} 2
response_time_bucket{url="/foo",le="95"} 2
response_time_bucket{url="/foo",le="+Inf"} 2
response_time_sum{url="/foo"} 23
response_time_count{url="/foo"} 2

18. 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_basic_metrics.go aplikace, která nabízí základní metriky definované přímo v klientovi Promethea https://github.com/tisnik/go-root/blob/master/article37/01_ba­sic_metrics.go
2 02_not_registered_counter.go čítač (counter), který však není explicitně zaregistrován https://github.com/tisnik/go-root/blob/master/article37/02_not_re­gistered_counter.go
3 03_registered_counter.go čítač (counter), který je adekvátně zaregistrován a bude poskytován s ostatními metrikami https://github.com/tisnik/go-root/blob/master/article37/03_re­gistered_counter.go
4 04_automatic_registration.go automatická registrace čítače při jeho vytvoření https://github.com/tisnik/go-root/blob/master/article37/04_au­tomatic_registration.go
5 05_gauge.go automaticky zaregistrovaná metrika typu gauge https://github.com/tisnik/go-root/blob/master/article37/05_gau­ge.go
6 06_summary.go automaticky zaregistrovaná metrika typu summary a princip jejího použití https://github.com/tisnik/go-root/blob/master/article37/06_sum­mary.go
7 07_histogram.go metrika prezentovaná formou kumulativního histogramu https://github.com/tisnik/go-root/blob/master/article37/07_his­togram.go
8 08_histogram_exp_buckets.go změna intervalů (buckets) v histogramu https://github.com/tisnik/go-root/blob/master/article37/08_his­togram_exp_buckets.go
9 09_counter_vec.go jedna metrika obsahující větší množství pojmenovaných čítačů https://github.com/tisnik/go-root/blob/master/article37/09_cou­nter_vec.go
10 10_histogram_vec.go složitější příklad používající větší množství kumulativních histogramů https://github.com/tisnik/go-root/blob/master/article37/10_his­togram_vec.go

19. Odkazy na Internetu

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