golang application metrics

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 是我们指定的指标名。例如在上文 expvarinit 函数下 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 库这么冷门呢? 欢迎留言讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云满笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值