diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2fff38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Develop tools +/.vscode/ +/.idea/ +.idea +.vscode + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Folders +_obj +_test +_build +.DS_Store + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# vim temp files +*~ +*.swp +*.swo diff --git a/go.mod b/go.mod index db06616..fab2891 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,23 @@ module go.unistack.org/micro-meter-victoriametrics/v3 -go 1.22 - -toolchain go1.23.2 +go 1.24.0 require ( - go.unistack.org/metrics v0.0.1 - go.unistack.org/micro/v3 v3.10.100 + github.com/VictoriaMetrics/metrics v1.40.2 + go.unistack.org/micro/v3 v3.11.48 ) require ( + github.com/ash3in/uuidv8 v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/matoous/go-nanoid v1.5.1 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/valyala/histogram v1.2.0 // indirect - golang.org/x/sys v0.27.0 // indirect + go.unistack.org/micro-proto/v3 v3.4.1 // indirect + golang.org/x/sys v0.28.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect + google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.36.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c1162d9..cf3382d 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,48 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/VictoriaMetrics/metrics v1.40.2 h1:OVSjKcQEx6JAwGeu8/KQm9Su5qJ72TMEW4xYn5vw3Ac= +github.com/VictoriaMetrics/metrics v1.40.2/go.mod h1:XE4uudAAIRaJE614Tl5HMrtoEU6+GDZO4QTnNSsZRuA= +github.com/ash3in/uuidv8 v1.2.0 h1:2oogGdtCPwaVtyvPPGin4TfZLtOGE5F+W++E880G6SI= +github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0dWgyY4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matoous/go-nanoid v1.5.1 h1:aCjdvTyO9LLnTIi0fgdXhOPPvOHjpXN6Ik9DaNjIct4= +github.com/matoous/go-nanoid v1.5.1/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= -go.unistack.org/metrics v0.0.1 h1:sCnGO059ZccGC/D34iRH121eSk+7ci5+OY9cl5K7GKY= -go.unistack.org/metrics v0.0.1/go.mod h1:1FY4R7EKJa9Oz2D6wlGScNerpl6igRs9Cx/3et4Rgs4= -go.unistack.org/micro/v3 v3.10.100 h1:yWOaU0ImCGm5k5MUzlIobJUOr+KLfrR/BoDZvcHyKxM= -go.unistack.org/micro/v3 v3.10.100/go.mod h1:YzMldzHN9Ei+zy5t/Psu7RUWDZwUfrNYiStSQtTz90g= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q= +go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo= +go.unistack.org/micro/v3 v3.11.48 h1:lHJYSHU2z1TTcuswItGwG7cZXN6n04EFqY7lk/0gA7w= +go.unistack.org/micro/v3 v3.11.48/go.mod h1:fDQ8Mu9wubaFP0L8hNQlpzHiEnWN0wbOlawN9HYo0N4= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 h1:Z7FRVJPSMaHQxD0uXU8WdgFh8PseLM8Q8NzhnpMrBhQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/victoriametrics.go b/victoriametrics.go index 1eb1e28..5ec2e32 100644 --- a/victoriametrics.go +++ b/victoriametrics.go @@ -4,7 +4,7 @@ import ( "io" "time" - "go.unistack.org/metrics" + "github.com/VictoriaMetrics/metrics" "go.unistack.org/micro/v3/meter" ) @@ -14,119 +14,129 @@ type victoriametricsMeter struct { prometheusCompat bool } -type prometheusCompatKey struct{} - -func PrometheusCompat(b bool) meter.Option { - return meter.SetOption(prometheusCompatKey{}, b) -} - func NewMeter(opts ...meter.Option) meter.Meter { m := &victoriametricsMeter{set: metrics.NewSet(), opts: meter.NewOptions(opts...)} - if v, ok := m.opts.Context.Value(prometheusCompatKey{}).(bool); ok { + if v, ok := m.opts.Context.Value(prometheusCompatKey{}).(bool); ok && v { m.prometheusCompat = v } return m } -func (m *victoriametricsMeter) Name() string { - return m.opts.Name +func (r *victoriametricsMeter) Name() string { + return r.opts.Name } -func (m *victoriametricsMeter) Clone(opts ...meter.Option) meter.Meter { - options := m.opts +func (r *victoriametricsMeter) Unregister(name string, labels ...string) bool { + return r.set.UnregisterMetric(r.buildName(name, labels...)) +} + +func (r *victoriametricsMeter) Clone(opts ...meter.Option) meter.Meter { + options := r.opts for _, o := range opts { o(&options) } - nm := &victoriametricsMeter{set: m.set, opts: options, prometheusCompat: m.prometheusCompat} - if v, ok := m.opts.Context.Value(prometheusCompatKey{}).(bool); ok { + m := &victoriametricsMeter{set: r.set, opts: options} + if v, ok := m.opts.Context.Value(prometheusCompatKey{}).(bool); ok && v { m.prometheusCompat = v } - return nm + return m } -func (m *victoriametricsMeter) buildName(name string, labels ...string) string { - nl := len(m.opts.Labels) + len(labels) +func (r *victoriametricsMeter) buildName(name string, labels ...string) string { + nl := len(r.opts.Labels) + len(labels) if nl == 0 { return name } nlabels := make([]string, 0, nl) - nlabels = append(nlabels, m.opts.Labels...) + nlabels = append(nlabels, r.opts.Labels...) nlabels = append(nlabels, labels...) return meter.BuildName(name, nlabels...) } -func (m *victoriametricsMeter) Counter(name string, labels ...string) meter.Counter { - return m.set.GetOrCreateCounter(m.buildName(name, labels...)) +func (r *victoriametricsMeter) Counter(name string, labels ...string) meter.Counter { + return r.set.GetOrCreateCounter(r.buildName(name, labels...)) } -func (m *victoriametricsMeter) FloatCounter(name string, labels ...string) meter.FloatCounter { - return m.set.GetOrCreateFloatCounter(m.buildName(name, labels...)) +func (r *victoriametricsMeter) FloatCounter(name string, labels ...string) meter.FloatCounter { + return r.set.GetOrCreateFloatCounter(r.buildName(name, labels...)) } -func (m *victoriametricsMeter) Gauge(name string, f func() float64, labels ...string) meter.Gauge { - return m.set.GetOrCreateGauge(m.buildName(name, labels...), f) +func (r *victoriametricsMeter) Gauge(name string, f func() float64, labels ...string) meter.Gauge { + return r.set.GetOrCreateGauge(r.buildName(name, labels...), f) } -func (m *victoriametricsMeter) Histogram(name string, labels ...string) meter.Histogram { - if m.prometheusCompat { - return m.set.GetOrCreateCompatibleHistogram(m.buildName(name, labels...)) +func (r *victoriametricsMeter) Histogram(name string, labels ...string) meter.Histogram { + if r.prometheusCompat { + return r.set.GetOrCreatePrometheusHistogramExt(r.buildName(name, labels...), r.opts.Quantiles) } - return m.set.GetOrCreateHistogram(m.buildName(name, labels...)) + return r.set.GetOrCreateHistogram(r.buildName(name, labels...)) } -func (m *victoriametricsMeter) Summary(name string, labels ...string) meter.Summary { - return m.set.GetOrCreateSummary(m.buildName(name, labels...)) -} - -func (m *victoriametricsMeter) SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) meter.Summary { - return m.set.GetOrCreateSummaryExt(m.buildName(name, labels...), window, quantiles) -} - -func (m *victoriametricsMeter) Set(opts ...meter.Option) meter.Meter { - nm := &victoriametricsMeter{opts: m.opts} - for _, o := range opts { - o(&nm.opts) +func (r *victoriametricsMeter) HistogramExt(name string, quantiles []float64, labels ...string) meter.Histogram { + if r.prometheusCompat { + return r.set.GetOrCreatePrometheusHistogramExt(r.buildName(name, labels...), quantiles) } - nm.set = metrics.NewSet() - if v, ok := nm.opts.Context.Value(prometheusCompatKey{}).(bool); ok { - nm.prometheusCompat = v - } - return nm + return r.set.GetOrCreateHistogram(r.buildName(name, labels...)) } -func (m *victoriametricsMeter) Init(opts ...meter.Option) error { +func (r *victoriametricsMeter) Summary(name string, labels ...string) meter.Summary { + return r.set.GetOrCreateSummary(r.buildName(name, labels...)) +} + +func (r *victoriametricsMeter) SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) meter.Summary { + return r.set.GetOrCreateSummaryExt(r.buildName(name, labels...), window, quantiles) +} + +func (r *victoriametricsMeter) Set(opts ...meter.Option) meter.Meter { + m := &victoriametricsMeter{opts: r.opts, prometheusCompat: r.prometheusCompat} for _, o := range opts { o(&m.opts) } - if v, ok := m.opts.Context.Value(prometheusCompatKey{}).(bool); ok { - m.prometheusCompat = v + m.set = metrics.NewSet() + return m +} + +func (r *victoriametricsMeter) Init(opts ...meter.Option) error { + for _, o := range opts { + o(&r.opts) + } + if v, ok := r.opts.Context.Value(prometheusCompatKey{}).(bool); ok && v { + r.prometheusCompat = v + } + if r.opts.Quantiles == nil { + r.opts.Quantiles = meter.DefaultQuantiles } return nil } -func (m *victoriametricsMeter) Write(w io.Writer, opts ...meter.Option) error { - options := m.opts +func (r *victoriametricsMeter) Write(w io.Writer, opts ...meter.Option) error { + options := r.opts for _, o := range opts { o(&options) } - m.set.WritePrometheus(w) + r.set.WritePrometheus(w) if options.WriteProcessMetrics { metrics.WriteProcessMetrics(w) } if options.WriteFDMetrics { metrics.WriteFDMetrics(w) } - return nil } -func (m *victoriametricsMeter) Options() meter.Options { - return m.opts +func (r *victoriametricsMeter) Options() meter.Options { + return r.opts } -func (m *victoriametricsMeter) String() string { +func (r *victoriametricsMeter) String() string { return "victoriametrics" } + +type prometheusCompatKey struct{} + +func PrometheusCompat(b bool) meter.Option { + return meter.SetOption(prometheusCompatKey{}, b) +} diff --git a/victoriametrics_test.go b/victoriametrics_test.go index 81f9e59..19cc7e6 100644 --- a/victoriametrics_test.go +++ b/victoriametrics_test.go @@ -2,14 +2,32 @@ package victoriametrics import ( "bytes" + "context" "testing" + + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/codec" + "go.unistack.org/micro/v3/meter" ) +func BenchmarkBuildName(b *testing.B) { + m := NewMeter(meter.Labels("pod", "xxx")) + if err := m.Init(); err != nil { + b.Fatal(err) + } + im := m.(*victoriametricsMeter) + b.ResetTimer() + for i := 0; i < b.N; i++ { + name := im.buildName("micro_foo", "bar", "baz", "aaa", "b", "ccc", "d") + _ = name + } +} + func TestBuildName(t *testing.T) { m := NewMeter() im := m.(*victoriametricsMeter) - check := `micro_foo{micro_aaa="b",micro_bar="baz",micro_ccc="d"}` - name := im.buildName("micro_foo", "micro_bar", "baz", "micro_aaa", "b", "micro_ccc", "d") + check := `micro_foo{bar="baz",aaa="b",ccc="d"}` + name := im.buildName("micro_foo", "bar", "baz", "aaa", "b", "ccc", "d") if name != check { t.Fatalf("metric name error: %s != %s", name, check) } @@ -18,12 +36,24 @@ func TestBuildName(t *testing.T) { cnt.Inc() } -func TestPrometheusCompat(t *testing.T) { - m := NewMeter(PrometheusCompat(true)) - m.Histogram("foo", "key", "val").Update(15) +func TestWrapper(t *testing.T) { + m := NewMeter() + if err := m.Init(); err != nil { + t.Fatal(err) + } + ctx := context.Background() + c := client.NewClient(client.Meter(m)) + if err := c.Init(); err != nil { + t.Fatal(err) + } + rsp := &codec.Frame{} + req := &codec.Frame{} + err := c.Call(ctx, c.NewRequest("svc2", "Service.Method", req), rsp) + _, _ = rsp, err buf := bytes.NewBuffer(nil) - - _ = m.Write(buf) - t.Logf("\n%s", buf.Bytes()) + _ = m.Write(buf, meter.WriteProcessMetrics(false), meter.WriteFDMetrics(false)) + if !bytes.Contains(buf.Bytes(), []byte(`micro_client_request_total{endpoint="Service.Method",status="failure",code="500"} 1`)) { + t.Fatalf("invalid metrics output: %s", buf.Bytes()) + } }