go-metrics

The Go language has a built-in expvar, and we can customize various metrics based on the support for basic metrics provided by expvar. But expvar only provides the lowest level of metric definition support, for some complex metric scenarios, third-party or self-implemented metrics packages are essential.

The go-metrics package is the most used metrics package in the Go domain, and it is an incomplete Go port of Coda Hale’s Metrics library, which is still very active in the Java community (I have to sigh: the Java ecosystem is really powerful). So the package is conceptually consistent with Coda Hale’s Metrics library.

The go-metrics package does not do enough in terms of documentation, and to understand many conceptual things, we have to go back to the project documentation of Coda Hale’s Metrics library to dig into it.

A package like go-metrics is a purely tool-based package, there is not much “brainstorming” involved, you just need to be able to use it.

1. structure of go-metrics

go-metrics uses the same structure for metrics organization as Coda Hale’s Metrics library, i.e., it uses a Metrics Registry. A Metrics Registry is a collection of metrics.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                             ┌─────────────┐
                             │             │
                      ┌──────┤    metric1  │
                      │      │             │
                      │      └─────────────┘
┌─────────────────┐   │      ┌─────────────┐
│                 ├───┘      │             │
│                 │          │    metric2  │
│     Registry    ├──────────┤             │
│                 │          └─────────────┘
│                 ├───────┐
│                 │       │
└──────────────┬──┘       │  ┌─────────────┐
               │          │  │             │
               │          └──┤    metric3  │
               │             │             │
               │             └─────────────┘
               │                   ... ...
               │             ┌─────────────┐
               │             │             │
               └─────────────┤    metricN  │
                             │             │
                             └─────────────┘

The go-metrics package defines the behavior of the Metrics registry as an interface type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
type Registry interface {

    // Call the given function for each registered metric.
    Each(func(string, interface{}))

    // Get the metric by the given name or nil if none is registered.
    Get(string) interface{}

    // GetAll metrics in the Registry.
    GetAll() map[string]map[string]interface{}

    // Gets an existing metric or registers the given one.
    // The interface can be the metric to register if not found in registry,
    // or a function returning the metric for lazy instantiation.
    GetOrRegister(string, interface{}) interface{}

    // Register the given metric under the given name.
    Register(string, interface{}) error

    // Run all registered healthchecks.
    RunHealthchecks()

    // Unregister the metric with the given name.
    Unregister(string)

    // Unregister all metrics.  (Mostly for testing.)
    UnregisterAll()
}

and provides a standard implementation of Registry of the type StandardRegistry.

1
2
3
4
5
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
type StandardRegistry struct {
    metrics map[string]interface{}
    mutex   sync.RWMutex
}

We see that StandardRegistry uses a map structure to organize metrics, and we can create an instance of a Registry based on StandardRegistry with the NewRegistry function.

1
2
3
4
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
func NewRegistry() Registry {
    return &StandardRegistry{metrics: make(map[string]interface{})}
}

Similar to the way the standard library flag or log packages are designed, the go-metrics package provides a default StandardRegistry instance at the package level: DefaultRegistry, so that most cases can directly use the DefaultRegistry instance to meet your needs.

1
2
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
var DefaultRegistry Registry = NewRegistry()

Once we have a default Registry instance, we typically use the following goroutine concurrency-safe package-level function GetOrRegister to register or obtain a metric.

1
2
3
4
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
func GetOrRegister(name string, i interface{}) interface{} {
    return DefaultRegistry.GetOrRegister(name, i)
}

2. go-metrics’ metric types

go-metrics inherits several basic metric types supported by its predecessor Coda Hale’s Metrics library, namely Gauges, Counters, Histograms, Meters and Timers. We will explain the meaning and usage of each of these basic metric types.

1) Gauge

Gauge is an instantaneous measurement of a value, which reflects a snapshot of a value. For example, if we want to measure the number of messages to be sent in the current queue or the number of goroutines started by the current application, we can use Gauge as a metric type.

The following example uses a Gauge metric type to measure the number of goroutines currently started by the application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// gauge.go
package main

import (
    "fmt"
    "net/http"
    "runtime"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    g := metrics.NewGauge()
    metrics.GetOrRegister("goroutines.now", g)
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    })

    go func() {
        t := time.NewTicker(time.Second)
        for {
            select {
            case <-t.C:
                c := runtime.NumGoroutine()
                g.Update(int64(c))
                fmt.Println("goroutines now =", g.Value())
            }
        }
    }()
    http.ListenAndServe(":8080", nil)
}

Starting the program and making an http request with the hey tool, we see the following output.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$hey -c 5 -n 1000000 -m GET http://127.0.0.1:8080

$go run gauge.go
goroutines now = 9
goroutines now = 10
goroutines now = 7
goroutines now = 8
goroutines now = 7
goroutines now = 7
... ...

The go-metrics package provides an interface to format the metrics in the Registry for output, so we can use this interface to output the metrics without having to output the logs ourselves, for example, the above example can be transformed into the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// gauge1.go
package main

import (
    "log"
    "net/http"
    "runtime"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    g := metrics.NewGauge()
    metrics.GetOrRegister("goroutines.now", g)
    go metrics.Log(metrics.DefaultRegistry, time.Second, log.Default())
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    })

    go func() {
        t := time.NewTicker(time.Second)
        for {
            select {
            case <-t.C:
                c := runtime.NumGoroutine()
                g.Update(int64(c))
            }
        }
    }()
    http.ListenAndServe(":8080", nil)
}

Run the above gauge1.log in the same way.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$go run gauge1.go
2021/07/04 09:42:58 gauge goroutines.now
2021/07/04 09:42:58   value:              10
2021/07/04 09:42:59 gauge goroutines.now
2021/07/04 09:42:59   value:               9
2021/07/04 09:43:00 gauge goroutines.now
2021/07/04 09:43:00   value:               9
2021/07/04 09:43:01 gauge goroutines.now
2021/07/04 09:43:01   value:              10
... ...

The Log function of the go-metrics package must be executed in a separate goroutine, otherwise it will block the continuation of the goroutine that called it. However, the Log function is also goroutine-safe, and each time it outputs a metric, it actually outputs a “snapshot copy” of each metric in the Registry.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
func (r *StandardRegistry) Each(f func(string, interface{})) {
    metrics := r.registered()
    for i := range metrics {
        kv := &metrics[i]
        f(kv.name, kv.value)
    }
}

func (r *StandardRegistry) registered() []metricKV {
    r.mutex.RLock()
    defer r.mutex.RUnlock()
    metrics := make([]metricKV, 0, len(r.metrics))
    for name, i := range r.metrics {
        metrics = append(metrics, metricKV{
            name:  name,
            value: i,
        })
    }
    return metrics
}

For Gauge, as in the code above, we set its value directly by Update.

2) Counter

Counter as the name implies Counter! Compared to Gauge, it provides the methods Inc and Dec to increase or decrease the metric, as in the following code.

1
2
3
4
5
6
7
8
// https://github.com/rcrowley/go-metrics/blob/master/counter.go
type Counter interface {
    Clear()
    Count() int64
    Dec(int64)
    Inc(int64)
    Snapshot() Counter
}

Counting is a metric that is used more often in everyday scenarios. For example, the number of requests handled by a service is a very suitable metric for counting, as demonstrated by the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// counter.go

package main

import (
    "log"
    "net/http"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    c := metrics.NewCounter()
    metrics.GetOrRegister("total.requests", c)
    go metrics.Log(metrics.DefaultRegistry, time.Second, log.Default())
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        c.Inc(1)
    })

    http.ListenAndServe(":8080", nil)
}

In this code, we use the Inc method of Counter to increase the count for each http request received in its corresponding handler function, and run the above code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$go run counter.go
2021/07/04 10:29:03 counter total.requests
... ...
2021/07/04 10:29:06 counter total.requests
2021/07/04 10:29:06   count:               0
2021/07/04 10:29:07 counter total.requests
2021/07/04 10:29:07   count:           33890
2021/07/04 10:29:08 counter total.requests
2021/07/04 10:29:08   count:           80160
2021/07/04 10:29:09 counter total.requests
2021/07/04 10:29:09   count:          124855
2021/07/04 10:29:10 counter total.requests
2021/07/04 10:29:10   count:          172077
2021/07/04 10:29:11 counter total.requests
2021/07/04 10:29:11   count:          218466
2021/07/04 10:29:12 counter total.requests
2021/07/04 10:29:12   count:          265476
2021/07/04 10:29:13 counter total.requests
2021/07/04 10:29:13   count:          309153
... ...

3) Meter

Meter is used to measure the speed of a set of events, e.g. the average processing performance of a web service (bars/sec). In addition to the average, go-metrics’ Meter also provides the average speed for 1 minute, 5 minutes and 15 minutes by default, similar to the load average output of the top command for one minute, five minutes and 15 minutes. similar to the one-minute, five-minute, and 15-minute system load averages output by the top command.

The following is an example of using Meters to measure the processing performance of a web service.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// meter.go 

package main

import (
    "log"
    "net/http"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    m := metrics.NewMeter()
    metrics.GetOrRegister("rate.requests", m)
    go metrics.Log(metrics.DefaultRegistry, time.Second, log.Default())
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        m.Mark(1)
    })
    http.ListenAndServe(":8080", nil)
}

We use hey to “pressure” the web server and look at the output of the Meter metric.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
$hey -c 5 -n 1000000 -m GET http://127.0.0.1:8080

$go run meter.go
2021/07/04 10:55:59 meter rate.requests
2021/07/04 10:55:59   count:               0
2021/07/04 10:55:59   1-min rate:          0.00
2021/07/04 10:55:59   5-min rate:          0.00
2021/07/04 10:55:59   15-min rate:         0.00
2021/07/04 10:55:59   mean rate:           0.00
2021/07/04 10:56:00 meter rate.requests
2021/07/04 10:56:00   count:               0
2021/07/04 10:56:00   1-min rate:          0.00
2021/07/04 10:56:00   5-min rate:          0.00
2021/07/04 10:56:00   15-min rate:         0.00
2021/07/04 10:56:00   mean rate:           0.00
2021/07/04 10:56:01 meter rate.requests
2021/07/04 10:56:01   count:            8155
2021/07/04 10:56:01   1-min rate:          0.00
2021/07/04 10:56:01   5-min rate:          0.00
2021/07/04 10:56:01   15-min rate:         0.00
2021/07/04 10:56:01   mean rate:        2718.27
2021/07/04 10:56:02 meter rate.requests
2021/07/04 10:56:02   count:           50937
2021/07/04 10:56:02   1-min rate:          0.00
2021/07/04 10:56:02   5-min rate:          0.00
2021/07/04 10:56:02   15-min rate:         0.00
2021/07/04 10:56:02   mean rate:       12734.04
2021/07/04 10:56:03 meter rate.requests
2021/07/04 10:56:03   count:           96129
2021/07/04 10:56:03   1-min rate:      19225.00
2021/07/04 10:56:03   5-min rate:      19225.00
2021/07/04 10:56:03   15-min rate:     19225.00
2021/07/04 10:56:03   mean rate:       19225.54
2021/07/04 10:56:04 meter rate.requests
2021/07/04 10:56:04   count:          141076
2021/07/04 10:56:04   1-min rate:      19225.00
2021/07/04 10:56:04   5-min rate:      19225.00
2021/07/04 10:56:04   15-min rate:     19225.00
2021/07/04 10:56:04   mean rate:       23512.40
2021/07/04 10:56:05 meter rate.requests
2021/07/04 10:56:05   count:          187733
2021/07/04 10:56:05   1-min rate:      19225.00
2021/07/04 10:56:05   5-min rate:      19225.00
2021/07/04 10:56:05   15-min rate:     19225.00
2021/07/04 10:56:05   mean rate:       26818.71
2021/07/04 10:56:06 meter rate.requests
2021/07/04 10:56:06   count:          234874
2021/07/04 10:56:06   1-min rate:      19225.00
2021/07/04 10:56:06   5-min rate:      19225.00
2021/07/04 10:56:06   15-min rate:     19225.00
2021/07/04 10:56:06   mean rate:       29358.98
2021/07/04 10:56:07 meter rate.requests
2021/07/04 10:56:07   count:          279201
2021/07/04 10:56:07   1-min rate:      19225.00
2021/07/04 10:56:07   5-min rate:      19225.00
2021/07/04 10:56:07   15-min rate:     19225.00
2021/07/04 10:56:07   mean rate:       31022.05
2021/07/04 10:56:08 meter rate.requests
2021/07/04 10:56:08   count:          321704
2021/07/04 10:56:08   1-min rate:      21295.03
2021/07/04 10:56:08   5-min rate:      19652.92
2021/07/04 10:56:08   15-min rate:     19368.43
2021/07/04 10:56:08   mean rate:       32170.20
2021/07/04 10:56:09 meter rate.requests
2021/07/04 10:56:09   count:          362403
2021/07/04 10:56:09   1-min rate:      21295.03
2021/07/04 10:56:09   5-min rate:      19652.92
2021/07/04 10:56:09   15-min rate:     19368.43
2021/07/04 10:56:09   mean rate:       32945.48
2021/07/04 10:56:10 meter rate.requests
2021/07/04 10:56:10   count:          401442
2021/07/04 10:56:10   1-min rate:      21295.03
2021/07/04 10:56:10   5-min rate:      19652.92
2021/07/04 10:56:10   15-min rate:     19368.43
2021/07/04 10:56:10   mean rate:       33453.34
2021/07/04 10:56:11 meter rate.requests
2021/07/04 10:56:11   count:          440905
2021/07/04 10:56:11   1-min rate:      21295.03
2021/07/04 10:56:11   5-min rate:      19652.92
2021/07/04 10:56:11   15-min rate:     19368.43
2021/07/04 10:56:11   mean rate:       33915.67
2021/07/04 10:56:12 meter rate.requests
2021/07/04 10:56:12   count:          479301
2021/07/04 10:56:12   1-min rate:      21295.03
2021/07/04 10:56:12   5-min rate:      19652.92
2021/07/04 10:56:12   15-min rate:     19368.43
2021/07/04 10:56:12   mean rate:       34235.60
2021/07/04 10:56:13 meter rate.requests
2021/07/04 10:56:13   count:          518843
2021/07/04 10:56:13   1-min rate:      22744.85
2021/07/04 10:56:13   5-min rate:      19979.77
2021/07/04 10:56:13   15-min rate:     19479.57
2021/07/04 10:56:13   mean rate:       34589.43
2021/07/04 10:56:14 meter rate.requests
2021/07/04 10:56:14   count:          560260
2021/07/04 10:56:14   1-min rate:      22744.85
2021/07/04 10:56:14   5-min rate:      19979.77
2021/07/04 10:56:14   15-min rate:     19479.57
2021/07/04 10:56:14   mean rate:       35016.17

The last measure of the Meter is the mean rate, which is the ratio of the total number of requests processed to the program’s running time after the service is started.

4) Histogram

Histogram is a histogram, similar to the concept of histogram in probability statistics, the Histogram in go-metrics is also used to count the statistical distribution of a set of data. In addition to the minimum (min), maximum (max), and mean (mean), it also measures the median, 75th, 90th, 95th, 98th, 99th, and 99.9th percentiles.

The histogram can be used to measure the distribution of data on the occurrence of events, for example, the distribution of data on the length of requests processed by the server, an example of which is the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// histogram.go
package main

import (
    "log"
    "math/rand"
    "net/http"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    s := metrics.NewExpDecaySample(1028, 0.015)
    h := metrics.NewHistogram(s)
    metrics.GetOrRegister("latency.response", h)
    go metrics.Log(metrics.DefaultRegistry, time.Second, log.Default())
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        i := rand.Intn(10)
        h.Update(int64(time.Microsecond * time.Duration(i)))
    })
    http.ListenAndServe(":8080", nil)
}

In the above example, we use a random value to simulate the service processing time for http requests. histogram requires a sampling algorithm and go-metrics has built-in ExpDecaySample sampling. Running the above example and using hey to simulate a client request, we get the following output.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$go run histogram.go
2021/07/04 11:31:54 histogram latency.response
2021/07/04 11:31:54   count:               0
2021/07/04 11:31:54   min:                 0
2021/07/04 11:31:54   max:                 0
2021/07/04 11:31:54   mean:                0.00
2021/07/04 11:31:54   stddev:              0.00
2021/07/04 11:31:54   median:              0.00
2021/07/04 11:31:54   75%:                 0.00
2021/07/04 11:31:54   95%:                 0.00
2021/07/04 11:31:54   99%:                 0.00
2021/07/04 11:31:54   99.9%:               0.00
2021/07/04 11:31:55   99.9%:               0.00
... ...
2021/07/04 11:31:59 histogram latency.response
2021/07/04 11:31:59   count:           33244
2021/07/04 11:31:59   min:                 0
2021/07/04 11:31:59   max:              9000
2021/07/04 11:31:59   mean:             4457.20
2021/07/04 11:31:59   stddev:           2793.67
2021/07/04 11:31:59   median:           4000.00
2021/07/04 11:31:59   75%:              7000.00
2021/07/04 11:31:59   95%:              9000.00
2021/07/04 11:31:59   99%:              9000.00
2021/07/04 11:31:59   99.9%:            9000.00
2021/07/04 11:32:00 histogram latency.response
2021/07/04 11:32:00   count:           78970
2021/07/04 11:32:00   min:                 0
2021/07/04 11:32:00   max:              9000
2021/07/04 11:32:00   mean:             4465.95
2021/07/04 11:32:00   stddev:           2842.12
2021/07/04 11:32:00   median:           4000.00
2021/07/04 11:32:00   75%:              7000.00
2021/07/04 11:32:00   95%:              9000.00
2021/07/04 11:32:00   99%:              9000.00
2021/07/04 11:32:00   99.9%:            9000.00
2021/07/04 11:32:01 histogram latency.response
2021/07/04 11:32:01   count:          124573
2021/07/04 11:32:01   min:                 0
2021/07/04 11:32:01   max:              9000
2021/07/04 11:32:01   mean:             4459.14
2021/07/04 11:32:01   stddev:           2820.38
2021/07/04 11:32:01   median:           4000.00
2021/07/04 11:32:01   75%:              7000.00
2021/07/04 11:32:01   95%:              9000.00
2021/07/04 11:32:01   99%:              9000.00
2021/07/04 11:32:01   99.9%:            9000.00
... ...

The Histogram metric output includes values for min, max, mean, median, 75th, 95th, 99th, and 99.9th percentile metric results.

5) Timer

Finally, let’s introduce Timer as a metric type. Don’t be misled by the name of this metric type, it is not a timer.

Timer is an abstract metric type defined by go-metrics, which can be understood as an “amalgamation” of Histogram and Meter, i.e., it measures the execution frequency (rate) of a piece of code, and gives the data distribution of the execution time of this piece of code. This can also be seen in the Timer implementation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// https://github.com/rcrowley/go-metrics/blob/master/timer.go

func NewTimer() Timer {
    if UseNilMetrics {
        return NilTimer{}
    }
    return &StandardTimer{
        histogram: NewHistogram(NewExpDecaySample(1028, 0.015)),
        meter:     NewMeter(),
    }
}

We see that a StandardTimer is composed of a histogram and a meter. Let’s use the http server service above as an example, and we’ll use the Timer metric this time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// timer.go

package main

import (
    "log"
    "math/rand"
    "net/http"
    "time"

    "github.com/rcrowley/go-metrics"
)

func main() {
    m := metrics.NewTimer()
    metrics.GetOrRegister("timer.requests", m)
    go metrics.Log(metrics.DefaultRegistry, time.Second, log.Default())
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        i := rand.Intn(10)
        m.Update(time.Microsecond * time.Duration(i))
    })
    http.ListenAndServe(":8080", nil)
}

As you can see here we also simulate the processing time of the request with a random number and pass it to Timer’s Update method. Run this code and stress test it with hey.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
go run timer.go
2021/07/04 17:13:47 timer timer.requests
2021/07/04 17:13:47   count:           13750
2021/07/04 17:13:47   min:                 0.00ns
2021/07/04 17:13:47   max:              9000.00ns
2021/07/04 17:13:47   mean:             4406.61ns
2021/07/04 17:13:47   stddev:           2785.11ns
2021/07/04 17:13:47   median:           4000.00ns
2021/07/04 17:13:47   75%:              7000.00ns
2021/07/04 17:13:47   95%:              9000.00ns
2021/07/04 17:13:47   99%:              9000.00ns
2021/07/04 17:13:47   99.9%:            9000.00ns
2021/07/04 17:13:47   1-min rate:          0.00
2021/07/04 17:13:47   5-min rate:          0.00
2021/07/04 17:13:47   15-min rate:         0.00
2021/07/04 17:13:47   mean rate:       13748.57
2021/07/04 17:13:48 timer timer.requests
2021/07/04 17:13:48   count:           56584
2021/07/04 17:13:48   min:                 0.00ns
2021/07/04 17:13:48   max:              9000.00ns
2021/07/04 17:13:48   mean:             4442.61ns
2021/07/04 17:13:48   stddev:           2895.66ns
2021/07/04 17:13:48   median:           4000.00ns
2021/07/04 17:13:48   75%:              7000.00ns
2021/07/04 17:13:48   95%:              9000.00ns
2021/07/04 17:13:48   99%:              9000.00ns
2021/07/04 17:13:48   99.9%:            9000.00ns
2021/07/04 17:13:48   1-min rate:          0.00
2021/07/04 17:13:48   5-min rate:          0.00
2021/07/04 17:13:48   15-min rate:         0.00
2021/07/04 17:13:48   mean rate:       28289.23
2021/07/04 17:13:49 timer timer.requests
2021/07/04 17:13:49   count:          102426
2021/07/04 17:13:49   min:                 0.00ns
2021/07/04 17:13:49   max:              9000.00ns
2021/07/04 17:13:49   mean:             4436.77ns
2021/07/04 17:13:49   stddev:           2892.85ns
2021/07/04 17:13:49   median:           4000.00ns
2021/07/04 17:13:49   75%:              7000.00ns
2021/07/04 17:13:49   95%:              9000.00ns
2021/07/04 17:13:49   99%:              9000.00ns
2021/07/04 17:13:49   99.9%:            9000.00ns
2021/07/04 17:13:49   1-min rate:          0.00
2021/07/04 17:13:49   5-min rate:          0.00
2021/07/04 17:13:49   15-min rate:         0.00
2021/07/04 17:13:49   mean rate:       34140.68

We see that the output of the Timer metric is indeed also the union of Histogram and Meter!

3. Summary

With the go-metrics package, we can easily add metrics to a Go application. go-metrics provides meters and histogram to cover the basic performance metrics needs of Go applications (throughput performance, latency data distribution, etc.). go-metrics also supports the export of various metric values, but they are not mentioned here, so you can You can go to the go-metrics website for details.

The source code involved in this article can be downloaded from here