使用 go-metrics 在 Go 應用中增加度量
本文永久鏈接 – https://tonybai.com/2021/07/06/add-metrics-for-go-application-using-go-metrics
Go 語言內置 expvar,基於 expvar 提供的對基礎度量的支持能力,我們可以自定義各種度量(metrics)。但是 expvar 僅僅是提供了最底層的度量定義支持,對於一些複雜的度量場景,第三方或自實現的 metrics 包必不可少。
go-metrics 包是 Go 領域使用較多的是 metrics 包,該包是對 Java 社區依舊十分活躍的 Coda Hale’s Metrics library 的不完全 Go 移植(不得不感慨一下:Java 的生態還真是強大)。因此該包在概念上與 Coda Hale’s Metrics library 是基本保持一致的。go-metrics 包在文檔方面做的還不夠,要理解很多概念性的東西,我們還得回到 Coda Hale’s Metrics library 的項目文檔去挖掘。
go-metrics 這樣的包是純工具類的包,沒有太多 “燒腦” 的地方,只需要會用即可,這篇文章我們就來簡單地看看如何使用 go-metrics 在 Go 應用中增加度量。
1. go-metrics 的結構
go-metrics 在度量指標組織上採用了與 Coda Hale’s Metrics library 相同的結構,即使用 Metrics Registry(Metrics 註冊表)。Metrics 註冊表是一個度量指標的集合:
┌─────────────┐
│ │
┌──────┤ metric1 │
│ │ │
│ └─────────────┘
│
│
┌─────────────────┐ │ ┌─────────────┐
│ ├───┘ │ │
│ │ │ metric2 │
│ Registry ├──────────┤ │
│ │ └─────────────┘
│ ├───────┐
│ │ │
└──────────────┬──┘ │ ┌─────────────┐
│ │ │ │
│ └──┤ metric3 │
│ │ │
│ └─────────────┘
│ ... ...
│ ┌─────────────┐
│ │ │
└─────────────┤ metricN │
│ │
└─────────────┘
go-metrics 包將 Metrics 註冊表的行爲定義爲了一個接口類型:
// 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()
}
並提供了一個 Registry 的標準實現類型 StandardRegistry:
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
type StandardRegistry struct {
metrics map[string]interface{}
mutex sync.RWMutex
}
我們看到 StandardRegistry 使用 map 結構來組織 metrics。我們可以通過 NewRegistry 函數創建了一個基於 StandardRegistry 的 Registry 實例:
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
func NewRegistry() Registry {
return &StandardRegistry{metrics: make(map[string]interface{})}
}
和標準庫的 flag 或 log 包的設計方式類似,go-metrics 包也在包層面上提供了默認的 StandardRegistry 實例:DefaultRegistry,這樣大多數情況直接使用 DefaultRegistry 實例即可滿足你的需求:
// https://github.com/rcrowley/go-metrics/blob/master/registry.go
var DefaultRegistry Registry = NewRegistry()
一旦有了默認 Registry 實例,我們通常使用下面 goroutine 併發安全的包級函數 GetOrRegister 來註冊或獲取某個度量指標:
// 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 的度量類型
go-metrics 繼承了其前身 Coda Hale’s Metrics library 所支持的幾種基本的度量類型,它們是 Gauges、Counters、Histograms、Meters 和 Timers。下面我們就針對這幾種基本度量類型逐一說明一下其含義和使用方法。
1) Gauge
Gauge 是對一個數值的即時測量值,其反映一個值的瞬時快照,比如我們要度量當前隊列中待發送消息數量、當前應用程序啓動的 goroutine 數量,都可以用 Gauge 這種度量類型實現。
下面的例子使用一個 Gauge 度量類型度量程序當前啓動的 goroutine 數量:
// 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)
}
啓動該程序,並用 hey 工具發起 http 請求,我們看到如下輸出:
$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
... ...
go-metrics 包提供了將 Registry 中的度量指標格式化輸出的接口,我們可以使用該接口將指標情況輸出出來,而無需自行輸出 log,比如上面例子可以改造爲下面這樣:
// 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)
}
同樣方式運行上面 gauge1.log:
$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
... ...
go-metrics 包的 Log 函數必須放在一個單獨的 goroutine 中執行,否則它將阻塞調用它的 goroutine 的繼續執行。但 Log 函數也是 goroutine 安全的,其每次輸出度量值時其實輸出的都是 Registry 中各個度量值的 “快照副本”:
// 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
}
對於 Gauge 這類的季世志度量,就像上面代碼那樣,我們都是通過 Update 直接設置其值的。
2) Counter
Counter 顧名思義計數器!和 Gauge 相比,其提供了指標增減方法 Inc 和 Dec,如下面代碼:
// https://github.com/rcrowley/go-metrics/blob/master/counter.go
type Counter interface {
Clear()
Count() int64
Dec(int64)
Inc(int64)
Snapshot() Counter
}
計數是日常使用較多的度量場景,比如一個服務處理的請求次數就十分適合用計數這個度量指標,下面這段代碼演示的就是這一場景:
// 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)
}
在這段代碼中,我們每收到一個 http request 就在其對應的處理函數中利用 Counter 的 Inc 方法增加計數,運行上述代碼:
$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 這個類型用於測量一組事件發生的速度,比如:web 服務的平均處理性能 (條 / 秒),除了平均值,go-metrics 的 Meter 默認還提供 1 分鐘、5 分鐘和 15 分鐘時間段的平均速度,和 top 命令中的 load average 輸出的一分鐘、五分鐘、以及十五分鐘的系統平均負載類似。
下面就是一個用 Meter 來測量 web 服務處理性能的例子:
// 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)
}
我們用 hey 給該 web server“施壓” 並查看 Meter 度量指標的輸出結果:
$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
如果使用 Meter 度量服務的最佳性能值,那麼需要有持續穩定的 “施壓”,待 1、5、15 分鐘速率穩定後,這時的值纔有意義。Meter 的最後一項 mean rate 是平均值,即服務啓動後處理請求的總量與程序運行時間的比值。
4) Histogram
Histogram 是直方圖,與概率統計學上直方圖的概念類似,go-metrics 中的 Histogram 也是用來統計一組數據的統計學分佈情況的。除了最小值 (min)、最大值(max)、平均值(mean) 等,它還測量中位數(median)、第 75、90、95、98、99 和 99.9 百分位數。
直方圖可以用來度量事件發生的數據分佈情況,比如:服務器處理請求時長的數據分佈情況,下面就是這樣一個例子:
// 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)
}
在上面這個例子中,我們使用一個隨機值來模擬服務處理 http 請求的時間。Histogram 需要一個採樣算法,go-metrics 內置了 ExpDecaySample 採樣。運行上述示例,並使用 hey 模擬客戶端請求,我們得到如下輸出:
$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
... ...
Histogram 度量輸出的值包括 min、max、mean(平均數)、median(中位數)、75、95、99、99.9 百分位數上的度量結果。
5) Timer
最後我們來介紹 Timer 這個度量類型。大家千萬別被這度量類型的名稱所誤導,這並不是一個定時器。
Timer 是 go-metrics 定義的一個抽象度量類型,它可以理解爲 Histogram 和 Meter 的 “合體”,即既度量一段代碼的執行頻率(rate), 又給出這段代碼執行時間的數據分佈。這一點從 Timer 的實現亦可以看出來:
// 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(),
}
}
我們看到一個 StandardTimer 是由 histogram 和 meter 組成的。 我們還是以上面的 http server 服務爲例,我們這次用 Timer 來度量:
// 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)
}
大家可以看到在這裏我們同樣用隨機數模擬請求的處理時間並傳給 Timer 的 Update 方法。運行這段代碼並用 hey 壓測:
$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
我們看到 Timer 度量的輸出也的確是 Histogram 和 Meter 的聯合體!
3. 小結
通過 go-metrics 包,我們可以很方便地爲一個 Go 應用添加度量指標,go-metrics 提供的 meter、histogram 可以覆蓋 Go 應用基本性能指標需求(吞吐性能、延遲數據分佈等)。go-metrics 還支持各種指標值導出的,只是這裏沒有提及,大家可以到 go-metrics 官網瞭解詳情。
本文涉及的源碼可以在這裏下載 – https://github.com/bigwhite/experiments/tree/master/go-metrics
我的聯繫方式:
- 微博:https://weibo.com/bigwhite20xx
- 微信公衆號:iamtonybai
- 博客:tonybai.com
- github: https://github.com/bigwhite
- “Gopher 部落” 知識星球:https://public.zsxq.com/groups/51284458844544
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://tonybai.com/2021/07/06/add-metrics-for-go-application-using-go-metrics/