这里填写目录标题
golang application metrics
prometheus
Docs: https://prometheus.io/docs/guides/go-application/
Adding your own metrics:
package main
import (
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func recordMetrics() {
go func() {
for {
opsProcessed.Inc()
time.Sleep(2 * time.Second)
}
}()
}
var (
opsProcessed = promauto.NewCounter(prometheus.CounterOpts{
Name: "myapp_processed_ops_total",
Help: "The total number of processed events",
})
)
func main() {
recordMetrics()
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":2112", nil)
}
runtime/metrics
Docs: https://pkg.go.dev/runtime/metrics
ReadingAllMetrics):
package main
import (
"fmt"
"runtime/metrics"
)
func main() {
// Get descriptions for all supported metrics.
descs := metrics.All()
// Create a sample for each metric.
samples := make([]metrics.Sample, len(descs))
for i := range samples {
samples[i].Name = descs[i].Name
}
// Sample the metrics. Re-use the samples slice if you can!
metrics.Read(samples)
// Iterate over all results.
for _, sample := range samples {
// Pull out the name and value.
name, value := sample.Name, sample.Value
// Handle each sample.
switch value.Kind() {
case metrics.KindUint64:
fmt.Printf("%s: %d\n", name, value.Uint64())
case metrics.KindFloat64:
fmt.Printf("%s: %f\n", name, value.Float64())
case metrics.KindFloat64Histogram:
// The histogram may be quite large, so let's just pull out
// a crude estimate for the median for the sake of this example.
fmt.Printf("%s: %f\n", name, medianBucket(value.Float64Histogram()))
case metrics.KindBad:
// This should never happen because all metrics are supported
// by construction.
panic("bug in runtime/metrics package!")
default:
// This may happen as new metrics get added.
//
// The safest thing to do here is to simply log it somewhere
// as something to look into, but ignore it for now.
// In the worst case, you might temporarily miss out on a new metric.
fmt.Printf("%s: unexpected metric Kind: %v\n", name, value.Kind())
}
}
}
func medianBucket(h *metrics.Float64Histogram) float64 {
total := uint64(0)
for _, count := range h.Counts {
total += count
}
thresh := total / 2
total = 0
for i, count := range h.Counts {
total += count
if total >= thresh {
return h.Buckets[i]
}
}
panic("should not happen")
}
hashicorp/go-metrics
Docs: https://github.com/hashicorp/go-metrics
使用 expvar 暴露 Go 程序运行指标
获取应用程序的运行指标, 可以让我们更好地了解它的实际状况。将这些指标对接到 prometheus、zabbix 等监控系统, 能够对应用程序持续检测, 发现异常可以及时告警并得到处理。
Pull 与 Push
与监控系统对接方式有两种, 一种是 Pull(拉取), 另外一种 Push(推送)。
以 Prometheus 为例, 应用程序通过暴露出 HTTP 接口, 让 Prometheus 周期性地通过该接口抓取指标, 这就是 Pull。而 Push 是应用程序主动将指标推送给 PushGateway, Prometheus 则去 PushGateway 抓取数据。
Go 标准库中有一个名为 expvar
的包, 它的名字由 exp 和 var 两部分组合而成, 意味着导出变量。
expvar 为公共变量提供了标准化的接口, 并通过 HTTP 以 Json 的格式将这些变量暴露出去, 很适合采用 Pull 的方式与监控系统进行对接。
使用 expvar 库
expvar
是标准库, 意味着我们并不要额外的依赖, 并且它还提供了一些开箱即用的指标。下面我们来学习一下该库的使用。
当引用了 expvar 库(import “expvar”), 以下 init 函数将被自动调用。
func init() {
http.HandleFunc("/debug/vars", expvarHandler)
Publish("cmdline", Func(cmdline))
Publish("memstats", Func(memstats))
}
该函数为我们注册了 /debug/vars
路径的 HTTP 服务, 访问该路径将得到 Json 格式的指标。
因此, 我们还需要调用 ListenAndServe 绑定端口, 并开始服务 HTTP 请求。
http.ListenAndServe(":8080", nil)
完整代码如下
package main
import (
_ "expvar"
"net/http"
)
func main() {
http.ListenAndServe(":8080", nil)
}
将程序运行起来后, 通过 curl 请求, 得到以下结果
$ curl localhost:8080/debug/vars {
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build1657217338/b001/exe/main"],
"memstats": {
"Alloc": 278880,
"TotalAlloc": 278880,
"Sys": 8735760,
"Lookups": 0,
"Mallocs": 1169,
"Frees": 87,
"HeapAlloc": 278880,
"HeapSys": 3866624,
"HeapIdle": 2949120,
"HeapInuse": 917504,
"HeapReleased": 2899968,
"HeapObjects": 1082,
"StackInuse": 327680,
"StackSys": 327680,
"MSpanInuse": 28696,
"MSpanSys": 32640,
"MCacheInuse": 9600,
"MCacheSys": 15600,
"BuckHashSys": 3875,
"GCSys": 3826448,
"OtherSys": 662893,
"NextGC": 4194304,
"LastGC": 0,
"PauseTotalNs": 0,
"PauseNs": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"PauseEnd": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"NumGC": 0,
"NumForcedGC": 0,
"GCCPUFraction": 0,
"EnableGC": true,
"DebugGC": false,
"BySize": [{
"Size": 0,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 8,
"Mallocs": 41,
"Frees": 0
}, {
"Size": 16,
"Mallocs": 496,
"Frees": 0
}, {
"Size": 24,
"Mallocs": 63,
"Frees": 0
}, {
"Size": 32,
"Mallocs": 28,
"Frees": 0
}, {
"Size": 48,
"Mallocs": 134,
"Frees": 0
}, {
"Size": 64,
"Mallocs": 50,
"Frees": 0
}, {
"Size": 80,
"Mallocs": 17,
"Frees": 0
}, {
"Size": 96,
"Mallocs": 17,
"Frees": 0
}, {
"Size": 112,
"Mallocs": 6,
"Frees": 0
}, {
"Size": 128,
"Mallocs": 9,
"Frees": 0
}, {
"Size": 144,
"Mallocs": 9,
"Frees": 0
}, {
"Size": 160,
"Mallocs": 18,
"Frees": 0
}, {
"Size": 176,
"Mallocs": 6,
"Frees": 0
}, {
"Size": 192,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 208,
"Mallocs": 37,
"Frees": 0
}, {
"Size": 224,
"Mallocs": 6,
"Frees": 0
}, {
"Size": 240,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 256,
"Mallocs": 12,
"Frees": 0
}, {
"Size": 288,
"Mallocs": 7,
"Frees": 0
}, {
"Size": 320,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 352,
"Mallocs": 13,
"Frees": 0
}, {
"Size": 384,
"Mallocs": 1,
"Frees": 0
}, {
"Size": 416,
"Mallocs": 30,
"Frees": 0
}, {
"Size": 448,
"Mallocs": 1,
"Frees": 0
}, {
"Size": 480,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 512,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 576,
"Mallocs": 5,
"Frees": 0
}, {
"Size": 640,
"Mallocs": 5,
"Frees": 0
}, {
"Size": 704,
"Mallocs": 3,
"Frees": 0
}, {
"Size": 768,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 896,
"Mallocs": 6,
"Frees": 0
}, {
"Size": 1024,
"Mallocs": 8,
"Frees": 0
}, {
"Size": 1152,
"Mallocs": 10,
"Frees": 0
}, {
"Size": 1280,
"Mallocs": 3,
"Frees": 0
}, {
"Size": 1408,
"Mallocs": 1,
"Frees": 0
}, {
"Size": 1536,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 1792,
"Mallocs": 7,
"Frees": 0
}, {
"Size": 2048,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 2304,
"Mallocs": 3,
"Frees": 0
}, {
"Size": 2688,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 3072,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 3200,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 3456,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 4096,
"Mallocs": 8,
"Frees": 0
}, {
"Size": 4864,
"Mallocs": 1,
"Frees": 0
}, {
"Size": 5376,
"Mallocs": 1,
"Frees": 0
}, {
"Size": 6144,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 6528,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 6784,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 6912,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 8192,
"Mallocs": 2,
"Frees": 0
}, {
"Size": 9472,
"Mallocs": 8,
"Frees": 0
}, {
"Size": 9728,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 10240,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 10880,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 12288,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 13568,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 14336,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 16384,
"Mallocs": 0,
"Frees": 0
}, {
"Size": 18432,
"Mallocs": 0,
"Frees": 0
}]
}
}
可以看到, expvar
默认已提供了两项指标, 分别是程序执行命令(os.Args
)和运行时内存分配(runtime.Memstats
)信息。
expvar 库重点内容
expvar
中最重要的是 Publish
函数和 Var
接口。
func Publish(name string, v Var) {}
Publish
函数签名中需要两个参数, name
是我们指定的指标名。例如在上文 expvar
的 init
函数下 Publish("cmdline", Func(cmdline))
代码行, 其中 cmdline
就是指标名, 而 Func(cmdline)
则是实现了 Var
接口的 expvar.Func
类型变量。
Var
接口, 它只定义了一个 String 方法。需要注意的是, 该方法必须要返回一个有效的 Json 字符串。
// Var is an abstract type for all exported variables.
type Var interface {
// String returns a valid JSON value for the variable.
// Types with String methods that do not return valid JSON
// (such as time.Time) must not be used as a Var.
String() string
}
为了方便使用, expvar 库中提供了五种导出变量类型, 它们均实现了 Var 接口。
type Int struct {
i int64
}
type Float struct {
f uint64
}
type Map struct {
m sync.Map // map[string]Var
keysMu sync.RWMutex
keys[] string // sorted
}
type String struct {
s atomic.Value // string
}
type Func func() any
我们分别通过调用 expvar.NewXXX
函数即可完成前四种类型的变量创建与指标注册。
intVar = expvar.NewInt("metricName")
floatVar = expvar.NewFloat("metricName")
mapVar = expvar.NewMap("metricName")
stringVar = expvar.NewString("metricName")
例如 expvar.NewInt
函数, 它会内部调用 Publish
方法完成指标名与 expvar.Int
类型变量的绑定。
func NewInt(name string) * Int {
v: = new(Int)
Publish(name, v)
return v
}
而 expvar.Func
类型, 其实是为了让我们可以自定义导出类型。
例如, 假如我们想要暴露以下定义的结构体
type MyStruct struct {
Field1 string
Field2 int
Field3 float64
}
首先需要创建一个数据生成函数。它用于在每次调用 HTTP 服务路径时, 通过该函数导出这里面的数据。
func MyStructData() interface {} {
return MyStruct {
Field1: "Gopher",
Field2: 22,
Field3: 19.99,
}
}
最后, 通过 Publish
函数注册指标名即可。
expvar.Publish("metricName", expvar.Func(MyStructData))
完整示例
下面, 我们给出一个覆盖五种导出变量类型的完整示例。
package main
import (
"expvar"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"net/http"
"time"
)
type Load struct {
Load1 float64
Load5 float64
Load15 float64
}
func AllLoadAvg() interface {} {
return Load {
Load1: LoadAvg(1),
Load5: LoadAvg(5),
Load15: LoadAvg(15),
}
}
func LoadAvg(loadNumber int) float64 {
avg, _: = load.Avg()
switch loadNumber {
case 5:
return avg.Load5
case 15:
return avg.Load15
default:
return avg.Load1
}
}
func main() {
var (
aliveOfSeconds = expvar.NewInt("aliveOfSeconds") hostID = expvar.NewString("hostID") lastLoad = expvar.NewFloat("lastLoad") virtualMemory = expvar.NewMap("virtualMemory")
)
expvar.Publish("allLoadAvg", expvar.Func(AllLoadAvg))
h, _: = host.HostID()
hostID.Set(h)
go http.ListenAndServe(":8080", nil)
for {
aliveOfSeconds.Add(1)
vm, _: = mem.VirtualMemory()
lastLoad.Set(LoadAvg(1))
virtualMemory.Add("active", int64(vm.Active))
virtualMemory.Add("buffer", int64(vm.Buffers))
time.Sleep(1 * time.Second)
}
}
在上述示例中, 我们通过 gopsutil
库(介绍可见还在自己写 Go 系统监控函数吗一文)获取了一些系统信息, 并展示了如何通过 expvar
中的各种变量类型将这些信息进行导出。
curl
访问 localhost:8080/debug/vars
结果如下
$ curl localhost:8080/debug/vars
{
"aliveOfSeconds": 1,
"allLoadAvg": {"Load1":1.69580078125,"Load5":1.97412109375,"Load15":1.90283203125},
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build3566019824/b001/exe/main"],
"hostID": "7a1a74f2-30fc-5bc1-b439-6b7aef22e58d",
"lastLoad": 1.69580078125,
"memstats": {"Alloc":256208,"TotalAlloc":256208,"Sys":8735760,"Lookups":0,"Mallocs":1089,"Frees":48,"HeapAlloc":256208,"HeapSys":3866624,"HeapIdle":2891776,"HeapInuse":974848,"HeapReleased":2859008,"HeapObjects":1041,"StackInuse":327680,"StackSys":327680,"MSpanInuse":19992,"MSpanSys":32640,"MCacheInuse":9600,"MCacheSys":15600,"BuckHashSys":3905,"GCSys":3851120,"OtherSys":638191,"NextGC":4194304,"LastGC":0,"PauseTotalNs":0,"PauseNs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":0,"NumForcedGC":0,"GCCPUFraction":0,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":35,"Frees":0},{"Size":16,"Mallocs":415,"Frees":0},{"Size":24,"Mallocs":71,"Frees":0},{"Size":32,"Mallocs":37,"Frees":0},{"Size":48,"Mallocs":141,"Frees":0},{"Size":64,"Mallocs":52,"Frees":0},{"Size":80,"Mallocs":20,"Frees":0},{"Size":96,"Mallocs":23,"Frees":0},{"Size":112,"Mallocs":14,"Frees":0},{"Size":128,"Mallocs":7,"Frees":0},{"Size":144,"Mallocs":7,"Frees":0},{"Size":160,"Mallocs":18,"Frees":0},{"Size":176,"Mallocs":6,"Frees":0},{"Size":192,"Mallocs":1,"Frees":0},{"Size":208,"Mallocs":42,"Frees":0},{"Size":224,"Mallocs":3,"Frees":0},{"Size":240,"Mallocs":0,"Frees":0},{"Size":256,"Mallocs":9,"Frees":0},{"Size":288,"Mallocs":8,"Frees":0},{"Size":320,"Mallocs":5,"Frees":0},{"Size":352,"Mallocs":13,"Frees":0},{"Size":384,"Mallocs":3,"Frees":0},{"Size":416,"Mallocs":33,"Frees":0},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":1,"Frees":0},{"Size":576,"Mallocs":4,"Frees":0},{"Size":640,"Mallocs":8,"Frees":0},{"Size":704,"Mallocs":3,"Frees":0},{"Size":768,"Mallocs":1,"Frees":0},{"Size":896,"Mallocs":6,"Frees":0},{"Size":1024,"Mallocs":8,"Frees":0},{"Size":1152,"Mallocs":9,"Frees":0},{"Size":1280,"Mallocs":3,"Frees":0},{"Size":1408,"Mallocs":1,"Frees":0},{"Size":1536,"Mallocs":1,"Frees":0},{"Size":1792,"Mallocs":9,"Frees":0},{"Size":2048,"Mallocs":1,"Frees":0},{"Size":2304,"Mallocs":2,"Frees":0},{"Size":2688,"Mallocs":2,"Frees":0},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3200,"Mallocs":1,"Frees":0},{"Size":3456,"Mallocs":0,"Frees":0},{"Size":4096,"Mallocs":5,"Frees":0},{"Size":4864,"Mallocs":0,"Frees":0},{"Size":5376,"Mallocs":1,"Frees":0},{"Size":6144,"Mallocs":1,"Frees":0},{"Size":6528,"Mallocs":0,"Frees":0},{"Size":6784,"Mallocs":0,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":1,"Frees":0},{"Size":9472,"Mallocs":8,"Frees":0},{"Size":9728,"Mallocs":0,"Frees":0},{"Size":10240,"Mallocs":0,"Frees":0},{"Size":10880,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":0,"Frees":0},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14336,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":18432,"Mallocs":0,"Frees":0}]},
"virtualMemory": {"active": 1957449728, "buffer": 0}
}
总结
标准库 expvar
为需要导出的公共变量提供了一个标准化的接口, 使用比较简单。
expvar
包内部定义的几种基础类型都相应给出了并发安全的操作方法, 我们不需要去重复实现一遍, 能够开箱即用。
但是, 在 https://go.dev/ 上统计的公共项目, 该库的 import
数量还不足 1 万。
相比于其他标准库的 import
数量而言, expvar
的存在感太低了。
所以问题来了, 你认为是什么原因导致 expvar
库这么冷门呢? 欢迎留言讨论。