From b63a56cf93ddfa108820db379c9f16306efab3d4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 13 Jan 2025 13:54:08 +0100 Subject: [PATCH 01/13] chore(metrics): use libevm metrics package and delete local metrics --- core/blockchain.go | 2 +- core/main_test.go | 2 +- core/rawdb/schema.go | 2 +- core/state/metrics.go | 2 +- core/state/snapshot/snapshot.go | 2 +- core/state/state_object.go | 2 +- core/state/statedb.go | 2 +- core/state/trie_prefetcher.go | 2 +- core/state/trie_prefetcher_extra_test.go | 2 +- core/txpool/blobpool/blobpool.go | 2 +- core/txpool/blobpool/metrics.go | 2 +- core/txpool/legacypool/legacypool.go | 2 +- core/txpool/txpool.go | 2 +- go.mod | 7 +- go.sum | 2 + metrics/FORK.md | 1 - metrics/LICENSE | 29 -- metrics/README.md | 102 ----- metrics/config.go | 43 -- metrics/counter.go | 112 ----- metrics/counter_float64.go | 126 ------ metrics/counter_float_64_test.go | 99 ----- metrics/counter_test.go | 77 ---- metrics/cpu.go | 35 -- metrics/cpu_disabled.go | 34 -- metrics/cpu_enabled.go | 54 --- metrics/cputime_nop.go | 36 -- metrics/cputime_unix.go | 46 -- metrics/debug.go | 76 ---- metrics/debug_test.go | 48 --- metrics/disk.go | 35 -- metrics/disk_linux.go | 82 ---- metrics/disk_nop.go | 37 -- metrics/ewma.go | 111 ----- metrics/ewma_test.go | 89 ---- metrics/gauge.go | 98 ----- metrics/gauge_float64.go | 73 ---- metrics/gauge_float64_test.go | 51 --- metrics/gauge_info.go | 84 ---- metrics/gauge_info_test.go | 36 -- metrics/gauge_test.go | 31 -- metrics/graphite.go | 117 ----- metrics/graphite_test.go | 22 - metrics/healthcheck.go | 61 --- metrics/histogram.go | 73 ---- metrics/histogram_test.go | 95 ----- metrics/inactive.go | 48 --- metrics/json.go | 31 -- metrics/json_test.go | 28 -- metrics/log.go | 86 ---- metrics/memory.md | 285 ------------- metrics/meter.go | 189 --------- metrics/meter_test.go | 89 ---- metrics/metrics.go | 18 - metrics/metrics_test.go | 99 ----- metrics/opentsdb.go | 128 ------ metrics/opentsdb_test.go | 67 --- metrics/prometheus/prometheus.go | 196 --------- metrics/prometheus/prometheus_test.go | 87 ---- metrics/prometheus/testdata/prometheus.want | 70 --- metrics/registry.go | 372 ---------------- metrics/registry_test.go | 335 --------------- metrics/resetting_sample.go | 24 -- metrics/resetting_timer.go | 171 -------- metrics/resetting_timer_test.go | 197 --------- metrics/sample.go | 446 -------------------- metrics/sample_test.go | 360 ---------------- metrics/syslog.go | 83 ---- metrics/testdata/opentsb.want | 23 - metrics/timer.go | 182 -------- metrics/timer_test.go | 114 ----- metrics/validate.sh | 10 - metrics/writer.go | 100 ----- metrics/writer_test.go | 23 - peer/peer_tracker.go | 2 +- peer/stats/stats.go | 2 +- plugin/evm/gossip_stats.go | 2 +- plugin/evm/network_handler.go | 2 +- plugin/evm/syncervm_test.go | 2 +- plugin/evm/vm.go | 6 +- plugin/evm/vm_test.go | 2 +- plugin/evm/vm_upgrade_bytes_test.go | 2 +- rpc/handler.go | 2 +- rpc/metrics.go | 2 +- sync/client/stats/stats.go | 2 +- sync/handlers/stats/stats.go | 2 +- sync/statesync/trie_sync_stats.go | 2 +- sync/statesync/trie_sync_stats_test.go | 2 +- trie/stacktrie.go | 2 +- trie/utils/verkle.go | 2 +- triedb/hashdb/database.go | 2 +- triedb/pathdb/metrics.go | 2 +- utils/metered_cache.go | 2 +- warp/handlers/signature_request_test.go | 11 + warp/handlers/stats.go | 2 +- warp/verifier_backend_test.go | 11 + warp/verifier_stats.go | 2 +- 97 files changed, 64 insertions(+), 5913 deletions(-) delete mode 100644 metrics/FORK.md delete mode 100644 metrics/LICENSE delete mode 100644 metrics/README.md delete mode 100644 metrics/config.go delete mode 100644 metrics/counter.go delete mode 100644 metrics/counter_float64.go delete mode 100644 metrics/counter_float_64_test.go delete mode 100644 metrics/counter_test.go delete mode 100644 metrics/cpu.go delete mode 100644 metrics/cpu_disabled.go delete mode 100644 metrics/cpu_enabled.go delete mode 100644 metrics/cputime_nop.go delete mode 100644 metrics/cputime_unix.go delete mode 100644 metrics/debug.go delete mode 100644 metrics/debug_test.go delete mode 100644 metrics/disk.go delete mode 100644 metrics/disk_linux.go delete mode 100644 metrics/disk_nop.go delete mode 100644 metrics/ewma.go delete mode 100644 metrics/ewma_test.go delete mode 100644 metrics/gauge.go delete mode 100644 metrics/gauge_float64.go delete mode 100644 metrics/gauge_float64_test.go delete mode 100644 metrics/gauge_info.go delete mode 100644 metrics/gauge_info_test.go delete mode 100644 metrics/gauge_test.go delete mode 100644 metrics/graphite.go delete mode 100644 metrics/graphite_test.go delete mode 100644 metrics/healthcheck.go delete mode 100644 metrics/histogram.go delete mode 100644 metrics/histogram_test.go delete mode 100644 metrics/inactive.go delete mode 100644 metrics/json.go delete mode 100644 metrics/json_test.go delete mode 100644 metrics/log.go delete mode 100644 metrics/memory.md delete mode 100644 metrics/meter.go delete mode 100644 metrics/meter_test.go delete mode 100644 metrics/metrics.go delete mode 100644 metrics/metrics_test.go delete mode 100644 metrics/opentsdb.go delete mode 100644 metrics/opentsdb_test.go delete mode 100644 metrics/prometheus/prometheus.go delete mode 100644 metrics/prometheus/prometheus_test.go delete mode 100644 metrics/prometheus/testdata/prometheus.want delete mode 100644 metrics/registry.go delete mode 100644 metrics/registry_test.go delete mode 100644 metrics/resetting_sample.go delete mode 100644 metrics/resetting_timer.go delete mode 100644 metrics/resetting_timer_test.go delete mode 100644 metrics/sample.go delete mode 100644 metrics/sample_test.go delete mode 100644 metrics/syslog.go delete mode 100644 metrics/testdata/opentsb.want delete mode 100644 metrics/timer.go delete mode 100644 metrics/timer_test.go delete mode 100755 metrics/validate.sh delete mode 100644 metrics/writer.go delete mode 100644 metrics/writer_test.go diff --git a/core/blockchain.go b/core/blockchain.go index 19615c80ba..4e40400f6a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -39,6 +39,7 @@ import ( "sync/atomic" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" @@ -48,7 +49,6 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/internal/version" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/triedb" diff --git a/core/main_test.go b/core/main_test.go index 571ea74dc1..57bd792327 100644 --- a/core/main_test.go +++ b/core/main_test.go @@ -15,7 +15,7 @@ func TestMain(m *testing.M) { opts := []goleak.Option{ // No good way to shut down these goroutines: goleak.IgnoreTopFunction("github.com/ava-labs/subnet-evm/core/state/snapshot.(*diskLayer).generate"), - goleak.IgnoreTopFunction("github.com/ava-labs/subnet-evm/metrics.(*meterArbiter).tick"), + goleak.IgnoreTopFunction("github.com/ava-labs/libevm/metrics.(*meterArbiter).tick"), goleak.IgnoreTopFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain"), } goleak.VerifyTestMain(m, opts...) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 78d1cb4a72..f676b74aba 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -32,7 +32,7 @@ import ( "encoding/binary" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) diff --git a/core/state/metrics.go b/core/state/metrics.go index 5af6243c98..4fbc86c2f5 100644 --- a/core/state/metrics.go +++ b/core/state/metrics.go @@ -26,7 +26,7 @@ package state -import "github.com/ava-labs/subnet-evm/metrics" +import "github.com/ava-labs/libevm/metrics" var ( accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil) diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 3490d743bf..6e8a0a0f3f 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -34,9 +34,9 @@ import ( "sync" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" diff --git a/core/state/state_object.go b/core/state/state_object.go index fbff5a1c48..2eb68d0889 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -32,8 +32,8 @@ import ( "io" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" diff --git a/core/state/statedb.go b/core/state/statedb.go index b4c2da9566..17dd82ce0e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -32,10 +32,10 @@ import ( "sort" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/predicate" "github.com/ava-labs/subnet-evm/trie" diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 8465deae2a..da30e9aa70 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -19,8 +19,8 @@ package state import ( "sync" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/libevm/options" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) diff --git a/core/state/trie_prefetcher_extra_test.go b/core/state/trie_prefetcher_extra_test.go index 5ac1d1c5c2..e5632bac71 100644 --- a/core/state/trie_prefetcher_extra_test.go +++ b/core/state/trie_prefetcher_extra_test.go @@ -13,10 +13,10 @@ import ( "testing" "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/triedb/hashdb" "github.com/ethereum/go-ethereum/common" diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 470a2b4e39..aa88d7758e 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -39,13 +39,13 @@ import ( "sync" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" diff --git a/core/txpool/blobpool/metrics.go b/core/txpool/blobpool/metrics.go index a65a345b22..ccd4dbb574 100644 --- a/core/txpool/blobpool/metrics.go +++ b/core/txpool/blobpool/metrics.go @@ -26,7 +26,7 @@ package blobpool -import "github.com/ava-labs/subnet-evm/metrics" +import "github.com/ava-labs/libevm/metrics" var ( // datacapGauge tracks the user's configured capacity for the blob pool. It diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 31e9fdd95e..466dfa081a 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -36,13 +36,13 @@ import ( "sync/atomic" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" "github.com/ava-labs/subnet-evm/utils" diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index e65babe5a3..01128e832c 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -33,9 +33,9 @@ import ( "sync" "sync/atomic" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" diff --git a/go.mod b/go.mod index c53bd1a4a6..c214e6a1c5 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 github.com/ava-labs/avalanchego v1.12.1 + github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 @@ -30,8 +31,6 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo/v2 v2.13.1 github.com/prometheus/client_golang v1.16.0 - github.com/prometheus/client_model v0.3.0 - github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cast v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 @@ -46,7 +45,6 @@ require ( golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/mod v0.17.0 golang.org/x/sync v0.10.0 - golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 golang.org/x/time v0.3.0 google.golang.org/protobuf v1.34.2 @@ -130,12 +128,14 @@ require ( github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -157,6 +157,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gonum.org/v1/gonum v0.11.0 // indirect diff --git a/go.sum b/go.sum index e989b5c81d..2e61401a83 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6Yyuj github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer h1:mRB03tLPUvgNko4nP4VwWQdiHeHaLHtdwsnqwxrsGec= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer/go.mod h1:tqRAe+7bGLo2Rq/Ph4iYMSch72ag/Jn0DiDMDz1Xa9E= +github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f h1:KeKggoIyyF+o/GeGofo2+UO93WN7ulqMcVlP2K3iUzM= +github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f/go.mod h1:M8TCw2g1D5GBB7hu7g1F4aot5bRHGSxnBawNVmHE9Z0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/metrics/FORK.md b/metrics/FORK.md deleted file mode 100644 index b19985bf56..0000000000 --- a/metrics/FORK.md +++ /dev/null @@ -1 +0,0 @@ -This repo has been forked from https://github.com/rcrowley/go-metrics at commit e181e09 diff --git a/metrics/LICENSE b/metrics/LICENSE deleted file mode 100644 index 363fa9ee77..0000000000 --- a/metrics/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright 2012 Richard Crowley. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of Richard Crowley. diff --git a/metrics/README.md b/metrics/README.md deleted file mode 100644 index cf153c8093..0000000000 --- a/metrics/README.md +++ /dev/null @@ -1,102 +0,0 @@ -go-metrics -========== - -![travis build status](https://travis-ci.org/rcrowley/go-metrics.svg?branch=master) - -Go port of Coda Hale's Metrics library: . - -Documentation: . - -Usage ------ - -Create and update metrics: - -```go -c := metrics.NewCounter() -metrics.Register("foo", c) -c.Inc(47) - -g := metrics.NewGauge() -metrics.Register("bar", g) -g.Update(47) - -r := NewRegistry() -g := metrics.NewRegisteredFunctionalGauge("cache-evictions", r, func() int64 { return cache.getEvictionsCount() }) - -s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028) -h := metrics.NewHistogram(s) -metrics.Register("baz", h) -h.Update(47) - -m := metrics.NewMeter() -metrics.Register("quux", m) -m.Mark(47) - -t := metrics.NewTimer() -metrics.Register("bang", t) -t.Time(func() {}) -t.Update(47) -``` - -Register() is not threadsafe. For threadsafe metric registration use -GetOrRegister: - -```go -t := metrics.GetOrRegisterTimer("account.create.latency", nil) -t.Time(func() {}) -t.Update(47) -``` - -**NOTE:** Be sure to unregister short-lived meters and timers otherwise they will -leak memory: - -```go -// Will call Stop() on the Meter to allow for garbage collection -metrics.Unregister("quux") -// Or similarly for a Timer that embeds a Meter -metrics.Unregister("bang") -``` - -Periodically log every metric in human-readable form to standard error: - -```go -go metrics.Log(metrics.DefaultRegistry, 5 * time.Second, log.New(os.Stderr, "metrics: ", log.Lmicroseconds)) -``` - -Periodically log every metric in slightly-more-parseable form to syslog: - -```go -w, _ := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics") -go metrics.Syslog(metrics.DefaultRegistry, 60e9, w) -``` - -Periodically emit every metric to Graphite using the [Graphite client](https://github.com/cyberdelia/go-metrics-graphite): - -```go - -import "github.com/cyberdelia/go-metrics-graphite" - -addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003") -go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr) -``` - -Installation ------------- - -```sh -go get github.com/rcrowley/go-metrics -``` - -StatHat support additionally requires their Go client: - -```sh -go get github.com/stathat/go -``` - -Publishing Metrics ------------------- - -Clients are available for the following destinations: - -* Prometheus - https://github.com/deathowl/go-metrics-prometheus diff --git a/metrics/config.go b/metrics/config.go deleted file mode 100644 index a60d96e962..0000000000 --- a/metrics/config.go +++ /dev/null @@ -1,43 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2021 The go-ethereum Authors -// This file is part of go-ethereum. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// Config contains the configuration for the metric collection. -type Config struct { - Enabled bool `toml:",omitempty"` - EnabledExpensive bool `toml:",omitempty"` - HTTP string `toml:",omitempty"` - Port int `toml:",omitempty"` -} - -// DefaultConfig is the default config for metrics used in go-ethereum. -var DefaultConfig = Config{ - Enabled: false, - EnabledExpensive: false, - HTTP: "127.0.0.1", - Port: 6060, -} diff --git a/metrics/counter.go b/metrics/counter.go deleted file mode 100644 index dbe8e16a90..0000000000 --- a/metrics/counter.go +++ /dev/null @@ -1,112 +0,0 @@ -package metrics - -import ( - "sync/atomic" -) - -type CounterSnapshot interface { - Count() int64 -} - -// Counter hold an int64 value that can be incremented and decremented. -type Counter interface { - Clear() - Dec(int64) - Inc(int64) - Snapshot() CounterSnapshot -} - -// GetOrRegisterCounter returns an existing Counter or constructs and registers -// a new StandardCounter. -func GetOrRegisterCounter(name string, r Registry) Counter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounter).(Counter) -} - -// GetOrRegisterCounterForced returns an existing Counter or constructs and registers a -// new Counter no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterForced(name string, r Registry) Counter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterForced).(Counter) -} - -// NewCounter constructs a new StandardCounter. -func NewCounter() Counter { - if !Enabled { - return NilCounter{} - } - return new(StandardCounter) -} - -// NewCounterForced constructs a new StandardCounter and returns it no matter if -// the global switch is enabled or not. -func NewCounterForced() Counter { - return new(StandardCounter) -} - -// NewRegisteredCounter constructs and registers a new StandardCounter. -func NewRegisteredCounter(name string, r Registry) Counter { - c := NewCounter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterForced constructs and registers a new StandardCounter -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterForced(name string, r Registry) Counter { - c := NewCounterForced() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// counterSnapshot is a read-only copy of another Counter. -type counterSnapshot int64 - -// Count returns the count at the time the snapshot was taken. -func (c counterSnapshot) Count() int64 { return int64(c) } - -// NilCounter is a no-op Counter. -type NilCounter struct{} - -func (NilCounter) Clear() {} -func (NilCounter) Dec(i int64) {} -func (NilCounter) Inc(i int64) {} -func (NilCounter) Snapshot() CounterSnapshot { return (*emptySnapshot)(nil) } - -// StandardCounter is the standard implementation of a Counter and uses the -// sync/atomic package to manage a single int64 value. -type StandardCounter atomic.Int64 - -// Clear sets the counter to zero. -func (c *StandardCounter) Clear() { - (*atomic.Int64)(c).Store(0) -} - -// Dec decrements the counter by the given amount. -func (c *StandardCounter) Dec(i int64) { - (*atomic.Int64)(c).Add(-i) -} - -// Inc increments the counter by the given amount. -func (c *StandardCounter) Inc(i int64) { - (*atomic.Int64)(c).Add(i) -} - -// Snapshot returns a read-only copy of the counter. -func (c *StandardCounter) Snapshot() CounterSnapshot { - return counterSnapshot((*atomic.Int64)(c).Load()) -} diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go deleted file mode 100644 index 15c81494ef..0000000000 --- a/metrics/counter_float64.go +++ /dev/null @@ -1,126 +0,0 @@ -package metrics - -import ( - "math" - "sync/atomic" -) - -type CounterFloat64Snapshot interface { - Count() float64 -} - -// CounterFloat64 holds a float64 value that can be incremented and decremented. -type CounterFloat64 interface { - Clear() - Dec(float64) - Inc(float64) - Snapshot() CounterFloat64Snapshot -} - -// GetOrRegisterCounterFloat64 returns an existing CounterFloat64 or constructs and registers -// a new StandardCounterFloat64. -func GetOrRegisterCounterFloat64(name string, r Registry) CounterFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterFloat64).(CounterFloat64) -} - -// GetOrRegisterCounterFloat64Forced returns an existing CounterFloat64 or constructs and registers a -// new CounterFloat64 no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterFloat64Forced(name string, r Registry) CounterFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterFloat64Forced).(CounterFloat64) -} - -// NewCounterFloat64 constructs a new StandardCounterFloat64. -func NewCounterFloat64() CounterFloat64 { - if !Enabled { - return NilCounterFloat64{} - } - return &StandardCounterFloat64{} -} - -// NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if -// the global switch is enabled or not. -func NewCounterFloat64Forced() CounterFloat64 { - return &StandardCounterFloat64{} -} - -// NewRegisteredCounterFloat64 constructs and registers a new StandardCounterFloat64. -func NewRegisteredCounterFloat64(name string, r Registry) CounterFloat64 { - c := NewCounterFloat64() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterFloat64Forced constructs and registers a new StandardCounterFloat64 -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterFloat64Forced(name string, r Registry) CounterFloat64 { - c := NewCounterFloat64Forced() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// counterFloat64Snapshot is a read-only copy of another CounterFloat64. -type counterFloat64Snapshot float64 - -// Count returns the value at the time the snapshot was taken. -func (c counterFloat64Snapshot) Count() float64 { return float64(c) } - -type NilCounterFloat64 struct{} - -func (NilCounterFloat64) Clear() {} -func (NilCounterFloat64) Count() float64 { return 0.0 } -func (NilCounterFloat64) Dec(i float64) {} -func (NilCounterFloat64) Inc(i float64) {} -func (NilCounterFloat64) Snapshot() CounterFloat64Snapshot { return NilCounterFloat64{} } - -// StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the -// atomic to manage a single float64 value. -type StandardCounterFloat64 struct { - floatBits atomic.Uint64 -} - -// Clear sets the counter to zero. -func (c *StandardCounterFloat64) Clear() { - c.floatBits.Store(0) -} - -// Dec decrements the counter by the given amount. -func (c *StandardCounterFloat64) Dec(v float64) { - atomicAddFloat(&c.floatBits, -v) -} - -// Inc increments the counter by the given amount. -func (c *StandardCounterFloat64) Inc(v float64) { - atomicAddFloat(&c.floatBits, v) -} - -// Snapshot returns a read-only copy of the counter. -func (c *StandardCounterFloat64) Snapshot() CounterFloat64Snapshot { - v := math.Float64frombits(c.floatBits.Load()) - return counterFloat64Snapshot(v) -} - -func atomicAddFloat(fbits *atomic.Uint64, v float64) { - for { - loadedBits := fbits.Load() - newBits := math.Float64bits(math.Float64frombits(loadedBits) + v) - if fbits.CompareAndSwap(loadedBits, newBits) { - break - } - } -} diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go deleted file mode 100644 index c21bd3307f..0000000000 --- a/metrics/counter_float_64_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package metrics - -import ( - "sync" - "testing" -) - -func BenchmarkCounterFloat64(b *testing.B) { - c := NewCounterFloat64() - b.ResetTimer() - for i := 0; i < b.N; i++ { - c.Inc(1.0) - } -} - -func BenchmarkCounterFloat64Parallel(b *testing.B) { - c := NewCounterFloat64() - b.ResetTimer() - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - for i := 0; i < b.N; i++ { - c.Inc(1.0) - } - wg.Done() - }() - } - wg.Wait() - if have, want := c.Snapshot().Count(), 10.0*float64(b.N); have != want { - b.Fatalf("have %f want %f", have, want) - } -} - -func TestCounterFloat64Clear(t *testing.T) { - c := NewCounterFloat64() - c.Inc(1.0) - c.Clear() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - -func TestCounterFloat64Dec1(t *testing.T) { - c := NewCounterFloat64() - c.Dec(1.0) - if count := c.Snapshot().Count(); count != -1.0 { - t.Errorf("c.Count(): -1.0 != %v\n", count) - } -} - -func TestCounterFloat64Dec2(t *testing.T) { - c := NewCounterFloat64() - c.Dec(2.0) - if count := c.Snapshot().Count(); count != -2.0 { - t.Errorf("c.Count(): -2.0 != %v\n", count) - } -} - -func TestCounterFloat64Inc1(t *testing.T) { - c := NewCounterFloat64() - c.Inc(1.0) - if count := c.Snapshot().Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) - } -} - -func TestCounterFloat64Inc2(t *testing.T) { - c := NewCounterFloat64() - c.Inc(2.0) - if count := c.Snapshot().Count(); count != 2.0 { - t.Errorf("c.Count(): 2.0 != %v\n", count) - } -} - -func TestCounterFloat64Snapshot(t *testing.T) { - c := NewCounterFloat64() - c.Inc(1.0) - snapshot := c.Snapshot() - c.Inc(1.0) - if count := snapshot.Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) - } -} - -func TestCounterFloat64Zero(t *testing.T) { - c := NewCounterFloat64() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - -func TestGetOrRegisterCounterFloat64(t *testing.T) { - r := NewRegistry() - NewRegisteredCounterFloat64("foo", r).Inc(47.0) - if c := GetOrRegisterCounterFloat64("foo", r).Snapshot(); c.Count() != 47.0 { - t.Fatal(c) - } -} diff --git a/metrics/counter_test.go b/metrics/counter_test.go deleted file mode 100644 index 1b15b23f21..0000000000 --- a/metrics/counter_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package metrics - -import "testing" - -func BenchmarkCounter(b *testing.B) { - c := NewCounter() - b.ResetTimer() - for i := 0; i < b.N; i++ { - c.Inc(1) - } -} - -func TestCounterClear(t *testing.T) { - c := NewCounter() - c.Inc(1) - c.Clear() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - -func TestCounterDec1(t *testing.T) { - c := NewCounter() - c.Dec(1) - if count := c.Snapshot().Count(); count != -1 { - t.Errorf("c.Count(): -1 != %v\n", count) - } -} - -func TestCounterDec2(t *testing.T) { - c := NewCounter() - c.Dec(2) - if count := c.Snapshot().Count(); count != -2 { - t.Errorf("c.Count(): -2 != %v\n", count) - } -} - -func TestCounterInc1(t *testing.T) { - c := NewCounter() - c.Inc(1) - if count := c.Snapshot().Count(); count != 1 { - t.Errorf("c.Count(): 1 != %v\n", count) - } -} - -func TestCounterInc2(t *testing.T) { - c := NewCounter() - c.Inc(2) - if count := c.Snapshot().Count(); count != 2 { - t.Errorf("c.Count(): 2 != %v\n", count) - } -} - -func TestCounterSnapshot(t *testing.T) { - c := NewCounter() - c.Inc(1) - snapshot := c.Snapshot() - c.Inc(1) - if count := snapshot.Count(); count != 1 { - t.Errorf("c.Count(): 1 != %v\n", count) - } -} - -func TestCounterZero(t *testing.T) { - c := NewCounter() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - -func TestGetOrRegisterCounter(t *testing.T) { - r := NewRegistry() - NewRegisteredCounter("foo", r).Inc(47) - if c := GetOrRegisterCounter("foo", r).Snapshot(); c.Count() != 47 { - t.Fatal(c) - } -} diff --git a/metrics/cpu.go b/metrics/cpu.go deleted file mode 100644 index 472a1a42d8..0000000000 --- a/metrics/cpu.go +++ /dev/null @@ -1,35 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// CPUStats is the system and process CPU stats. -// All values are in seconds. -type CPUStats struct { - GlobalTime float64 // Time spent by the CPU working on all processes - GlobalWait float64 // Time spent by waiting on disk for all processes - LocalTime float64 // Time spent by the CPU working on this process -} diff --git a/metrics/cpu_disabled.go b/metrics/cpu_disabled.go deleted file mode 100644 index f2c3ead5db..0000000000 --- a/metrics/cpu_disabled.go +++ /dev/null @@ -1,34 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//go:build ios || js -// +build ios js - -package metrics - -// ReadCPUStats retrieves the current CPU stats. Internally this uses `gosigar`, -// which is not supported on the platforms in this file. -func ReadCPUStats(stats *CPUStats) {} diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go deleted file mode 100644 index 7b5fe4d207..0000000000 --- a/metrics/cpu_enabled.go +++ /dev/null @@ -1,54 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//go:build !ios && !js -// +build !ios,!js - -package metrics - -import ( - "github.com/ethereum/go-ethereum/log" - "github.com/shirou/gopsutil/cpu" -) - -// ReadCPUStats retrieves the current CPU stats. -func ReadCPUStats(stats *CPUStats) { - // passing false to request all cpu times - timeStats, err := cpu.Times(false) - if err != nil { - log.Error("Could not read cpu stats", "err", err) - return - } - if len(timeStats) == 0 { - log.Error("Empty cpu stats") - return - } - // requesting all cpu times will always return an array with only one time stats entry - timeStat := timeStats[0] - stats.GlobalTime = timeStat.User + timeStat.Nice + timeStat.System - stats.GlobalWait = timeStat.Iowait - stats.LocalTime = getProcessCPUTime() -} diff --git a/metrics/cputime_nop.go b/metrics/cputime_nop.go deleted file mode 100644 index 275b983717..0000000000 --- a/metrics/cputime_nop.go +++ /dev/null @@ -1,36 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//go:build windows || js -// +build windows js - -package metrics - -// getProcessCPUTime returns 0 on Windows as there is no system call to resolve -// the actual process' CPU time. -func getProcessCPUTime() float64 { - return 0 -} diff --git a/metrics/cputime_unix.go b/metrics/cputime_unix.go deleted file mode 100644 index 5a479d8aa8..0000000000 --- a/metrics/cputime_unix.go +++ /dev/null @@ -1,46 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//go:build !windows && !js -// +build !windows,!js - -package metrics - -import ( - syscall "golang.org/x/sys/unix" - - "github.com/ethereum/go-ethereum/log" -) - -// getProcessCPUTime retrieves the process' CPU time since program startup. -func getProcessCPUTime() float64 { - var usage syscall.Rusage - if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { - log.Warn("Failed to retrieve CPU time", "err", err) - return 0 - } - return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1000000 //nolint:unconvert -} diff --git a/metrics/debug.go b/metrics/debug.go deleted file mode 100644 index de4a2739fe..0000000000 --- a/metrics/debug.go +++ /dev/null @@ -1,76 +0,0 @@ -package metrics - -import ( - "runtime/debug" - "time" -) - -var ( - debugMetrics struct { - GCStats struct { - LastGC Gauge - NumGC Gauge - Pause Histogram - //PauseQuantiles Histogram - PauseTotal Gauge - } - ReadGCStats Timer - } - gcStats debug.GCStats -) - -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called as a goroutine. -func CaptureDebugGCStats(r Registry, d time.Duration) { - for range time.Tick(d) { - CaptureDebugGCStatsOnce(r) - } -} - -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called in a background goroutine. -// Giving a registry which has not been given to RegisterDebugGCStats will -// panic. -// -// Be careful (but much less so) with this because debug.ReadGCStats calls -// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world -// operation, isn't something you want to be doing all the time. -func CaptureDebugGCStatsOnce(r Registry) { - lastGC := gcStats.LastGC - t := time.Now() - debug.ReadGCStats(&gcStats) - debugMetrics.ReadGCStats.UpdateSince(t) - - debugMetrics.GCStats.LastGC.Update(gcStats.LastGC.UnixNano()) - debugMetrics.GCStats.NumGC.Update(gcStats.NumGC) - if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { - debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) - } - //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) - debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) -} - -// Register metrics for the Go garbage collector statistics exported in -// debug.GCStats. The metrics are named by their fully-qualified Go symbols, -// i.e. debug.GCStats.PauseTotal. -func RegisterDebugGCStats(r Registry) { - debugMetrics.GCStats.LastGC = NewGauge() - debugMetrics.GCStats.NumGC = NewGauge() - debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015)) - //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015)) - debugMetrics.GCStats.PauseTotal = NewGauge() - debugMetrics.ReadGCStats = NewTimer() - - r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) - r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) - r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) - //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles) - r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) - r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) -} - -// Allocate an initial slice for gcStats.Pause to avoid allocations during -// normal operation. -func init() { - gcStats.Pause = make([]time.Duration, 11) -} diff --git a/metrics/debug_test.go b/metrics/debug_test.go deleted file mode 100644 index 07eb867841..0000000000 --- a/metrics/debug_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package metrics - -import ( - "runtime" - "runtime/debug" - "testing" - "time" -) - -func BenchmarkDebugGCStats(b *testing.B) { - r := NewRegistry() - RegisterDebugGCStats(r) - b.ResetTimer() - for i := 0; i < b.N; i++ { - CaptureDebugGCStatsOnce(r) - } -} - -func TestDebugGCStatsBlocking(t *testing.T) { - if g := runtime.GOMAXPROCS(0); g < 2 { - t.Skipf("skipping TestDebugGCMemStatsBlocking with GOMAXPROCS=%d\n", g) - return - } - ch := make(chan int) - go testDebugGCStatsBlocking(ch) - var gcStats debug.GCStats - t0 := time.Now() - debug.ReadGCStats(&gcStats) - t1 := time.Now() - t.Log("i++ during debug.ReadGCStats:", <-ch) - go testDebugGCStatsBlocking(ch) - d := t1.Sub(t0) - t.Log(d) - time.Sleep(d) - t.Log("i++ during time.Sleep:", <-ch) -} - -func testDebugGCStatsBlocking(ch chan int) { - i := 0 - for { - select { - case ch <- i: - return - default: - i++ - } - } -} diff --git a/metrics/disk.go b/metrics/disk.go deleted file mode 100644 index 1fdd32a4d3..0000000000 --- a/metrics/disk.go +++ /dev/null @@ -1,35 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// DiskStats is the per process disk io stats. -type DiskStats struct { - ReadCount int64 // Number of read operations executed - ReadBytes int64 // Total number of bytes read - WriteCount int64 // Number of write operations executed - WriteBytes int64 // Total number of byte written -} diff --git a/metrics/disk_linux.go b/metrics/disk_linux.go deleted file mode 100644 index 25341d748a..0000000000 --- a/metrics/disk_linux.go +++ /dev/null @@ -1,82 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Linux implementation of process disk IO counter retrieval. - -package metrics - -import ( - "bufio" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -// ReadDiskStats retrieves the disk IO stats belonging to the current process. -func ReadDiskStats(stats *DiskStats) error { - // Open the process disk IO counter file - inf, err := os.Open(fmt.Sprintf("/proc/%d/io", os.Getpid())) - if err != nil { - return err - } - defer inf.Close() - in := bufio.NewReader(inf) - - // Iterate over the IO counter, and extract what we need - for { - // Read the next line and split to key and value - line, err := in.ReadString('\n') - if err != nil { - if err == io.EOF { - return nil - } - return err - } - parts := strings.Split(line, ":") - if len(parts) != 2 { - continue - } - key := strings.TrimSpace(parts[0]) - value, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64) - if err != nil { - return err - } - - // Update the counter based on the key - switch key { - case "syscr": - stats.ReadCount = value - case "syscw": - stats.WriteCount = value - case "rchar": - stats.ReadBytes = value - case "wchar": - stats.WriteBytes = value - } - } -} diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go deleted file mode 100644 index b1d6ff9f5d..0000000000 --- a/metrics/disk_nop.go +++ /dev/null @@ -1,37 +0,0 @@ -// (c) 2022, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -//go:build !linux -// +build !linux - -package metrics - -import "errors" - -// ReadDiskStats retrieves the disk IO stats belonging to the current process. -func ReadDiskStats(stats *DiskStats) error { - return errors.New("not implemented") -} diff --git a/metrics/ewma.go b/metrics/ewma.go deleted file mode 100644 index 1d7a4f00cf..0000000000 --- a/metrics/ewma.go +++ /dev/null @@ -1,111 +0,0 @@ -package metrics - -import ( - "math" - "sync" - "sync/atomic" - "time" -) - -type EWMASnapshot interface { - Rate() float64 -} - -// EWMAs continuously calculate an exponentially-weighted moving average -// based on an outside source of clock ticks. -type EWMA interface { - Snapshot() EWMASnapshot - Tick() - Update(int64) -} - -// NewEWMA constructs a new EWMA with the given alpha. -func NewEWMA(alpha float64) EWMA { - return &StandardEWMA{alpha: alpha} -} - -// NewEWMA1 constructs a new EWMA for a one-minute moving average. -func NewEWMA1() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/1)) -} - -// NewEWMA5 constructs a new EWMA for a five-minute moving average. -func NewEWMA5() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/5)) -} - -// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. -func NewEWMA15() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/15)) -} - -// ewmaSnapshot is a read-only copy of another EWMA. -type ewmaSnapshot float64 - -// Rate returns the rate of events per second at the time the snapshot was -// taken. -func (a ewmaSnapshot) Rate() float64 { return float64(a) } - -// NilEWMA is a no-op EWMA. -type NilEWMA struct{} - -func (NilEWMA) Snapshot() EWMASnapshot { return (*emptySnapshot)(nil) } -func (NilEWMA) Tick() {} -func (NilEWMA) Update(n int64) {} - -// StandardEWMA is the standard implementation of an EWMA and tracks the number -// of uncounted events and processes them on each tick. It uses the -// sync/atomic package to manage uncounted events. -type StandardEWMA struct { - uncounted atomic.Int64 - alpha float64 - rate atomic.Uint64 - init atomic.Bool - mutex sync.Mutex -} - -// Snapshot returns a read-only copy of the EWMA. -func (a *StandardEWMA) Snapshot() EWMASnapshot { - r := math.Float64frombits(a.rate.Load()) * float64(time.Second) - return ewmaSnapshot(r) -} - -// Tick ticks the clock to update the moving average. It assumes it is called -// every five seconds. -func (a *StandardEWMA) Tick() { - // Optimization to avoid mutex locking in the hot-path. - if a.init.Load() { - a.updateRate(a.fetchInstantRate()) - return - } - // Slow-path: this is only needed on the first Tick() and preserves transactional updating - // of init and rate in the else block. The first conditional is needed below because - // a different thread could have set a.init = 1 between the time of the first atomic load and when - // the lock was acquired. - a.mutex.Lock() - if a.init.Load() { - // The fetchInstantRate() uses atomic loading, which is unnecessary in this critical section - // but again, this section is only invoked on the first successful Tick() operation. - a.updateRate(a.fetchInstantRate()) - } else { - a.init.Store(true) - a.rate.Store(math.Float64bits(a.fetchInstantRate())) - } - a.mutex.Unlock() -} - -func (a *StandardEWMA) fetchInstantRate() float64 { - count := a.uncounted.Swap(0) - return float64(count) / float64(5*time.Second) -} - -func (a *StandardEWMA) updateRate(instantRate float64) { - currentRate := math.Float64frombits(a.rate.Load()) - currentRate += a.alpha * (instantRate - currentRate) - a.rate.Store(math.Float64bits(currentRate)) -} - -// Update adds n uncounted events. -func (a *StandardEWMA) Update(n int64) { - a.uncounted.Add(n) -} diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go deleted file mode 100644 index 9a91b43db8..0000000000 --- a/metrics/ewma_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package metrics - -import ( - "math" - "testing" -) - -const epsilon = 0.0000000000000001 - -func BenchmarkEWMA(b *testing.B) { - a := NewEWMA1() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Update(1) - a.Tick() - } -} - -func BenchmarkEWMAParallel(b *testing.B) { - a := NewEWMA1() - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - a.Update(1) - a.Tick() - } - }) -} - -func TestEWMA1(t *testing.T) { - a := NewEWMA1() - a.Update(3) - a.Tick() - for i, want := range []float64{0.6, - 0.22072766470286553, 0.08120116994196772, 0.029872241020718428, - 0.01098938333324054, 0.004042768199451294, 0.0014872513059998212, - 0.0005471291793327122, 0.00020127757674150815, 7.404588245200814e-05, - 2.7239957857491083e-05, 1.0021020474147462e-05, 3.6865274119969525e-06, - 1.3561976441886433e-06, 4.989172314621449e-07, 1.8354139230109722e-07, - } { - if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { - t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) - } - elapseMinute(a) - } -} - -func TestEWMA5(t *testing.T) { - a := NewEWMA5() - a.Update(3) - a.Tick() - for i, want := range []float64{ - 0.6, 0.49123845184678905, 0.4021920276213837, 0.32928698165641596, - 0.269597378470333, 0.2207276647028654, 0.18071652714732128, - 0.14795817836496392, 0.12113791079679326, 0.09917933293295193, - 0.08120116994196763, 0.06648189501740036, 0.05443077197364752, - 0.04456414692860035, 0.03648603757513079, 0.0298722410207183831020718428, - } { - if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { - t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) - } - elapseMinute(a) - } -} - -func TestEWMA15(t *testing.T) { - a := NewEWMA15() - a.Update(3) - a.Tick() - for i, want := range []float64{ - 0.6, 0.5613041910189706, 0.5251039914257684, 0.4912384518467888184678905, - 0.459557003018789, 0.4299187863442732, 0.4021920276213831, - 0.37625345116383313, 0.3519877317060185, 0.3292869816564153165641596, - 0.3080502714195546, 0.2881831806538789, 0.26959737847033216, - 0.2522102307052083, 0.23594443252115815, 0.2207276647028646247028654470286553, - } { - if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon { - t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate) - } - elapseMinute(a) - } -} - -func elapseMinute(a EWMA) { - for i := 0; i < 12; i++ { - a.Tick() - } -} diff --git a/metrics/gauge.go b/metrics/gauge.go deleted file mode 100644 index 5933df3107..0000000000 --- a/metrics/gauge.go +++ /dev/null @@ -1,98 +0,0 @@ -package metrics - -import "sync/atomic" - -// GaugeSnapshot contains a readonly int64. -type GaugeSnapshot interface { - Value() int64 -} - -// Gauge holds an int64 value that can be set arbitrarily. -type Gauge interface { - Snapshot() GaugeSnapshot - Update(int64) - UpdateIfGt(int64) - Dec(int64) - Inc(int64) -} - -// GetOrRegisterGauge returns an existing Gauge or constructs and registers a -// new StandardGauge. -func GetOrRegisterGauge(name string, r Registry) Gauge { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewGauge).(Gauge) -} - -// NewGauge constructs a new StandardGauge. -func NewGauge() Gauge { - if !Enabled { - return NilGauge{} - } - return &StandardGauge{} -} - -// NewRegisteredGauge constructs and registers a new StandardGauge. -func NewRegisteredGauge(name string, r Registry) Gauge { - c := NewGauge() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// gaugeSnapshot is a read-only copy of another Gauge. -type gaugeSnapshot int64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeSnapshot) Value() int64 { return int64(g) } - -// NilGauge is a no-op Gauge. -type NilGauge struct{} - -func (NilGauge) Snapshot() GaugeSnapshot { return (*emptySnapshot)(nil) } -func (NilGauge) Update(v int64) {} -func (NilGauge) UpdateIfGt(v int64) {} -func (NilGauge) Dec(i int64) {} -func (NilGauge) Inc(i int64) {} - -// StandardGauge is the standard implementation of a Gauge and uses the -// sync/atomic package to manage a single int64 value. -type StandardGauge struct { - value atomic.Int64 -} - -// Snapshot returns a read-only copy of the gauge. -func (g *StandardGauge) Snapshot() GaugeSnapshot { - return gaugeSnapshot(g.value.Load()) -} - -// Update updates the gauge's value. -func (g *StandardGauge) Update(v int64) { - g.value.Store(v) -} - -// Update updates the gauge's value if v is larger then the current value. -func (g *StandardGauge) UpdateIfGt(v int64) { - for { - exist := g.value.Load() - if exist >= v { - break - } - if g.value.CompareAndSwap(exist, v) { - break - } - } -} - -// Dec decrements the gauge's current value by the given amount. -func (g *StandardGauge) Dec(i int64) { - g.value.Add(-i) -} - -// Inc increments the gauge's current value by the given amount. -func (g *StandardGauge) Inc(i int64) { - g.value.Add(i) -} diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go deleted file mode 100644 index c1c3c6b6e6..0000000000 --- a/metrics/gauge_float64.go +++ /dev/null @@ -1,73 +0,0 @@ -package metrics - -import ( - "math" - "sync/atomic" -) - -type GaugeFloat64Snapshot interface { - Value() float64 -} - -// GaugeFloat64 hold a float64 value that can be set arbitrarily. -type GaugeFloat64 interface { - Snapshot() GaugeFloat64Snapshot - Update(float64) -} - -// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a -// new StandardGaugeFloat64. -func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) -} - -// NewGaugeFloat64 constructs a new StandardGaugeFloat64. -func NewGaugeFloat64() GaugeFloat64 { - if !Enabled { - return NilGaugeFloat64{} - } - return &StandardGaugeFloat64{} -} - -// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. -func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { - c := NewGaugeFloat64() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// gaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. -type gaugeFloat64Snapshot float64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } - -// NilGaugeFloat64 is a no-op Gauge. -type NilGaugeFloat64 struct{} - -func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } -func (NilGaugeFloat64) Update(v float64) {} -func (NilGaugeFloat64) Value() float64 { return 0.0 } - -// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses -// atomic to manage a single float64 value. -type StandardGaugeFloat64 struct { - floatBits atomic.Uint64 -} - -// Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64Snapshot { - v := math.Float64frombits(g.floatBits.Load()) - return gaugeFloat64Snapshot(v) -} - -// Update updates the gauge's value. -func (g *StandardGaugeFloat64) Update(v float64) { - g.floatBits.Store(math.Float64bits(v)) -} diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go deleted file mode 100644 index 194a18821f..0000000000 --- a/metrics/gauge_float64_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package metrics - -import ( - "sync" - "testing" -) - -func BenchmarkGaugeFloat64(b *testing.B) { - g := NewGaugeFloat64() - b.ResetTimer() - for i := 0; i < b.N; i++ { - g.Update(float64(i)) - } -} - -func BenchmarkGaugeFloat64Parallel(b *testing.B) { - c := NewGaugeFloat64() - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - for i := 0; i < b.N; i++ { - c.Update(float64(i)) - } - wg.Done() - }() - } - wg.Wait() - if have, want := c.Snapshot().Value(), float64(b.N-1); have != want { - b.Fatalf("have %f want %f", have, want) - } -} - -func TestGaugeFloat64Snapshot(t *testing.T) { - g := NewGaugeFloat64() - g.Update(47.0) - snapshot := g.Snapshot() - g.Update(float64(0)) - if v := snapshot.Value(); v != 47.0 { - t.Errorf("g.Value(): 47.0 != %v\n", v) - } -} - -func TestGetOrRegisterGaugeFloat64(t *testing.T) { - r := NewRegistry() - NewRegisteredGaugeFloat64("foo", r).Update(47.0) - t.Logf("registry: %v", r) - if g := GetOrRegisterGaugeFloat64("foo", r).Snapshot(); g.Value() != 47.0 { - t.Fatal(g) - } -} diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go deleted file mode 100644 index 0010edc324..0000000000 --- a/metrics/gauge_info.go +++ /dev/null @@ -1,84 +0,0 @@ -package metrics - -import ( - "encoding/json" - "sync" -) - -type GaugeInfoSnapshot interface { - Value() GaugeInfoValue -} - -// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily. -type GaugeInfo interface { - Update(GaugeInfoValue) - Snapshot() GaugeInfoSnapshot -} - -// GaugeInfoValue is a mapping of keys to values -type GaugeInfoValue map[string]string - -func (val GaugeInfoValue) String() string { - data, _ := json.Marshal(val) - return string(data) -} - -// GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a -// new StandardGaugeInfo. -func GetOrRegisterGaugeInfo(name string, r Registry) GaugeInfo { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewGaugeInfo()).(GaugeInfo) -} - -// NewGaugeInfo constructs a new StandardGaugeInfo. -func NewGaugeInfo() GaugeInfo { - if !Enabled { - return NilGaugeInfo{} - } - return &StandardGaugeInfo{ - value: GaugeInfoValue{}, - } -} - -// NewRegisteredGaugeInfo constructs and registers a new StandardGaugeInfo. -func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { - c := NewGaugeInfo() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// gaugeInfoSnapshot is a read-only copy of another GaugeInfo. -type gaugeInfoSnapshot GaugeInfoValue - -// Value returns the value at the time the snapshot was taken. -func (g gaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } - -type NilGaugeInfo struct{} - -func (NilGaugeInfo) Snapshot() GaugeInfoSnapshot { return NilGaugeInfo{} } -func (NilGaugeInfo) Update(v GaugeInfoValue) {} -func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } - -// StandardGaugeInfo is the standard implementation of a GaugeInfo and uses -// sync.Mutex to manage a single string value. -type StandardGaugeInfo struct { - mutex sync.Mutex - value GaugeInfoValue -} - -// Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeInfo) Snapshot() GaugeInfoSnapshot { - return gaugeInfoSnapshot(g.value) -} - -// Update updates the gauge's value. -func (g *StandardGaugeInfo) Update(v GaugeInfoValue) { - g.mutex.Lock() - defer g.mutex.Unlock() - g.value = v -} diff --git a/metrics/gauge_info_test.go b/metrics/gauge_info_test.go deleted file mode 100644 index 319afbf92e..0000000000 --- a/metrics/gauge_info_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package metrics - -import ( - "testing" -) - -func TestGaugeInfoJsonString(t *testing.T) { - g := NewGaugeInfo() - g.Update(GaugeInfoValue{ - "chain_id": "5", - "anotherKey": "any_string_value", - "third_key": "anything", - }, - ) - want := `{"anotherKey":"any_string_value","chain_id":"5","third_key":"anything"}` - - original := g.Snapshot() - g.Update(GaugeInfoValue{"value": "updated"}) - - if have := original.Value().String(); have != want { - t.Errorf("\nhave: %v\nwant: %v\n", have, want) - } - if have, want := g.Snapshot().Value().String(), `{"value":"updated"}`; have != want { - t.Errorf("\nhave: %v\nwant: %v\n", have, want) - } -} - -func TestGetOrRegisterGaugeInfo(t *testing.T) { - r := NewRegistry() - NewRegisteredGaugeInfo("foo", r).Update( - GaugeInfoValue{"chain_id": "5"}) - g := GetOrRegisterGaugeInfo("foo", r).Snapshot() - if have, want := g.Value().String(), `{"chain_id":"5"}`; have != want { - t.Errorf("have\n%v\nwant\n%v\n", have, want) - } -} diff --git a/metrics/gauge_test.go b/metrics/gauge_test.go deleted file mode 100644 index f2ba930bc4..0000000000 --- a/metrics/gauge_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package metrics - -import ( - "testing" -) - -func BenchmarkGauge(b *testing.B) { - g := NewGauge() - b.ResetTimer() - for i := 0; i < b.N; i++ { - g.Update(int64(i)) - } -} - -func TestGaugeSnapshot(t *testing.T) { - g := NewGauge() - g.Update(int64(47)) - snapshot := g.Snapshot() - g.Update(int64(0)) - if v := snapshot.Value(); v != 47 { - t.Errorf("g.Value(): 47 != %v\n", v) - } -} - -func TestGetOrRegisterGauge(t *testing.T) { - r := NewRegistry() - NewRegisteredGauge("foo", r).Update(47) - if g := GetOrRegisterGauge("foo", r); g.Snapshot().Value() != 47 { - t.Fatal(g) - } -} diff --git a/metrics/graphite.go b/metrics/graphite.go deleted file mode 100644 index aba752e0ed..0000000000 --- a/metrics/graphite.go +++ /dev/null @@ -1,117 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "log" - "net" - "strconv" - "strings" - "time" -) - -// GraphiteConfig provides a container with configuration parameters for -// the Graphite exporter -type GraphiteConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names - Percentiles []float64 // Percentiles to export from timers and histograms -} - -// Graphite is a blocking exporter function which reports metrics in r -// to a graphite server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, - }) -} - -// GraphiteWithConfig is a blocking exporter function just like Graphite, -// but it takes a GraphiteConfig instead. -func GraphiteWithConfig(c GraphiteConfig) { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - for range time.Tick(c.FlushInterval) { - if err := graphite(&c); nil != err { - log.Println(err) - } - } -} - -// GraphiteOnce performs a single submission to Graphite, returning a -// non-nil error on failed connections. This can be used in a loop -// similar to GraphiteWithConfig for custom error handling. -func GraphiteOnce(c GraphiteConfig) error { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - return graphite(&c) -} - -func graphite(c *GraphiteConfig) error { - now := time.Now().Unix() - du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case CounterFloat64: - fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case Gauge: - fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeFloat64: - fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeInfo: - fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Snapshot().Value().String(), now) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) - } - w.Flush() - }) - return nil -} diff --git a/metrics/graphite_test.go b/metrics/graphite_test.go deleted file mode 100644 index c797c781df..0000000000 --- a/metrics/graphite_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package metrics - -import ( - "net" - "time" -) - -func ExampleGraphite() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr) -} - -func ExampleGraphiteWithConfig() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: DefaultRegistry, - FlushInterval: 1 * time.Second, - DurationUnit: time.Millisecond, - Percentiles: []float64{0.5, 0.75, 0.99, 0.999}, - }) -} diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go deleted file mode 100644 index adcd15ab58..0000000000 --- a/metrics/healthcheck.go +++ /dev/null @@ -1,61 +0,0 @@ -package metrics - -// Healthcheck holds an error value describing an arbitrary up/down status. -type Healthcheck interface { - Check() - Error() error - Healthy() - Unhealthy(error) -} - -// NewHealthcheck constructs a new Healthcheck which will use the given -// function to update its status. -func NewHealthcheck(f func(Healthcheck)) Healthcheck { - if !Enabled { - return NilHealthcheck{} - } - return &StandardHealthcheck{nil, f} -} - -// NilHealthcheck is a no-op. -type NilHealthcheck struct{} - -// Check is a no-op. -func (NilHealthcheck) Check() {} - -// Error is a no-op. -func (NilHealthcheck) Error() error { return nil } - -// Healthy is a no-op. -func (NilHealthcheck) Healthy() {} - -// Unhealthy is a no-op. -func (NilHealthcheck) Unhealthy(error) {} - -// StandardHealthcheck is the standard implementation of a Healthcheck and -// stores the status and a function to call to update the status. -type StandardHealthcheck struct { - err error - f func(Healthcheck) -} - -// Check runs the healthcheck function to update the healthcheck's status. -func (h *StandardHealthcheck) Check() { - h.f(h) -} - -// Error returns the healthcheck's status, which will be nil if it is healthy. -func (h *StandardHealthcheck) Error() error { - return h.err -} - -// Healthy marks the healthcheck as healthy. -func (h *StandardHealthcheck) Healthy() { - h.err = nil -} - -// Unhealthy marks the healthcheck as unhealthy. The error is stored and -// may be retrieved by the Error method. -func (h *StandardHealthcheck) Unhealthy(err error) { - h.err = err -} diff --git a/metrics/histogram.go b/metrics/histogram.go deleted file mode 100644 index 10259a2463..0000000000 --- a/metrics/histogram.go +++ /dev/null @@ -1,73 +0,0 @@ -package metrics - -type HistogramSnapshot interface { - SampleSnapshot -} - -// Histogram calculates distribution statistics from a series of int64 values. -type Histogram interface { - Clear() - Update(int64) - Snapshot() HistogramSnapshot -} - -// GetOrRegisterHistogram returns an existing Histogram or constructs and -// registers a new StandardHistogram. -func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) -} - -// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and -// registers a new StandardHistogram. -func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, func() Histogram { return NewHistogram(s()) }).(Histogram) -} - -// NewHistogram constructs a new StandardHistogram from a Sample. -func NewHistogram(s Sample) Histogram { - if !Enabled { - return NilHistogram{} - } - return &StandardHistogram{sample: s} -} - -// NewRegisteredHistogram constructs and registers a new StandardHistogram from -// a Sample. -func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { - c := NewHistogram(s) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NilHistogram is a no-op Histogram. -type NilHistogram struct{} - -func (NilHistogram) Clear() {} -func (NilHistogram) Snapshot() HistogramSnapshot { return (*emptySnapshot)(nil) } -func (NilHistogram) Update(v int64) {} - -// StandardHistogram is the standard implementation of a Histogram and uses a -// Sample to bound its memory use. -type StandardHistogram struct { - sample Sample -} - -// Clear clears the histogram and its sample. -func (h *StandardHistogram) Clear() { h.sample.Clear() } - -// Snapshot returns a read-only copy of the histogram. -func (h *StandardHistogram) Snapshot() HistogramSnapshot { - return h.sample.Snapshot() -} - -// Update samples a new value. -func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) } diff --git a/metrics/histogram_test.go b/metrics/histogram_test.go deleted file mode 100644 index 22fc5468b0..0000000000 --- a/metrics/histogram_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package metrics - -import "testing" - -func BenchmarkHistogram(b *testing.B) { - h := NewHistogram(NewUniformSample(100)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - h.Update(int64(i)) - } -} - -func TestGetOrRegisterHistogram(t *testing.T) { - r := NewRegistry() - s := NewUniformSample(100) - NewRegisteredHistogram("foo", r, s).Update(47) - if h := GetOrRegisterHistogram("foo", r, s).Snapshot(); h.Count() != 1 { - t.Fatal(h) - } -} - -func TestHistogram10000(t *testing.T) { - h := NewHistogram(NewUniformSample(100000)) - for i := 1; i <= 10000; i++ { - h.Update(int64(i)) - } - testHistogram10000(t, h.Snapshot()) -} - -func TestHistogramEmpty(t *testing.T) { - h := NewHistogram(NewUniformSample(100)).Snapshot() - if count := h.Count(); count != 0 { - t.Errorf("h.Count(): 0 != %v\n", count) - } - if min := h.Min(); min != 0 { - t.Errorf("h.Min(): 0 != %v\n", min) - } - if max := h.Max(); max != 0 { - t.Errorf("h.Max(): 0 != %v\n", max) - } - if mean := h.Mean(); mean != 0.0 { - t.Errorf("h.Mean(): 0.0 != %v\n", mean) - } - if stdDev := h.StdDev(); stdDev != 0.0 { - t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev) - } - ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) - if ps[0] != 0.0 { - t.Errorf("median: 0.0 != %v\n", ps[0]) - } - if ps[1] != 0.0 { - t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) - } - if ps[2] != 0.0 { - t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) - } -} - -func TestHistogramSnapshot(t *testing.T) { - h := NewHistogram(NewUniformSample(100000)) - for i := 1; i <= 10000; i++ { - h.Update(int64(i)) - } - snapshot := h.Snapshot() - h.Update(0) - testHistogram10000(t, snapshot) -} - -func testHistogram10000(t *testing.T, h HistogramSnapshot) { - if count := h.Count(); count != 10000 { - t.Errorf("h.Count(): 10000 != %v\n", count) - } - if min := h.Min(); min != 1 { - t.Errorf("h.Min(): 1 != %v\n", min) - } - if max := h.Max(); max != 10000 { - t.Errorf("h.Max(): 10000 != %v\n", max) - } - if mean := h.Mean(); mean != 5000.5 { - t.Errorf("h.Mean(): 5000.5 != %v\n", mean) - } - if stdDev := h.StdDev(); stdDev != 2886.751331514372 { - t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev) - } - ps := h.Percentiles([]float64{0.5, 0.75, 0.99}) - if ps[0] != 5000.5 { - t.Errorf("median: 5000.5 != %v\n", ps[0]) - } - if ps[1] != 7500.75 { - t.Errorf("75th percentile: 7500.75 != %v\n", ps[1]) - } - if ps[2] != 9900.99 { - t.Errorf("99th percentile: 9900.99 != %v\n", ps[2]) - } -} diff --git a/metrics/inactive.go b/metrics/inactive.go deleted file mode 100644 index 1f47f0210a..0000000000 --- a/metrics/inactive.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// compile-time checks that interfaces are implemented. -var ( - _ SampleSnapshot = (*emptySnapshot)(nil) - _ HistogramSnapshot = (*emptySnapshot)(nil) - _ CounterSnapshot = (*emptySnapshot)(nil) - _ GaugeSnapshot = (*emptySnapshot)(nil) - _ MeterSnapshot = (*emptySnapshot)(nil) - _ EWMASnapshot = (*emptySnapshot)(nil) - _ TimerSnapshot = (*emptySnapshot)(nil) -) - -type emptySnapshot struct{} - -func (*emptySnapshot) Count() int64 { return 0 } -func (*emptySnapshot) Max() int64 { return 0 } -func (*emptySnapshot) Mean() float64 { return 0.0 } -func (*emptySnapshot) Min() int64 { return 0 } -func (*emptySnapshot) Percentile(p float64) float64 { return 0.0 } -func (*emptySnapshot) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -func (*emptySnapshot) Size() int { return 0 } -func (*emptySnapshot) StdDev() float64 { return 0.0 } -func (*emptySnapshot) Sum() int64 { return 0 } -func (*emptySnapshot) Values() []int64 { return []int64{} } -func (*emptySnapshot) Variance() float64 { return 0.0 } -func (*emptySnapshot) Value() int64 { return 0 } -func (*emptySnapshot) Rate() float64 { return 0.0 } -func (*emptySnapshot) Rate1() float64 { return 0.0 } -func (*emptySnapshot) Rate5() float64 { return 0.0 } -func (*emptySnapshot) Rate15() float64 { return 0.0 } -func (*emptySnapshot) RateMean() float64 { return 0.0 } diff --git a/metrics/json.go b/metrics/json.go deleted file mode 100644 index 2087d8211e..0000000000 --- a/metrics/json.go +++ /dev/null @@ -1,31 +0,0 @@ -package metrics - -import ( - "encoding/json" - "io" - "time" -) - -// MarshalJSON returns a byte slice containing a JSON representation of all -// the metrics in the Registry. -func (r *StandardRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(r.GetAll()) -} - -// WriteJSON writes metrics from the given registry periodically to the -// specified io.Writer as JSON. -func WriteJSON(r Registry, d time.Duration, w io.Writer) { - for range time.Tick(d) { - WriteJSONOnce(r, w) - } -} - -// WriteJSONOnce writes metrics from the given registry to the specified -// io.Writer as JSON. -func WriteJSONOnce(r Registry, w io.Writer) { - json.NewEncoder(w).Encode(r) -} - -func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(p.GetAll()) -} diff --git a/metrics/json_test.go b/metrics/json_test.go deleted file mode 100644 index f91fe8cfa5..0000000000 --- a/metrics/json_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package metrics - -import ( - "bytes" - "encoding/json" - "testing" -) - -func TestRegistryMarshallJSON(t *testing.T) { - b := &bytes.Buffer{} - enc := json.NewEncoder(b) - r := NewRegistry() - r.Register("counter", NewCounter()) - enc.Encode(r) - if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" { - t.Fatalf(s) - } -} - -func TestRegistryWriteJSONOnce(t *testing.T) { - r := NewRegistry() - r.Register("counter", NewCounter()) - b := &bytes.Buffer{} - WriteJSONOnce(r, b) - if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" { - t.Fail() - } -} diff --git a/metrics/log.go b/metrics/log.go deleted file mode 100644 index 3b9773faa7..0000000000 --- a/metrics/log.go +++ /dev/null @@ -1,86 +0,0 @@ -package metrics - -import ( - "time" -) - -type Logger interface { - Printf(format string, v ...interface{}) -} - -func Log(r Registry, freq time.Duration, l Logger) { - LogScaled(r, freq, time.Nanosecond, l) -} - -// Output each metric in the given registry periodically using the given -// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. -func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { - du := float64(scale) - duSuffix := scale.String()[1:] - - for range time.Tick(freq) { - r.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - l.Printf("counter %s\n", name) - l.Printf(" count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: - l.Printf("counter %s\n", name) - l.Printf(" count: %f\n", metric.Snapshot().Count()) - case Gauge: - l.Printf("gauge %s\n", name) - l.Printf(" value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: - l.Printf("gauge %s\n", name) - l.Printf(" value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: - l.Printf("gauge %s\n", name) - l.Printf(" value: %s\n", metric.Snapshot().Value()) - case Healthcheck: - metric.Check() - l.Printf("healthcheck %s\n", name) - l.Printf(" error: %v\n", metric.Error()) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - l.Printf("histogram %s\n", name) - l.Printf(" count: %9d\n", h.Count()) - l.Printf(" min: %9d\n", h.Min()) - l.Printf(" max: %9d\n", h.Max()) - l.Printf(" mean: %12.2f\n", h.Mean()) - l.Printf(" stddev: %12.2f\n", h.StdDev()) - l.Printf(" median: %12.2f\n", ps[0]) - l.Printf(" 75%%: %12.2f\n", ps[1]) - l.Printf(" 95%%: %12.2f\n", ps[2]) - l.Printf(" 99%%: %12.2f\n", ps[3]) - l.Printf(" 99.9%%: %12.2f\n", ps[4]) - case Meter: - m := metric.Snapshot() - l.Printf("meter %s\n", name) - l.Printf(" count: %9d\n", m.Count()) - l.Printf(" 1-min rate: %12.2f\n", m.Rate1()) - l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) - l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) - l.Printf(" mean rate: %12.2f\n", m.RateMean()) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - l.Printf("timer %s\n", name) - l.Printf(" count: %9d\n", t.Count()) - l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix) - l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix) - l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix) - l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix) - l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix) - l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix) - l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix) - l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix) - l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix) - l.Printf(" 1-min rate: %12.2f\n", t.Rate1()) - l.Printf(" 5-min rate: %12.2f\n", t.Rate5()) - l.Printf(" 15-min rate: %12.2f\n", t.Rate15()) - l.Printf(" mean rate: %12.2f\n", t.RateMean()) - } - }) - } -} diff --git a/metrics/memory.md b/metrics/memory.md deleted file mode 100644 index 47454f54b6..0000000000 --- a/metrics/memory.md +++ /dev/null @@ -1,285 +0,0 @@ -Memory usage -============ - -(Highly unscientific.) - -Command used to gather static memory usage: - -```sh -grep ^Vm "/proc/$(ps fax | grep [m]etrics-bench | awk '{print $1}')/status" -``` - -Program used to gather baseline memory usage: - -```go -package main - -import "time" - -func main() { - time.Sleep(600e9) -} -``` - -Baseline --------- - -``` -VmPeak: 42604 kB -VmSize: 42604 kB -VmLck: 0 kB -VmHWM: 1120 kB -VmRSS: 1120 kB -VmData: 35460 kB -VmStk: 136 kB -VmExe: 1020 kB -VmLib: 1848 kB -VmPTE: 36 kB -VmSwap: 0 kB -``` - -Program used to gather metric memory usage (with other metrics being similar): - -```go -package main - -import ( - "fmt" - "metrics" - "time" -) - -func main() { - fmt.Sprintf("foo") - metrics.NewRegistry() - time.Sleep(600e9) -} -``` - -1000 counters registered ------------------------- - -``` -VmPeak: 44016 kB -VmSize: 44016 kB -VmLck: 0 kB -VmHWM: 1928 kB -VmRSS: 1928 kB -VmData: 36868 kB -VmStk: 136 kB -VmExe: 1024 kB -VmLib: 1848 kB -VmPTE: 40 kB -VmSwap: 0 kB -``` - -**1.412 kB virtual, TODO 0.808 kB resident per counter.** - -100000 counters registered --------------------------- - -``` -VmPeak: 55024 kB -VmSize: 55024 kB -VmLck: 0 kB -VmHWM: 12440 kB -VmRSS: 12440 kB -VmData: 47876 kB -VmStk: 136 kB -VmExe: 1024 kB -VmLib: 1848 kB -VmPTE: 64 kB -VmSwap: 0 kB -``` - -**0.1242 kB virtual, 0.1132 kB resident per counter.** - -1000 gauges registered ----------------------- - -``` -VmPeak: 44012 kB -VmSize: 44012 kB -VmLck: 0 kB -VmHWM: 1928 kB -VmRSS: 1928 kB -VmData: 36868 kB -VmStk: 136 kB -VmExe: 1020 kB -VmLib: 1848 kB -VmPTE: 40 kB -VmSwap: 0 kB -``` - -**1.408 kB virtual, 0.808 kB resident per counter.** - -100000 gauges registered ------------------------- - -``` -VmPeak: 55020 kB -VmSize: 55020 kB -VmLck: 0 kB -VmHWM: 12432 kB -VmRSS: 12432 kB -VmData: 47876 kB -VmStk: 136 kB -VmExe: 1020 kB -VmLib: 1848 kB -VmPTE: 60 kB -VmSwap: 0 kB -``` - -**0.12416 kB virtual, 0.11312 resident per gauge.** - -1000 histograms with a uniform sample size of 1028 --------------------------------------------------- - -``` -VmPeak: 72272 kB -VmSize: 72272 kB -VmLck: 0 kB -VmHWM: 16204 kB -VmRSS: 16204 kB -VmData: 65100 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 80 kB -VmSwap: 0 kB -``` - -**29.668 kB virtual, TODO 15.084 resident per histogram.** - -10000 histograms with a uniform sample size of 1028 ---------------------------------------------------- - -``` -VmPeak: 256912 kB -VmSize: 256912 kB -VmLck: 0 kB -VmHWM: 146204 kB -VmRSS: 146204 kB -VmData: 249740 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 448 kB -VmSwap: 0 kB -``` - -**21.4308 kB virtual, 14.5084 kB resident per histogram.** - -50000 histograms with a uniform sample size of 1028 ---------------------------------------------------- - -``` -VmPeak: 908112 kB -VmSize: 908112 kB -VmLck: 0 kB -VmHWM: 645832 kB -VmRSS: 645588 kB -VmData: 900940 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 1716 kB -VmSwap: 1544 kB -``` - -**17.31016 kB virtual, 12.88936 kB resident per histogram.** - -1000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 -------------------------------------------------------------------------------------- - -``` -VmPeak: 62480 kB -VmSize: 62480 kB -VmLck: 0 kB -VmHWM: 11572 kB -VmRSS: 11572 kB -VmData: 55308 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 64 kB -VmSwap: 0 kB -``` - -**19.876 kB virtual, 10.452 kB resident per histogram.** - -10000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 --------------------------------------------------------------------------------------- - -``` -VmPeak: 153296 kB -VmSize: 153296 kB -VmLck: 0 kB -VmHWM: 101176 kB -VmRSS: 101176 kB -VmData: 146124 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 240 kB -VmSwap: 0 kB -``` - -**11.0692 kB virtual, 10.0056 kB resident per histogram.** - -50000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 --------------------------------------------------------------------------------------- - -``` -VmPeak: 557264 kB -VmSize: 557264 kB -VmLck: 0 kB -VmHWM: 501056 kB -VmRSS: 501056 kB -VmData: 550092 kB -VmStk: 136 kB -VmExe: 1048 kB -VmLib: 1848 kB -VmPTE: 1032 kB -VmSwap: 0 kB -``` - -**10.2932 kB virtual, 9.99872 kB resident per histogram.** - -1000 meters ------------ - -``` -VmPeak: 74504 kB -VmSize: 74504 kB -VmLck: 0 kB -VmHWM: 24124 kB -VmRSS: 24124 kB -VmData: 67340 kB -VmStk: 136 kB -VmExe: 1040 kB -VmLib: 1848 kB -VmPTE: 92 kB -VmSwap: 0 kB -``` - -**31.9 kB virtual, 23.004 kB resident per meter.** - -10000 meters ------------- - -``` -VmPeak: 278920 kB -VmSize: 278920 kB -VmLck: 0 kB -VmHWM: 227300 kB -VmRSS: 227300 kB -VmData: 271756 kB -VmStk: 136 kB -VmExe: 1040 kB -VmLib: 1848 kB -VmPTE: 488 kB -VmSwap: 0 kB -``` - -**23.6316 kB virtual, 22.618 kB resident per meter.** diff --git a/metrics/meter.go b/metrics/meter.go deleted file mode 100644 index 22475ef6eb..0000000000 --- a/metrics/meter.go +++ /dev/null @@ -1,189 +0,0 @@ -package metrics - -import ( - "math" - "sync" - "sync/atomic" - "time" -) - -type MeterSnapshot interface { - Count() int64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 -} - -// Meters count events to produce exponentially-weighted moving average rates -// at one-, five-, and fifteen-minutes and a mean rate. -type Meter interface { - Mark(int64) - Snapshot() MeterSnapshot - Stop() -} - -// GetOrRegisterMeter returns an existing Meter or constructs and registers a -// new StandardMeter. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterMeter(name string, r Registry) Meter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewMeter).(Meter) -} - -// NewMeter constructs a new StandardMeter and launches a goroutine. -// Be sure to call Stop() once the meter is of no use to allow for garbage collection. -func NewMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } - return m -} - -// NewInactiveMeter returns a meter but does not start any goroutines. This -// method is mainly intended for testing. -func NewInactiveMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - return m -} - -// NewRegisteredMeter constructs and registers a new StandardMeter -// and launches a goroutine. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredMeter(name string, r Registry) Meter { - return GetOrRegisterMeter(name, r) -} - -// meterSnapshot is a read-only copy of the meter's internal values. -type meterSnapshot struct { - count int64 - rate1, rate5, rate15, rateMean float64 -} - -// Count returns the count of events at the time the snapshot was taken. -func (m *meterSnapshot) Count() int64 { return m.count } - -// Rate1 returns the one-minute moving average rate of events per second at the -// time the snapshot was taken. -func (m *meterSnapshot) Rate1() float64 { return m.rate1 } - -// Rate5 returns the five-minute moving average rate of events per second at -// the time the snapshot was taken. -func (m *meterSnapshot) Rate5() float64 { return m.rate5 } - -// Rate15 returns the fifteen-minute moving average rate of events per second -// at the time the snapshot was taken. -func (m *meterSnapshot) Rate15() float64 { return m.rate15 } - -// RateMean returns the meter's mean rate of events per second at the time the -// snapshot was taken. -func (m *meterSnapshot) RateMean() float64 { return m.rateMean } - -// NilMeter is a no-op Meter. -type NilMeter struct{} - -func (NilMeter) Count() int64 { return 0 } -func (NilMeter) Mark(n int64) {} -func (NilMeter) Snapshot() MeterSnapshot { return (*emptySnapshot)(nil) } -func (NilMeter) Stop() {} - -// StandardMeter is the standard implementation of a Meter. -type StandardMeter struct { - count atomic.Int64 - uncounted atomic.Int64 // not yet added to the EWMAs - rateMean atomic.Uint64 - - a1, a5, a15 EWMA - startTime time.Time - stopped atomic.Bool -} - -func newStandardMeter() *StandardMeter { - return &StandardMeter{ - a1: NewEWMA1(), - a5: NewEWMA5(), - a15: NewEWMA15(), - startTime: time.Now(), - } -} - -// Stop stops the meter, Mark() will be a no-op if you use it after being stopped. -func (m *StandardMeter) Stop() { - if stopped := m.stopped.Swap(true); !stopped { - arbiter.Lock() - delete(arbiter.meters, m) - arbiter.Unlock() - } -} - -// Mark records the occurrence of n events. -func (m *StandardMeter) Mark(n int64) { - m.uncounted.Add(n) -} - -// Snapshot returns a read-only copy of the meter. -func (m *StandardMeter) Snapshot() MeterSnapshot { - return &meterSnapshot{ - count: m.count.Load() + m.uncounted.Load(), - rate1: m.a1.Snapshot().Rate(), - rate5: m.a5.Snapshot().Rate(), - rate15: m.a15.Snapshot().Rate(), - rateMean: math.Float64frombits(m.rateMean.Load()), - } -} - -func (m *StandardMeter) tick() { - // Take the uncounted values, add to count - n := m.uncounted.Swap(0) - count := m.count.Add(n) - m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds())) - // Update the EWMA's internal state - m.a1.Update(n) - m.a5.Update(n) - m.a15.Update(n) - // And trigger them to calculate the rates - m.a1.Tick() - m.a5.Tick() - m.a15.Tick() -} - -// meterArbiter ticks meters every 5s from a single goroutine. -// meters are references in a set for future stopping. -type meterArbiter struct { - sync.RWMutex - started bool - meters map[*StandardMeter]struct{} - ticker *time.Ticker -} - -var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} - -// Ticks meters on the scheduled interval -func (ma *meterArbiter) tick() { - for range ma.ticker.C { - ma.tickMeters() - } -} - -func (ma *meterArbiter) tickMeters() { - ma.RLock() - defer ma.RUnlock() - for meter := range ma.meters { - meter.tick() - } -} diff --git a/metrics/meter_test.go b/metrics/meter_test.go deleted file mode 100644 index 019c4d765b..0000000000 --- a/metrics/meter_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package metrics - -import ( - "testing" - "time" -) - -func BenchmarkMeter(b *testing.B) { - m := NewMeter() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.Mark(1) - } -} -func TestMeter(t *testing.T) { - m := NewMeter() - m.Mark(47) - if v := m.Snapshot().Count(); v != 47 { - t.Fatalf("have %d want %d", v, 47) - } -} -func TestGetOrRegisterMeter(t *testing.T) { - r := NewRegistry() - NewRegisteredMeter("foo", r).Mark(47) - if m := GetOrRegisterMeter("foo", r).Snapshot(); m.Count() != 47 { - t.Fatal(m.Count()) - } -} - -func TestMeterDecay(t *testing.T) { - ma := meterArbiter{ - ticker: time.NewTicker(time.Millisecond), - meters: make(map[*StandardMeter]struct{}), - } - defer ma.ticker.Stop() - m := newStandardMeter() - ma.meters[m] = struct{}{} - m.Mark(1) - ma.tickMeters() - rateMean := m.Snapshot().RateMean() - time.Sleep(100 * time.Millisecond) - ma.tickMeters() - if m.Snapshot().RateMean() >= rateMean { - t.Error("m.RateMean() didn't decrease") - } -} - -func TestMeterNonzero(t *testing.T) { - m := NewMeter() - m.Mark(3) - if count := m.Snapshot().Count(); count != 3 { - t.Errorf("m.Count(): 3 != %v\n", count) - } -} - -func TestMeterStop(t *testing.T) { - l := len(arbiter.meters) - m := NewMeter() - if l+1 != len(arbiter.meters) { - t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters)) - } - m.Stop() - if l != len(arbiter.meters) { - t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters)) - } -} - -func TestMeterZero(t *testing.T) { - m := NewMeter().Snapshot() - if count := m.Count(); count != 0 { - t.Errorf("m.Count(): 0 != %v\n", count) - } -} - -func TestMeterRepeat(t *testing.T) { - m := NewMeter() - for i := 0; i < 101; i++ { - m.Mark(int64(i)) - } - if count := m.Snapshot().Count(); count != 5050 { - t.Errorf("m.Count(): 5050 != %v\n", count) - } - for i := 0; i < 101; i++ { - m.Mark(int64(i)) - } - if count := m.Snapshot().Count(); count != 10100 { - t.Errorf("m.Count(): 10100 != %v\n", count) - } -} diff --git a/metrics/metrics.go b/metrics/metrics.go deleted file mode 100644 index e01beef68e..0000000000 --- a/metrics/metrics.go +++ /dev/null @@ -1,18 +0,0 @@ -// Go port of Coda Hale's Metrics library -// -// -// -// Coda Hale's original work: -package metrics - -// Enabled is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. -// -// This global kill-switch helps quantify the observer effect and makes -// for less cluttered pprof profiles. -var Enabled = true - -// EnabledExpensive is a soft-flag meant for external packages to check if costly -// metrics gathering is allowed or not. The goal is to separate standard metrics -// for health monitoring and debug metrics that might impact runtime performance. -var EnabledExpensive = false diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go deleted file mode 100644 index 775b247185..0000000000 --- a/metrics/metrics_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package metrics - -import ( - "fmt" - "sync" - "testing" - "time" -) - -const FANOUT = 128 - -func BenchmarkMetrics(b *testing.B) { - r := NewRegistry() - c := NewRegisteredCounter("counter", r) - cf := NewRegisteredCounterFloat64("counterfloat64", r) - g := NewRegisteredGauge("gauge", r) - gf := NewRegisteredGaugeFloat64("gaugefloat64", r) - h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) - m := NewRegisteredMeter("meter", r) - t := NewRegisteredTimer("timer", r) - RegisterDebugGCStats(r) - b.ResetTimer() - ch := make(chan bool) - - wgD := &sync.WaitGroup{} - /* - wgD.Add(1) - go func() { - defer wgD.Done() - //log.Println("go CaptureDebugGCStats") - for { - select { - case <-ch: - //log.Println("done CaptureDebugGCStats") - return - default: - CaptureDebugGCStatsOnce(r) - } - } - }() - //*/ - - wgW := &sync.WaitGroup{} - /* - wgW.Add(1) - go func() { - defer wgW.Done() - //log.Println("go Write") - for { - select { - case <-ch: - //log.Println("done Write") - return - default: - WriteOnce(r, io.Discard) - } - } - }() - //*/ - - wg := &sync.WaitGroup{} - wg.Add(FANOUT) - for i := 0; i < FANOUT; i++ { - go func(i int) { - defer wg.Done() - //log.Println("go", i) - for i := 0; i < b.N; i++ { - c.Inc(1) - cf.Inc(1.0) - g.Update(int64(i)) - gf.Update(float64(i)) - h.Update(int64(i)) - m.Mark(1) - t.Update(1) - } - //log.Println("done", i) - }(i) - } - wg.Wait() - close(ch) - wgD.Wait() - wgW.Wait() -} - -func Example() { - c := NewCounter() - Register("money", c) - c.Inc(17) - - // Threadsafe registration - t := GetOrRegisterTimer("db.get.latency", nil) - t.Time(func() { time.Sleep(10 * time.Millisecond) }) - t.Update(1) - - fmt.Println(c.Snapshot().Count()) - fmt.Println(t.Snapshot().Min()) - // Output: 17 - // 1 -} diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go deleted file mode 100644 index e81690f943..0000000000 --- a/metrics/opentsdb.go +++ /dev/null @@ -1,128 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "io" - "log" - "net" - "os" - "strings" - "time" -) - -var shortHostName = "" - -// OpenTSDBConfig provides a container with configuration parameters for -// the OpenTSDB exporter -type OpenTSDBConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names -} - -// OpenTSDB is a blocking exporter function which reports metrics in r -// to a TSDB server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - OpenTSDBWithConfig(OpenTSDBConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - }) -} - -// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB, -// but it takes a OpenTSDBConfig instead. -func OpenTSDBWithConfig(c OpenTSDBConfig) { - for range time.Tick(c.FlushInterval) { - if err := openTSDB(&c); nil != err { - log.Println(err) - } - } -} - -func getShortHostname() string { - if shortHostName == "" { - host, _ := os.Hostname() - if index := strings.Index(host, "."); index > 0 { - shortHostName = host[:index] - } else { - shortHostName = host - } - } - return shortHostName -} - -// writeRegistry writes the registry-metrics on the opentsb format. -func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname string) { - du := float64(c.DurationUnit) - - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case CounterFloat64: - fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case Gauge: - fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeFloat64: - fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeInfo: - fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Snapshot().Value().String(), shortHostname) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname) - fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname) - fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname) - fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname) - fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname) - fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) - fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) - fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) - fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) - fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname) - fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname) - fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname) - fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname) - } - }) -} - -func openTSDB(c *OpenTSDBConfig) error { - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.writeRegistry(w, time.Now().Unix(), getShortHostname()) - w.Flush() - return nil -} diff --git a/metrics/opentsdb_test.go b/metrics/opentsdb_test.go deleted file mode 100644 index d13973a588..0000000000 --- a/metrics/opentsdb_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package metrics - -import ( - "fmt" - "net" - "os" - "strings" - "testing" - "time" -) - -func ExampleOpenTSDB() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go OpenTSDB(DefaultRegistry, 1*time.Second, "some.prefix", addr) -} - -func ExampleOpenTSDBWithConfig() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go OpenTSDBWithConfig(OpenTSDBConfig{ - Addr: addr, - Registry: DefaultRegistry, - FlushInterval: 1 * time.Second, - DurationUnit: time.Millisecond, - }) -} - -func TestExampleOpenTSB(t *testing.T) { - r := NewOrderedRegistry() - NewRegisteredGaugeInfo("foo", r).Update(GaugeInfoValue{"chain_id": "5"}) - NewRegisteredGaugeFloat64("pi", r).Update(3.14) - NewRegisteredCounter("months", r).Inc(12) - NewRegisteredCounterFloat64("tau", r).Inc(1.57) - NewRegisteredMeter("elite", r).Mark(1337) - NewRegisteredTimer("second", r).Update(time.Second) - NewRegisteredCounterFloat64("tau", r).Inc(1.57) - NewRegisteredCounterFloat64("tau", r).Inc(1.57) - - w := new(strings.Builder) - (&OpenTSDBConfig{ - Registry: r, - DurationUnit: time.Millisecond, - Prefix: "pre", - }).writeRegistry(w, 978307200, "hal9000") - - wantB, err := os.ReadFile("./testdata/opentsb.want") - if err != nil { - t.Fatal(err) - } - want := strings.ReplaceAll(string(wantB), "\r\n", "\n") - if have := w.String(); have != want { - t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) - t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) - } -} - -func findFirstDiffPos(a, b string) string { - yy := strings.Split(b, "\n") - for i, x := range strings.Split(a, "\n") { - if i >= len(yy) { - return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i) - } - if y := yy[i]; x != y { - return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y) - } - } - return "" -} diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go deleted file mode 100644 index c07a38ca5f..0000000000 --- a/metrics/prometheus/prometheus.go +++ /dev/null @@ -1,196 +0,0 @@ -// (c) 2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package prometheus - -import ( - "sort" - "strings" - - "github.com/ava-labs/subnet-evm/metrics" - - "github.com/prometheus/client_golang/prometheus" - - dto "github.com/prometheus/client_model/go" -) - -var ( - pv = []float64{.5, .75, .95, .99, .999, .9999} - pvShortPercent = []float64{50, 95, 99} - pvShort = []float64{.50, .95, .99} -) - -type gatherer struct { - reg metrics.Registry -} - -func (g gatherer) Gather() ([]*dto.MetricFamily, error) { - // Gather and pre-sort the metrics to avoid random listings - var names []string - g.reg.Each(func(name string, i interface{}) { - names = append(names, name) - }) - sort.Strings(names) - - mfs := make([]*dto.MetricFamily, 0, len(names)) - for _, name := range names { - mIntf := g.reg.Get(name) - name := strings.Replace(name, "/", "_", -1) - - switch m := mIntf.(type) { - case metrics.Counter: - val := m.Snapshot().Count() - valFloat := float64(val) - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{{ - Counter: &dto.Counter{ - Value: &valFloat, - }, - }}, - }) - case metrics.CounterFloat64: - val := m.Snapshot().Count() - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{{ - Counter: &dto.Counter{ - Value: &val, - }, - }}, - }) - case metrics.Gauge: - val := m.Snapshot().Value() - valFloat := float64(val) - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_GAUGE.Enum(), - Metric: []*dto.Metric{{ - Gauge: &dto.Gauge{ - Value: &valFloat, - }, - }}, - }) - case metrics.GaugeFloat64: - val := m.Snapshot().Value() - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_GAUGE.Enum(), - Metric: []*dto.Metric{{ - Gauge: &dto.Gauge{ - Value: &val, - }, - }}, - }) - case metrics.Histogram: - snapshot := m.Snapshot() - count := snapshot.Count() - countUint := uint64(count) - sum := snapshot.Sum() - sumFloat := float64(sum) - - ps := snapshot.Percentiles(pv) - qs := make([]*dto.Quantile, len(pv)) - for i := range ps { - v := pv[i] - s := ps[i] - qs[i] = &dto.Quantile{ - Quantile: &v, - Value: &s, - } - } - - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{{ - Summary: &dto.Summary{ - SampleCount: &countUint, - SampleSum: &sumFloat, - Quantile: qs, - }, - }}, - }) - case metrics.Meter: - val := m.Snapshot().Count() - valFloat := float64(val) - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_GAUGE.Enum(), - Metric: []*dto.Metric{{ - Gauge: &dto.Gauge{ - Value: &valFloat, - }, - }}, - }) - case metrics.Timer: - snapshot := m.Snapshot() - count := snapshot.Count() - countUint := uint64(count) - sum := snapshot.Sum() - sumFloat := float64(sum) - - ps := snapshot.Percentiles(pv) - qs := make([]*dto.Quantile, len(pv)) - for i := range ps { - v := pv[i] - s := ps[i] - qs[i] = &dto.Quantile{ - Quantile: &v, - Value: &s, - } - } - - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{{ - Summary: &dto.Summary{ - SampleCount: &countUint, - SampleSum: &sumFloat, - Quantile: qs, - }, - }}, - }) - case metrics.ResettingTimer: - snapshot := m.Snapshot() - - count := uint64(snapshot.Count()) - if count == 0 { - continue - } - - ps := snapshot.Percentiles(pvShortPercent) - qs := make([]*dto.Quantile, len(pv)) - for i := range pvShort { - v := pv[i] - s := ps[i] - qs[i] = &dto.Quantile{ - Quantile: &v, - Value: &s, - } - } - - mfs = append(mfs, &dto.MetricFamily{ - Name: &name, - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{{ - Summary: &dto.Summary{ - SampleCount: &count, - // TODO: do we need to specify SampleSum here? and if so - // what should that be? - Quantile: qs, - }, - }}, - }) - } - } - - return mfs, nil -} - -func Gatherer(reg metrics.Registry) prometheus.Gatherer { - return gatherer{reg: reg} -} diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go deleted file mode 100644 index 58d0b550ec..0000000000 --- a/metrics/prometheus/prometheus_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// (c) 2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package prometheus - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/ava-labs/subnet-evm/metrics" -) - -func TestGatherer(t *testing.T) { - registry := metrics.NewRegistry() - - counter := metrics.NewCounter() - counter.Inc(12345) - - err := registry.Register("test/counter", counter) - assert.NoError(t, err) - - gauge := metrics.NewGauge() - gauge.Update(23456) - - err = registry.Register("test/gauge", gauge) - assert.NoError(t, err) - - gaugeFloat64 := metrics.NewGaugeFloat64() - gaugeFloat64.Update(34567.89) - - err = registry.Register("test/gauge_float64", gaugeFloat64) - assert.NoError(t, err) - - sample := metrics.NewUniformSample(1028) - histogram := metrics.NewHistogram(sample) - - err = registry.Register("test/histogram", histogram) - assert.NoError(t, err) - - meter := metrics.NewMeter() - defer meter.Stop() - meter.Mark(9999999) - - err = registry.Register("test/meter", meter) - assert.NoError(t, err) - - timer := metrics.NewTimer() - defer timer.Stop() - timer.Update(20 * time.Millisecond) - timer.Update(21 * time.Millisecond) - timer.Update(22 * time.Millisecond) - timer.Update(120 * time.Millisecond) - timer.Update(23 * time.Millisecond) - timer.Update(24 * time.Millisecond) - - err = registry.Register("test/timer", timer) - assert.NoError(t, err) - - resettingTimer := metrics.NewResettingTimer() - resettingTimer.Update(10 * time.Millisecond) - resettingTimer.Update(11 * time.Millisecond) - resettingTimer.Update(12 * time.Millisecond) - resettingTimer.Update(120 * time.Millisecond) - resettingTimer.Update(13 * time.Millisecond) - resettingTimer.Update(14 * time.Millisecond) - - err = registry.Register("test/resetting_timer", resettingTimer) - assert.NoError(t, err) - - err = registry.Register("test/resetting_timer_snapshot", resettingTimer.Snapshot()) - assert.NoError(t, err) - - emptyResettingTimer := metrics.NewResettingTimer() - - err = registry.Register("test/empty_resetting_timer", emptyResettingTimer) - assert.NoError(t, err) - - err = registry.Register("test/empty_resetting_timer_snapshot", emptyResettingTimer.Snapshot()) - assert.NoError(t, err) - - g := Gatherer(registry) - - _, err = g.Gather() - assert.NoError(t, err) -} diff --git a/metrics/prometheus/testdata/prometheus.want b/metrics/prometheus/testdata/prometheus.want deleted file mode 100644 index 861c5f5cf0..0000000000 --- a/metrics/prometheus/testdata/prometheus.want +++ /dev/null @@ -1,70 +0,0 @@ -# TYPE system_cpu_schedlatency_count counter -system_cpu_schedlatency_count 5645 - -# TYPE system_cpu_schedlatency summary -system_cpu_schedlatency {quantile="0.5"} 0 -system_cpu_schedlatency {quantile="0.75"} 7168 -system_cpu_schedlatency {quantile="0.95"} 1.6777216e+07 -system_cpu_schedlatency {quantile="0.99"} 2.9360128e+07 -system_cpu_schedlatency {quantile="0.999"} 3.3554432e+07 -system_cpu_schedlatency {quantile="0.9999"} 3.3554432e+07 - -# TYPE system_memory_pauses_count counter -system_memory_pauses_count 14 - -# TYPE system_memory_pauses summary -system_memory_pauses {quantile="0.5"} 32768 -system_memory_pauses {quantile="0.75"} 57344 -system_memory_pauses {quantile="0.95"} 196608 -system_memory_pauses {quantile="0.99"} 196608 -system_memory_pauses {quantile="0.999"} 196608 -system_memory_pauses {quantile="0.9999"} 196608 - -# TYPE test_counter gauge -test_counter 12345 - -# TYPE test_counter_float64 gauge -test_counter_float64 54321.98 - -# TYPE test_gauge gauge -test_gauge 23456 - -# TYPE test_gauge_float64 gauge -test_gauge_float64 34567.89 - -# TYPE test_gauge_info gauge -test_gauge_info {arch="amd64", commit="7caa2d8163ae3132c1c2d6978c76610caee2d949", os="linux", protocol_versions="64 65 66", version="1.10.18-unstable"} 1 - -# TYPE test_histogram_count counter -test_histogram_count 3 - -# TYPE test_histogram summary -test_histogram {quantile="0.5"} 2 -test_histogram {quantile="0.75"} 3 -test_histogram {quantile="0.95"} 3 -test_histogram {quantile="0.99"} 3 -test_histogram {quantile="0.999"} 3 -test_histogram {quantile="0.9999"} 3 - -# TYPE test_meter gauge -test_meter 0 - -# TYPE test_resetting_timer_count counter -test_resetting_timer_count 6 - -# TYPE test_resetting_timer summary -test_resetting_timer {quantile="0.50"} 1.25e+07 -test_resetting_timer {quantile="0.95"} 1.2e+08 -test_resetting_timer {quantile="0.99"} 1.2e+08 - -# TYPE test_timer_count counter -test_timer_count 6 - -# TYPE test_timer summary -test_timer {quantile="0.5"} 2.25e+07 -test_timer {quantile="0.75"} 4.8e+07 -test_timer {quantile="0.95"} 1.2e+08 -test_timer {quantile="0.99"} 1.2e+08 -test_timer {quantile="0.999"} 1.2e+08 -test_timer {quantile="0.9999"} 1.2e+08 - diff --git a/metrics/registry.go b/metrics/registry.go deleted file mode 100644 index 8bfbc08042..0000000000 --- a/metrics/registry.go +++ /dev/null @@ -1,372 +0,0 @@ -package metrics - -import ( - "fmt" - "reflect" - "sort" - "strings" - "sync" -) - -// DuplicateMetric is the error returned by Registry.Register when a metric -// already exists. If you mean to Register that metric you must first -// Unregister the existing metric. -type DuplicateMetric string - -func (err DuplicateMetric) Error() string { - return fmt.Sprintf("duplicate metric: %s", string(err)) -} - -// A Registry holds references to a set of metrics by name and can iterate -// over them, calling callback functions provided by the user. -// -// This is an interface so as to encourage other structs to implement -// the Registry API as appropriate. -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) -} - -type orderedRegistry struct { - StandardRegistry -} - -// Call the given function for each registered metric. -func (r *orderedRegistry) Each(f func(string, interface{})) { - var names []string - reg := r.registered() - for name := range reg { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - f(name, reg[name]) - } -} - -// NewRegistry creates a new registry. -func NewRegistry() Registry { - return new(StandardRegistry) -} - -// NewOrderedRegistry creates a new ordered registry (for testing). -func NewOrderedRegistry() Registry { - return new(orderedRegistry) -} - -// The standard implementation of a Registry uses sync.map -// of names to metrics. -type StandardRegistry struct { - metrics sync.Map -} - -// Call the given function for each registered metric. -func (r *StandardRegistry) Each(f func(string, interface{})) { - for name, i := range r.registered() { - f(name, i) - } -} - -// Get the metric by the given name or nil if none is registered. -func (r *StandardRegistry) Get(name string) interface{} { - item, _ := r.metrics.Load(name) - return item -} - -// Gets an existing metric or creates and registers a new one. Threadsafe -// alternative to calling Get and Register on failure. -// The interface can be the metric to register if not found in registry, -// or a function returning the metric for lazy instantiation. -func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { - // fast path - cached, ok := r.metrics.Load(name) - if ok { - return cached - } - if v := reflect.ValueOf(i); v.Kind() == reflect.Func { - i = v.Call(nil)[0].Interface() - } - item, _, ok := r.loadOrRegister(name, i) - if !ok { - return i - } - return item -} - -// Register the given metric under the given name. Returns a DuplicateMetric -// if a metric by the given name is already registered. -func (r *StandardRegistry) Register(name string, i interface{}) error { - // fast path - _, ok := r.metrics.Load(name) - if ok { - return DuplicateMetric(name) - } - - if v := reflect.ValueOf(i); v.Kind() == reflect.Func { - i = v.Call(nil)[0].Interface() - } - _, loaded, _ := r.loadOrRegister(name, i) - if loaded { - return DuplicateMetric(name) - } - return nil -} - -// Run all registered healthchecks. -func (r *StandardRegistry) RunHealthchecks() { - r.metrics.Range(func(key, value any) bool { - if h, ok := value.(Healthcheck); ok { - h.Check() - } - return true - }) -} - -// GetAll metrics in the Registry -func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { - data := make(map[string]map[string]interface{}) - r.Each(func(name string, i interface{}) { - values := make(map[string]interface{}) - switch metric := i.(type) { - case Counter: - values["count"] = metric.Snapshot().Count() - case CounterFloat64: - values["count"] = metric.Snapshot().Count() - case Gauge: - values["value"] = metric.Snapshot().Value() - case GaugeFloat64: - values["value"] = metric.Snapshot().Value() - case Healthcheck: - values["error"] = nil - metric.Check() - if err := metric.Error(); nil != err { - values["error"] = metric.Error().Error() - } - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - values["count"] = h.Count() - values["min"] = h.Min() - values["max"] = h.Max() - values["mean"] = h.Mean() - values["stddev"] = h.StdDev() - values["median"] = ps[0] - values["75%"] = ps[1] - values["95%"] = ps[2] - values["99%"] = ps[3] - values["99.9%"] = ps[4] - case Meter: - m := metric.Snapshot() - values["count"] = m.Count() - values["1m.rate"] = m.Rate1() - values["5m.rate"] = m.Rate5() - values["15m.rate"] = m.Rate15() - values["mean.rate"] = m.RateMean() - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - values["count"] = t.Count() - values["min"] = t.Min() - values["max"] = t.Max() - values["mean"] = t.Mean() - values["stddev"] = t.StdDev() - values["median"] = ps[0] - values["75%"] = ps[1] - values["95%"] = ps[2] - values["99%"] = ps[3] - values["99.9%"] = ps[4] - values["1m.rate"] = t.Rate1() - values["5m.rate"] = t.Rate5() - values["15m.rate"] = t.Rate15() - values["mean.rate"] = t.RateMean() - } - data[name] = values - }) - return data -} - -// Unregister the metric with the given name. -func (r *StandardRegistry) Unregister(name string) { - r.stop(name) - r.metrics.LoadAndDelete(name) -} - -func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { - switch i.(type) { - case Counter, CounterFloat64, Gauge, GaugeFloat64, GaugeInfo, Healthcheck, Histogram, Meter, Timer, ResettingTimer: - default: - return nil, false, false - } - item, loaded := r.metrics.LoadOrStore(name, i) - return item, loaded, true -} - -func (r *StandardRegistry) registered() map[string]interface{} { - metrics := make(map[string]interface{}) - r.metrics.Range(func(key, value any) bool { - metrics[key.(string)] = value - return true - }) - return metrics -} - -func (r *StandardRegistry) stop(name string) { - if i, ok := r.metrics.Load(name); ok { - if s, ok := i.(Stoppable); ok { - s.Stop() - } - } -} - -// Stoppable defines the metrics which has to be stopped. -type Stoppable interface { - Stop() -} - -type PrefixedRegistry struct { - underlying Registry - prefix string -} - -func NewPrefixedRegistry(prefix string) Registry { - return &PrefixedRegistry{ - underlying: NewRegistry(), - prefix: prefix, - } -} - -func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { - return &PrefixedRegistry{ - underlying: parent, - prefix: prefix, - } -} - -// Call the given function for each registered metric. -func (r *PrefixedRegistry) Each(fn func(string, interface{})) { - wrappedFn := func(prefix string) func(string, interface{}) { - return func(name string, iface interface{}) { - if strings.HasPrefix(name, prefix) { - fn(name, iface) - } else { - return - } - } - } - - baseRegistry, prefix := findPrefix(r, "") - baseRegistry.Each(wrappedFn(prefix)) -} - -func findPrefix(registry Registry, prefix string) (Registry, string) { - switch r := registry.(type) { - case *PrefixedRegistry: - return findPrefix(r.underlying, r.prefix+prefix) - case *StandardRegistry: - return r, prefix - } - return nil, "" -} - -// Get the metric by the given name or nil if none is registered. -func (r *PrefixedRegistry) Get(name string) interface{} { - realName := r.prefix + name - return r.underlying.Get(realName) -} - -// 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. -func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} { - realName := r.prefix + name - return r.underlying.GetOrRegister(realName, metric) -} - -// Register the given metric under the given name. The name will be prefixed. -func (r *PrefixedRegistry) Register(name string, metric interface{}) error { - realName := r.prefix + name - return r.underlying.Register(realName, metric) -} - -// Run all registered healthchecks. -func (r *PrefixedRegistry) RunHealthchecks() { - r.underlying.RunHealthchecks() -} - -// GetAll metrics in the Registry -func (r *PrefixedRegistry) GetAll() map[string]map[string]interface{} { - return r.underlying.GetAll() -} - -// Unregister the metric with the given name. The name will be prefixed. -func (r *PrefixedRegistry) Unregister(name string) { - realName := r.prefix + name - r.underlying.Unregister(realName) -} - -var ( - DefaultRegistry = NewRegistry() - EphemeralRegistry = NewRegistry() - AccountingRegistry = NewRegistry() // registry used in swarm -) - -// Call the given function for each registered metric. -func Each(f func(string, interface{})) { - DefaultRegistry.Each(f) -} - -// Get the metric by the given name or nil if none is registered. -func Get(name string) interface{} { - return DefaultRegistry.Get(name) -} - -// Gets an existing metric or creates and registers a new one. Threadsafe -// alternative to calling Get and Register on failure. -func GetOrRegister(name string, i interface{}) interface{} { - return DefaultRegistry.GetOrRegister(name, i) -} - -// Register the given metric under the given name. Returns a DuplicateMetric -// if a metric by the given name is already registered. -func Register(name string, i interface{}) error { - return DefaultRegistry.Register(name, i) -} - -// Register the given metric under the given name. Panics if a metric by the -// given name is already registered. -func MustRegister(name string, i interface{}) { - if err := Register(name, i); err != nil { - panic(err) - } -} - -// Run all registered healthchecks. -func RunHealthchecks() { - DefaultRegistry.RunHealthchecks() -} - -// Unregister the metric with the given name. -func Unregister(name string) { - DefaultRegistry.Unregister(name) -} diff --git a/metrics/registry_test.go b/metrics/registry_test.go deleted file mode 100644 index 75012dd4ac..0000000000 --- a/metrics/registry_test.go +++ /dev/null @@ -1,335 +0,0 @@ -package metrics - -import ( - "sync" - "testing" -) - -func BenchmarkRegistry(b *testing.B) { - r := NewRegistry() - r.Register("foo", NewCounter()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - r.Each(func(string, interface{}) {}) - } -} - -func BenchmarkRegistryGetOrRegisterParallel_8(b *testing.B) { - benchmarkRegistryGetOrRegisterParallel(b, 8) -} - -func BenchmarkRegistryGetOrRegisterParallel_32(b *testing.B) { - benchmarkRegistryGetOrRegisterParallel(b, 32) -} - -func benchmarkRegistryGetOrRegisterParallel(b *testing.B, amount int) { - r := NewRegistry() - b.ResetTimer() - var wg sync.WaitGroup - for i := 0; i < amount; i++ { - wg.Add(1) - go func() { - for i := 0; i < b.N; i++ { - r.GetOrRegister("foo", NewMeter) - } - wg.Done() - }() - } - wg.Wait() -} - -func TestRegistry(t *testing.T) { - r := NewRegistry() - r.Register("foo", NewCounter()) - i := 0 - r.Each(func(name string, iface interface{}) { - i++ - if name != "foo" { - t.Fatal(name) - } - if _, ok := iface.(Counter); !ok { - t.Fatal(iface) - } - }) - if i != 1 { - t.Fatal(i) - } - r.Unregister("foo") - i = 0 - r.Each(func(string, interface{}) { i++ }) - if i != 0 { - t.Fatal(i) - } -} - -func TestRegistryDuplicate(t *testing.T) { - r := NewRegistry() - if err := r.Register("foo", NewCounter()); nil != err { - t.Fatal(err) - } - if err := r.Register("foo", NewGauge()); nil == err { - t.Fatal(err) - } - i := 0 - r.Each(func(name string, iface interface{}) { - i++ - if _, ok := iface.(Counter); !ok { - t.Fatal(iface) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestRegistryGet(t *testing.T) { - r := NewRegistry() - r.Register("foo", NewCounter()) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 0 { - t.Fatal(count) - } - r.Get("foo").(Counter).Inc(1) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 1 { - t.Fatal(count) - } -} - -func TestRegistryGetOrRegister(t *testing.T) { - r := NewRegistry() - - // First metric wins with GetOrRegister - _ = r.GetOrRegister("foo", NewCounter()) - m := r.GetOrRegister("foo", NewGauge()) - if _, ok := m.(Counter); !ok { - t.Fatal(m) - } - - i := 0 - r.Each(func(name string, iface interface{}) { - i++ - if name != "foo" { - t.Fatal(name) - } - if _, ok := iface.(Counter); !ok { - t.Fatal(iface) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) { - r := NewRegistry() - - // First metric wins with GetOrRegister - _ = r.GetOrRegister("foo", NewCounter) - m := r.GetOrRegister("foo", NewGauge) - if _, ok := m.(Counter); !ok { - t.Fatal(m) - } - - i := 0 - r.Each(func(name string, iface interface{}) { - i++ - if name != "foo" { - t.Fatal(name) - } - if _, ok := iface.(Counter); !ok { - t.Fatal(iface) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestRegistryUnregister(t *testing.T) { - l := len(arbiter.meters) - r := NewRegistry() - r.Register("foo", NewCounter()) - r.Register("bar", NewMeter()) - r.Register("baz", NewTimer()) - if len(arbiter.meters) != l+2 { - t.Errorf("arbiter.meters: %d != %d\n", l+2, len(arbiter.meters)) - } - r.Unregister("foo") - r.Unregister("bar") - r.Unregister("baz") - if len(arbiter.meters) != l { - t.Errorf("arbiter.meters: %d != %d\n", l+2, len(arbiter.meters)) - } -} - -func TestPrefixedChildRegistryGetOrRegister(t *testing.T) { - r := NewRegistry() - pr := NewPrefixedChildRegistry(r, "prefix.") - - _ = pr.GetOrRegister("foo", NewCounter()) - - i := 0 - r.Each(func(name string, m interface{}) { - i++ - if name != "prefix.foo" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestPrefixedRegistryGetOrRegister(t *testing.T) { - r := NewPrefixedRegistry("prefix.") - - _ = r.GetOrRegister("foo", NewCounter()) - - i := 0 - r.Each(func(name string, m interface{}) { - i++ - if name != "prefix.foo" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestPrefixedRegistryRegister(t *testing.T) { - r := NewPrefixedRegistry("prefix.") - err := r.Register("foo", NewCounter()) - c := NewCounter() - Register("bar", c) - if err != nil { - t.Fatal(err.Error()) - } - - i := 0 - r.Each(func(name string, m interface{}) { - i++ - if name != "prefix.foo" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestPrefixedRegistryUnregister(t *testing.T) { - r := NewPrefixedRegistry("prefix.") - - _ = r.Register("foo", NewCounter()) - - i := 0 - r.Each(func(name string, m interface{}) { - i++ - if name != "prefix.foo" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } - - r.Unregister("foo") - - i = 0 - r.Each(func(name string, m interface{}) { - i++ - }) - - if i != 0 { - t.Fatal(i) - } -} - -func TestPrefixedRegistryGet(t *testing.T) { - pr := NewPrefixedRegistry("prefix.") - name := "foo" - pr.Register(name, NewCounter()) - - fooCounter := pr.Get(name) - if fooCounter == nil { - t.Fatal(name) - } -} - -func TestPrefixedChildRegistryGet(t *testing.T) { - r := NewRegistry() - pr := NewPrefixedChildRegistry(r, "prefix.") - name := "foo" - pr.Register(name, NewCounter()) - fooCounter := pr.Get(name) - if fooCounter == nil { - t.Fatal(name) - } -} - -func TestChildPrefixedRegistryRegister(t *testing.T) { - r := NewPrefixedChildRegistry(DefaultRegistry, "prefix.") - err := r.Register("foo", NewCounter()) - c := NewCounter() - Register("bar", c) - if err != nil { - t.Fatal(err.Error()) - } - - i := 0 - r.Each(func(name string, m interface{}) { - i++ - if name != "prefix.foo" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestChildPrefixedRegistryOfChildRegister(t *testing.T) { - r := NewPrefixedChildRegistry(NewRegistry(), "prefix.") - r2 := NewPrefixedChildRegistry(r, "prefix2.") - err := r.Register("foo2", NewCounter()) - if err != nil { - t.Fatal(err.Error()) - } - err = r2.Register("baz", NewCounter()) - if err != nil { - t.Fatal(err.Error()) - } - c := NewCounter() - Register("bars", c) - - i := 0 - r2.Each(func(name string, m interface{}) { - i++ - if name != "prefix.prefix2.baz" { - t.Fatal(name) - } - }) - if i != 1 { - t.Fatal(i) - } -} - -func TestWalkRegistries(t *testing.T) { - r := NewPrefixedChildRegistry(NewRegistry(), "prefix.") - r2 := NewPrefixedChildRegistry(r, "prefix2.") - err := r.Register("foo2", NewCounter()) - if err != nil { - t.Fatal(err.Error()) - } - err = r2.Register("baz", NewCounter()) - if err != nil { - t.Fatal(err.Error()) - } - c := NewCounter() - Register("bars", c) - - _, prefix := findPrefix(r2, "") - if prefix != "prefix.prefix2." { - t.Fatal(prefix) - } -} diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go deleted file mode 100644 index c38ffcd3ec..0000000000 --- a/metrics/resetting_sample.go +++ /dev/null @@ -1,24 +0,0 @@ -package metrics - -// ResettingSample converts an ordinary sample into one that resets whenever its -// snapshot is retrieved. This will break for multi-monitor systems, but when only -// a single metric is being pushed out, this ensure that low-frequency events don't -// skew th charts indefinitely. -func ResettingSample(sample Sample) Sample { - return &resettingSample{ - Sample: sample, - } -} - -// resettingSample is a simple wrapper around a sample that resets it upon the -// snapshot retrieval. -type resettingSample struct { - Sample -} - -// Snapshot returns a read-only copy of the sample with the original reset. -func (rs *resettingSample) Snapshot() SampleSnapshot { - s := rs.Sample.Snapshot() - rs.Sample.Clear() - return s -} diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go deleted file mode 100644 index 6802e3fcea..0000000000 --- a/metrics/resetting_timer.go +++ /dev/null @@ -1,171 +0,0 @@ -package metrics - -import ( - "sync" - "time" -) - -// Initial slice capacity for the values stored in a ResettingTimer -const InitialResettingTimerSliceCap = 10 - -type ResettingTimerSnapshot interface { - Count() int - Mean() float64 - Max() int64 - Min() int64 - Percentiles([]float64) []float64 -} - -// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. -type ResettingTimer interface { - Snapshot() ResettingTimerSnapshot - Time(func()) - Update(time.Duration) - UpdateSince(time.Time) -} - -// GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a -// new StandardResettingTimer. -func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) -} - -// NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. -func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { - c := NewResettingTimer() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewResettingTimer constructs a new StandardResettingTimer -func NewResettingTimer() ResettingTimer { - if !Enabled { - return NilResettingTimer{} - } - return &StandardResettingTimer{ - values: make([]int64, 0, InitialResettingTimerSliceCap), - } -} - -// NilResettingTimer is a no-op ResettingTimer. -type NilResettingTimer struct{} - -func (NilResettingTimer) Values() []int64 { return nil } -func (n NilResettingTimer) Snapshot() ResettingTimerSnapshot { return n } -func (NilResettingTimer) Time(f func()) { f() } -func (NilResettingTimer) Update(time.Duration) {} -func (NilResettingTimer) Percentiles([]float64) []float64 { return nil } -func (NilResettingTimer) Mean() float64 { return 0.0 } -func (NilResettingTimer) Max() int64 { return 0 } -func (NilResettingTimer) Min() int64 { return 0 } -func (NilResettingTimer) UpdateSince(time.Time) {} -func (NilResettingTimer) Count() int { return 0 } - -// StandardResettingTimer is the standard implementation of a ResettingTimer. -// and Meter. -type StandardResettingTimer struct { - values []int64 - sum int64 // sum is a running count of the total sum, used later to calculate mean - - mutex sync.Mutex -} - -// Snapshot resets the timer and returns a read-only copy of its contents. -func (t *StandardResettingTimer) Snapshot() ResettingTimerSnapshot { - t.mutex.Lock() - defer t.mutex.Unlock() - snapshot := &resettingTimerSnapshot{} - if len(t.values) > 0 { - snapshot.mean = float64(t.sum) / float64(len(t.values)) - snapshot.values = t.values - t.values = make([]int64, 0, InitialResettingTimerSliceCap) - } - t.sum = 0 - return snapshot -} - -// Record the duration of the execution of the given function. -func (t *StandardResettingTimer) Time(f func()) { - ts := time.Now() - f() - t.Update(time.Since(ts)) -} - -// Record the duration of an event. -func (t *StandardResettingTimer) Update(d time.Duration) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.values = append(t.values, int64(d)) - t.sum += int64(d) -} - -// Record the duration of an event that started at a time and ends now. -func (t *StandardResettingTimer) UpdateSince(ts time.Time) { - t.Update(time.Since(ts)) -} - -// resettingTimerSnapshot is a point-in-time copy of another ResettingTimer. -type resettingTimerSnapshot struct { - values []int64 - mean float64 - max int64 - min int64 - thresholdBoundaries []float64 - calculated bool -} - -// Count return the length of the values from snapshot. -func (t *resettingTimerSnapshot) Count() int { - return len(t.values) -} - -// Percentiles returns the boundaries for the input percentiles. -// note: this method is not thread safe -func (t *resettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { - t.calc(percentiles) - return t.thresholdBoundaries -} - -// Mean returns the mean of the snapshotted values -// note: this method is not thread safe -func (t *resettingTimerSnapshot) Mean() float64 { - if !t.calculated { - t.calc(nil) - } - - return t.mean -} - -// Max returns the max of the snapshotted values -// note: this method is not thread safe -func (t *resettingTimerSnapshot) Max() int64 { - if !t.calculated { - t.calc(nil) - } - return t.max -} - -// Min returns the min of the snapshotted values -// note: this method is not thread safe -func (t *resettingTimerSnapshot) Min() int64 { - if !t.calculated { - t.calc(nil) - } - return t.min -} - -func (t *resettingTimerSnapshot) calc(percentiles []float64) { - scores := CalculatePercentiles(t.values, percentiles) - t.thresholdBoundaries = scores - if len(t.values) == 0 { - return - } - t.min = t.values[0] - t.max = t.values[len(t.values)-1] -} diff --git a/metrics/resetting_timer_test.go b/metrics/resetting_timer_test.go deleted file mode 100644 index 4571fc8eb0..0000000000 --- a/metrics/resetting_timer_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package metrics - -import ( - "testing" - "time" -) - -func TestResettingTimer(t *testing.T) { - tests := []struct { - values []int64 - start int - end int - wantP50 float64 - wantP95 float64 - wantP99 float64 - wantMean float64 - wantMin int64 - wantMax int64 - }{ - { - values: []int64{}, - start: 1, - end: 11, - wantP50: 5.5, wantP95: 10, wantP99: 10, - wantMin: 1, wantMax: 10, wantMean: 5.5, - }, - { - values: []int64{}, - start: 1, - end: 101, - wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99, - wantMin: 1, wantMax: 100, wantMean: 50.5, - }, - { - values: []int64{1}, - start: 0, - end: 0, - wantP50: 1, wantP95: 1, wantP99: 1, - wantMin: 1, wantMax: 1, wantMean: 1, - }, - { - values: []int64{0}, - start: 0, - end: 0, - wantP50: 0, wantP95: 0, wantP99: 0, - wantMin: 0, wantMax: 0, wantMean: 0, - }, - { - values: []int64{}, - start: 0, - end: 0, - wantP50: 0, wantP95: 0, wantP99: 0, - wantMin: 0, wantMax: 0, wantMean: 0, - }, - { - values: []int64{1, 10}, - start: 0, - end: 0, - wantP50: 5.5, wantP95: 10, wantP99: 10, - wantMin: 1, wantMax: 10, wantMean: 5.5, - }, - } - for i, tt := range tests { - timer := NewResettingTimer() - - for i := tt.start; i < tt.end; i++ { - tt.values = append(tt.values, int64(i)) - } - - for _, v := range tt.values { - timer.Update(time.Duration(v)) - } - snap := timer.Snapshot() - - ps := snap.Percentiles([]float64{0.50, 0.95, 0.99}) - - if have, want := snap.Min(), tt.wantMin; have != want { - t.Fatalf("%d: min: have %d, want %d", i, have, want) - } - if have, want := snap.Max(), tt.wantMax; have != want { - t.Fatalf("%d: max: have %d, want %d", i, have, want) - } - if have, want := snap.Mean(), tt.wantMean; have != want { - t.Fatalf("%d: mean: have %v, want %v", i, have, want) - } - if have, want := ps[0], tt.wantP50; have != want { - t.Errorf("%d: p50: have %v, want %v", i, have, want) - } - if have, want := ps[1], tt.wantP95; have != want { - t.Errorf("%d: p95: have %v, want %v", i, have, want) - } - if have, want := ps[2], tt.wantP99; have != want { - t.Errorf("%d: p99: have %v, want %v", i, have, want) - } - } -} - -func TestResettingTimerWithFivePercentiles(t *testing.T) { - tests := []struct { - values []int64 - start int - end int - wantP05 float64 - wantP20 float64 - wantP50 float64 - wantP95 float64 - wantP99 float64 - wantMean float64 - wantMin int64 - wantMax int64 - }{ - { - values: []int64{}, - start: 1, - end: 11, - wantP05: 1, wantP20: 2.2, wantP50: 5.5, wantP95: 10, wantP99: 10, - wantMin: 1, wantMax: 10, wantMean: 5.5, - }, - { - values: []int64{}, - start: 1, - end: 101, - wantP05: 5.050000000000001, wantP20: 20.200000000000003, wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99, - wantMin: 1, wantMax: 100, wantMean: 50.5, - }, - { - values: []int64{1}, - start: 0, - end: 0, - wantP05: 1, wantP20: 1, wantP50: 1, wantP95: 1, wantP99: 1, - wantMin: 1, wantMax: 1, wantMean: 1, - }, - { - values: []int64{0}, - start: 0, - end: 0, - wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0, - wantMin: 0, wantMax: 0, wantMean: 0, - }, - { - values: []int64{}, - start: 0, - end: 0, - wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0, - wantMin: 0, wantMax: 0, wantMean: 0, - }, - { - values: []int64{1, 10}, - start: 0, - end: 0, - wantP05: 1, wantP20: 1, wantP50: 5.5, wantP95: 10, wantP99: 10, - wantMin: 1, wantMax: 10, wantMean: 5.5, - }, - } - for ind, tt := range tests { - timer := NewResettingTimer() - - for i := tt.start; i < tt.end; i++ { - tt.values = append(tt.values, int64(i)) - } - - for _, v := range tt.values { - timer.Update(time.Duration(v)) - } - - snap := timer.Snapshot() - - ps := snap.Percentiles([]float64{0.05, 0.20, 0.50, 0.95, 0.99}) - - if tt.wantMin != snap.Min() { - t.Errorf("%d: min: got %d, want %d", ind, snap.Min(), tt.wantMin) - } - - if tt.wantMax != snap.Max() { - t.Errorf("%d: max: got %d, want %d", ind, snap.Max(), tt.wantMax) - } - - if tt.wantMean != snap.Mean() { - t.Errorf("%d: mean: got %.2f, want %.2f", ind, snap.Mean(), tt.wantMean) - } - if tt.wantP05 != ps[0] { - t.Errorf("%d: p05: got %v, want %v", ind, ps[0], tt.wantP05) - } - if tt.wantP20 != ps[1] { - t.Errorf("%d: p20: got %v, want %v", ind, ps[1], tt.wantP20) - } - if tt.wantP50 != ps[2] { - t.Errorf("%d: p50: got %v, want %v", ind, ps[2], tt.wantP50) - } - if tt.wantP95 != ps[3] { - t.Errorf("%d: p95: got %v, want %v", ind, ps[3], tt.wantP95) - } - if tt.wantP99 != ps[4] { - t.Errorf("%d: p99: got %v, want %v", ind, ps[4], tt.wantP99) - } - } -} diff --git a/metrics/sample.go b/metrics/sample.go deleted file mode 100644 index bb81e105cf..0000000000 --- a/metrics/sample.go +++ /dev/null @@ -1,446 +0,0 @@ -package metrics - -import ( - "math" - "math/rand" - "sync" - "time" - - "golang.org/x/exp/slices" -) - -const rescaleThreshold = time.Hour - -type SampleSnapshot interface { - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Size() int - StdDev() float64 - Sum() int64 - Variance() float64 -} - -// Samples maintain a statistically-significant selection of values from -// a stream. -type Sample interface { - Snapshot() SampleSnapshot - Clear() - Update(int64) -} - -// ExpDecaySample is an exponentially-decaying sample using a forward-decaying -// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time -// Decay Model for Streaming Systems". -// -// -type ExpDecaySample struct { - alpha float64 - count int64 - mutex sync.Mutex - reservoirSize int - t0, t1 time.Time - values *expDecaySampleHeap - rand *rand.Rand -} - -// NewExpDecaySample constructs a new exponentially-decaying sample with the -// given reservoir size and alpha. -func NewExpDecaySample(reservoirSize int, alpha float64) Sample { - if !Enabled { - return NilSample{} - } - s := &ExpDecaySample{ - alpha: alpha, - reservoirSize: reservoirSize, - t0: time.Now(), - values: newExpDecaySampleHeap(reservoirSize), - } - s.t1 = s.t0.Add(rescaleThreshold) - return s -} - -// SetRand sets the random source (useful in tests) -func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample { - s.rand = prng - return s -} - -// Clear clears all samples. -func (s *ExpDecaySample) Clear() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count = 0 - s.t0 = time.Now() - s.t1 = s.t0.Add(rescaleThreshold) - s.values.Clear() -} - -// Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() SampleSnapshot { - s.mutex.Lock() - defer s.mutex.Unlock() - var ( - samples = s.values.Values() - values = make([]int64, len(samples)) - max int64 = math.MinInt64 - min int64 = math.MaxInt64 - sum int64 - ) - for i, item := range samples { - v := item.v - values[i] = v - sum += v - if v > max { - max = v - } - if v < min { - min = v - } - } - return newSampleSnapshotPrecalculated(s.count, values, min, max, sum) -} - -// Update samples a new value. -func (s *ExpDecaySample) Update(v int64) { - s.update(time.Now(), v) -} - -// update samples a new value at a particular timestamp. This is a method all -// its own to facilitate testing. -func (s *ExpDecaySample) update(t time.Time, v int64) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count++ - if s.values.Size() == s.reservoirSize { - s.values.Pop() - } - var f64 float64 - if s.rand != nil { - f64 = s.rand.Float64() - } else { - f64 = rand.Float64() - } - s.values.Push(expDecaySample{ - k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64, - v: v, - }) - if t.After(s.t1) { - values := s.values.Values() - t0 := s.t0 - s.values.Clear() - s.t0 = t - s.t1 = s.t0.Add(rescaleThreshold) - for _, v := range values { - v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) - s.values.Push(v) - } - } -} - -// NilSample is a no-op Sample. -type NilSample struct{} - -func (NilSample) Clear() {} -func (NilSample) Snapshot() SampleSnapshot { return (*emptySnapshot)(nil) } -func (NilSample) Update(v int64) {} - -// SamplePercentile returns an arbitrary percentile of the slice of int64. -func SamplePercentile(values []int64, p float64) float64 { - return CalculatePercentiles(values, []float64{p})[0] -} - -// CalculatePercentiles returns a slice of arbitrary percentiles of the slice of -// int64. This method returns interpolated results, so e.g if there are only two -// values, [0, 10], a 50% percentile will land between them. -// -// Note: As a side-effect, this method will also sort the slice of values. -// Note2: The input format for percentiles is NOT percent! To express 50%, use 0.5, not 50. -func CalculatePercentiles(values []int64, ps []float64) []float64 { - scores := make([]float64, len(ps)) - size := len(values) - if size == 0 { - return scores - } - slices.Sort(values) - for i, p := range ps { - pos := p * float64(size+1) - - if pos < 1.0 { - scores[i] = float64(values[0]) - } else if pos >= float64(size) { - scores[i] = float64(values[size-1]) - } else { - lower := float64(values[int(pos)-1]) - upper := float64(values[int(pos)]) - scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) - } - } - return scores -} - -// sampleSnapshot is a read-only copy of another Sample. -type sampleSnapshot struct { - count int64 - values []int64 - - max int64 - min int64 - mean float64 - sum int64 - variance float64 -} - -// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using -// precalculated sums to avoid iterating the values -func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { - if len(values) == 0 { - return &sampleSnapshot{ - count: count, - values: values, - } - } - return &sampleSnapshot{ - count: count, - values: values, - max: max, - min: min, - mean: float64(sum) / float64(len(values)), - sum: sum, - } -} - -// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some -// numbers. -func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { - var ( - max int64 = math.MinInt64 - min int64 = math.MaxInt64 - sum int64 - ) - for _, v := range values { - sum += v - if v > max { - max = v - } - if v < min { - min = v - } - } - return newSampleSnapshotPrecalculated(count, values, min, max, sum) -} - -// Count returns the count of inputs at the time the snapshot was taken. -func (s *sampleSnapshot) Count() int64 { return s.count } - -// Max returns the maximal value at the time the snapshot was taken. -func (s *sampleSnapshot) Max() int64 { return s.max } - -// Mean returns the mean value at the time the snapshot was taken. -func (s *sampleSnapshot) Mean() float64 { return s.mean } - -// Min returns the minimal value at the time the snapshot was taken. -func (s *sampleSnapshot) Min() int64 { return s.min } - -// Percentile returns an arbitrary percentile of values at the time the -// snapshot was taken. -func (s *sampleSnapshot) Percentile(p float64) float64 { - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values at the time -// the snapshot was taken. -func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { - return CalculatePercentiles(s.values, ps) -} - -// Size returns the size of the sample at the time the snapshot was taken. -func (s *sampleSnapshot) Size() int { return len(s.values) } - -// Snapshot returns the snapshot. -func (s *sampleSnapshot) Snapshot() SampleSnapshot { return s } - -// StdDev returns the standard deviation of values at the time the snapshot was -// taken. -func (s *sampleSnapshot) StdDev() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return math.Sqrt(s.variance) -} - -// Sum returns the sum of values at the time the snapshot was taken. -func (s *sampleSnapshot) Sum() int64 { return s.sum } - -// Values returns a copy of the values in the sample. -func (s *sampleSnapshot) Values() []int64 { - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of values at the time the snapshot was taken. -func (s *sampleSnapshot) Variance() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return s.variance -} - -// SampleVariance returns the variance of the slice of int64. -func SampleVariance(mean float64, values []int64) float64 { - if len(values) == 0 { - return 0.0 - } - var sum float64 - for _, v := range values { - d := float64(v) - mean - sum += d * d - } - return sum / float64(len(values)) -} - -// A uniform sample using Vitter's Algorithm R. -// -// -type UniformSample struct { - count int64 - mutex sync.Mutex - reservoirSize int - values []int64 - rand *rand.Rand -} - -// NewUniformSample constructs a new uniform sample with the given reservoir -// size. -func NewUniformSample(reservoirSize int) Sample { - if !Enabled { - return NilSample{} - } - return &UniformSample{ - reservoirSize: reservoirSize, - values: make([]int64, 0, reservoirSize), - } -} - -// SetRand sets the random source (useful in tests) -func (s *UniformSample) SetRand(prng *rand.Rand) Sample { - s.rand = prng - return s -} - -// Clear clears all samples. -func (s *UniformSample) Clear() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count = 0 - s.values = make([]int64, 0, s.reservoirSize) -} - -// Snapshot returns a read-only copy of the sample. -func (s *UniformSample) Snapshot() SampleSnapshot { - s.mutex.Lock() - values := make([]int64, len(s.values)) - copy(values, s.values) - count := s.count - s.mutex.Unlock() - return newSampleSnapshot(count, values) -} - -// Update samples a new value. -func (s *UniformSample) Update(v int64) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count++ - if len(s.values) < s.reservoirSize { - s.values = append(s.values, v) - } else { - var r int64 - if s.rand != nil { - r = s.rand.Int63n(s.count) - } else { - r = rand.Int63n(s.count) - } - if r < int64(len(s.values)) { - s.values[int(r)] = v - } - } -} - -// expDecaySample represents an individual sample in a heap. -type expDecaySample struct { - k float64 - v int64 -} - -func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap { - return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)} -} - -// expDecaySampleHeap is a min-heap of expDecaySamples. -// The internal implementation is copied from the standard library's container/heap -type expDecaySampleHeap struct { - s []expDecaySample -} - -func (h *expDecaySampleHeap) Clear() { - h.s = h.s[:0] -} - -func (h *expDecaySampleHeap) Push(s expDecaySample) { - n := len(h.s) - h.s = h.s[0 : n+1] - h.s[n] = s - h.up(n) -} - -func (h *expDecaySampleHeap) Pop() expDecaySample { - n := len(h.s) - 1 - h.s[0], h.s[n] = h.s[n], h.s[0] - h.down(0, n) - - n = len(h.s) - s := h.s[n-1] - h.s = h.s[0 : n-1] - return s -} - -func (h *expDecaySampleHeap) Size() int { - return len(h.s) -} - -func (h *expDecaySampleHeap) Values() []expDecaySample { - return h.s -} - -func (h *expDecaySampleHeap) up(j int) { - for { - i := (j - 1) / 2 // parent - if i == j || !(h.s[j].k < h.s[i].k) { - break - } - h.s[i], h.s[j] = h.s[j], h.s[i] - j = i - } -} - -func (h *expDecaySampleHeap) down(i, n int) { - for { - j1 := 2*i + 1 - if j1 >= n || j1 < 0 { // j1 < 0 after int overflow - break - } - j := j1 // left child - if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) { - j = j2 // = 2*i + 2 // right child - } - if !(h.s[j].k < h.s[i].k) { - break - } - h.s[i], h.s[j] = h.s[j], h.s[i] - i = j - } -} diff --git a/metrics/sample_test.go b/metrics/sample_test.go deleted file mode 100644 index 7967357055..0000000000 --- a/metrics/sample_test.go +++ /dev/null @@ -1,360 +0,0 @@ -package metrics - -import ( - "math" - "math/rand" - "runtime" - "testing" - "time" -) - -const epsilonPercentile = .00000000001 - -// Benchmark{Compute,Copy}{1000,1000000} demonstrate that, even for relatively -// expensive computations like Variance, the cost of copying the Sample, as -// approximated by a make and copy, is much greater than the cost of the -// computation for small samples and only slightly less for large samples. -func BenchmarkCompute1000(b *testing.B) { - s := make([]int64, 1000) - var sum int64 - for i := 0; i < len(s); i++ { - s[i] = int64(i) - sum += int64(i) - } - mean := float64(sum) / float64(len(s)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - SampleVariance(mean, s) - } -} -func BenchmarkCompute1000000(b *testing.B) { - s := make([]int64, 1000000) - var sum int64 - for i := 0; i < len(s); i++ { - s[i] = int64(i) - sum += int64(i) - } - mean := float64(sum) / float64(len(s)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - SampleVariance(mean, s) - } -} -func BenchmarkCopy1000(b *testing.B) { - s := make([]int64, 1000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} -func BenchmarkCopy1000000(b *testing.B) { - s := make([]int64, 1000000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} - -func BenchmarkExpDecaySample257(b *testing.B) { - benchmarkSample(b, NewExpDecaySample(257, 0.015)) -} - -func BenchmarkExpDecaySample514(b *testing.B) { - benchmarkSample(b, NewExpDecaySample(514, 0.015)) -} - -func BenchmarkExpDecaySample1028(b *testing.B) { - benchmarkSample(b, NewExpDecaySample(1028, 0.015)) -} - -func BenchmarkUniformSample257(b *testing.B) { - benchmarkSample(b, NewUniformSample(257)) -} - -func BenchmarkUniformSample514(b *testing.B) { - benchmarkSample(b, NewUniformSample(514)) -} - -func BenchmarkUniformSample1028(b *testing.B) { - benchmarkSample(b, NewUniformSample(1028)) -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func TestExpDecaySample(t *testing.T) { - for _, tc := range []struct { - reservoirSize int - alpha float64 - updates int - }{ - {100, 0.99, 10}, - {1000, 0.01, 100}, - {100, 0.99, 1000}, - } { - sample := NewExpDecaySample(tc.reservoirSize, tc.alpha) - for i := 0; i < tc.updates; i++ { - sample.Update(int64(i)) - } - snap := sample.Snapshot() - if have, want := int(snap.Count()), tc.updates; have != want { - t.Errorf("have %d want %d", have, want) - } - if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) - } - values := snap.(*sampleSnapshot).values - if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) - } - for _, v := range values { - if v > int64(tc.updates) || v < 0 { - t.Errorf("out of range [0, %d): %v", tc.updates, v) - } - } - } -} - -// This test makes sure that the sample's priority is not amplified by using -// nanosecond duration since start rather than second duration since start. -// The priority becomes +Inf quickly after starting if this is done, -// effectively freezing the set of samples until a rescale step happens. -func TestExpDecaySampleNanosecondRegression(t *testing.T) { - sw := NewExpDecaySample(100, 0.99) - for i := 0; i < 100; i++ { - sw.Update(10) - } - time.Sleep(1 * time.Millisecond) - for i := 0; i < 100; i++ { - sw.Update(20) - } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values - avg := float64(0) - for i := 0; i < len(v); i++ { - avg += float64(v[i]) - } - avg /= float64(len(v)) - if avg > 16 || avg < 14 { - t.Errorf("out of range [14, 16]: %v\n", avg) - } -} - -func TestExpDecaySampleRescale(t *testing.T) { - s := NewExpDecaySample(2, 0.001).(*ExpDecaySample) - s.update(time.Now(), 1) - s.update(time.Now().Add(time.Hour+time.Microsecond), 1) - for _, v := range s.values.Values() { - if v.k == 0.0 { - t.Fatal("v.k == 0.0") - } - } -} - -func TestExpDecaySampleSnapshot(t *testing.T) { - now := time.Now() - s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1))) - for i := 1; i <= 10000; i++ { - s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i)) - } - snapshot := s.Snapshot() - s.Update(1) - testExpDecaySampleStatistics(t, snapshot) -} - -func TestExpDecaySampleStatistics(t *testing.T) { - now := time.Now() - s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1))) - for i := 1; i <= 10000; i++ { - s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i)) - } - testExpDecaySampleStatistics(t, s.Snapshot()) -} - -func TestUniformSample(t *testing.T) { - sw := NewUniformSample(100) - for i := 0; i < 1000; i++ { - sw.Update(int64(i)) - } - s := sw.Snapshot() - if size := s.Count(); size != 1000 { - t.Errorf("s.Count(): 1000 != %v\n", size) - } - if size := s.Size(); size != 100 { - t.Errorf("s.Size(): 100 != %v\n", size) - } - values := s.(*sampleSnapshot).values - - if l := len(values); l != 100 { - t.Errorf("len(s.Values()): 100 != %v\n", l) - } - for _, v := range values { - if v > 1000 || v < 0 { - t.Errorf("out of range [0, 100): %v\n", v) - } - } -} - -func TestUniformSampleIncludesTail(t *testing.T) { - sw := NewUniformSample(100) - max := 100 - for i := 0; i < max; i++ { - sw.Update(int64(i)) - } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values - sum := 0 - exp := (max - 1) * max / 2 - for i := 0; i < len(v); i++ { - sum += int(v[i]) - } - if exp != sum { - t.Errorf("sum: %v != %v\n", exp, sum) - } -} - -func TestUniformSampleSnapshot(t *testing.T) { - s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1))) - for i := 1; i <= 10000; i++ { - s.Update(int64(i)) - } - snapshot := s.Snapshot() - s.Update(1) - testUniformSampleStatistics(t, snapshot) -} - -func TestUniformSampleStatistics(t *testing.T) { - s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1))) - for i := 1; i <= 10000; i++ { - s.Update(int64(i)) - } - testUniformSampleStatistics(t, s.Snapshot()) -} - -func benchmarkSample(b *testing.B, s Sample) { - var memStats runtime.MemStats - runtime.ReadMemStats(&memStats) - pauseTotalNs := memStats.PauseTotalNs - b.ResetTimer() - for i := 0; i < b.N; i++ { - s.Update(1) - } - b.StopTimer() - runtime.GC() - runtime.ReadMemStats(&memStats) - b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N) -} - -func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { - if count := s.Count(); count != 10000 { - t.Errorf("s.Count(): 10000 != %v\n", count) - } - if min := s.Min(); min != 107 { - t.Errorf("s.Min(): 107 != %v\n", min) - } - if max := s.Max(); max != 10000 { - t.Errorf("s.Max(): 10000 != %v\n", max) - } - if mean := s.Mean(); mean != 4965.98 { - t.Errorf("s.Mean(): 4965.98 != %v\n", mean) - } - if stdDev := s.StdDev(); stdDev != 2959.825156930727 { - t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev) - } - ps := s.Percentiles([]float64{0.5, 0.75, 0.99}) - if ps[0] != 4615 { - t.Errorf("median: 4615 != %v\n", ps[0]) - } - if ps[1] != 7672 { - t.Errorf("75th percentile: 7672 != %v\n", ps[1]) - } - if ps[2] != 9998.99 { - t.Errorf("99th percentile: 9998.99 != %v\n", ps[2]) - } -} - -func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) { - if count := s.Count(); count != 10000 { - t.Errorf("s.Count(): 10000 != %v\n", count) - } - if min := s.Min(); min != 37 { - t.Errorf("s.Min(): 37 != %v\n", min) - } - if max := s.Max(); max != 9989 { - t.Errorf("s.Max(): 9989 != %v\n", max) - } - if mean := s.Mean(); mean != 4748.14 { - t.Errorf("s.Mean(): 4748.14 != %v\n", mean) - } - if stdDev := s.StdDev(); stdDev != 2826.684117548333 { - t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev) - } - ps := s.Percentiles([]float64{0.5, 0.75, 0.99}) - if ps[0] != 4599 { - t.Errorf("median: 4599 != %v\n", ps[0]) - } - if ps[1] != 7380.5 { - t.Errorf("75th percentile: 7380.5 != %v\n", ps[1]) - } - if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile { - t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2]) - } -} - -// TestUniformSampleConcurrentUpdateCount would expose data race problems with -// concurrent Update and Count calls on Sample when test is called with -race -// argument -func TestUniformSampleConcurrentUpdateCount(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - s := NewUniformSample(100) - for i := 0; i < 100; i++ { - s.Update(int64(i)) - } - quit := make(chan struct{}) - go func() { - t := time.NewTicker(10 * time.Millisecond) - defer t.Stop() - for { - select { - case <-t.C: - s.Update(rand.Int63()) - case <-quit: - t.Stop() - return - } - } - }() - for i := 0; i < 1000; i++ { - s.Snapshot().Count() - time.Sleep(5 * time.Millisecond) - } - quit <- struct{}{} -} - -func BenchmarkCalculatePercentiles(b *testing.B) { - pss := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} - var vals []int64 - for i := 0; i < 1000; i++ { - vals = append(vals, int64(rand.Int31())) - } - v := make([]int64, len(vals)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - copy(v, vals) - _ = CalculatePercentiles(v, pss) - } -} diff --git a/metrics/syslog.go b/metrics/syslog.go deleted file mode 100644 index fd856d6973..0000000000 --- a/metrics/syslog.go +++ /dev/null @@ -1,83 +0,0 @@ -//go:build !windows -// +build !windows - -package metrics - -import ( - "fmt" - "log/syslog" - "time" -) - -// Output each metric in the given registry to syslog periodically using -// the given syslogger. -func Syslog(r Registry, d time.Duration, w *syslog.Writer) { - for range time.Tick(d) { - r.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count())) - case CounterFloat64: - w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count())) - case Gauge: - w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value())) - case GaugeFloat64: - w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value())) - case GaugeInfo: - w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value())) - case Healthcheck: - metric.Check() - w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - w.Info(fmt.Sprintf( - "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f", - name, - h.Count(), - h.Min(), - h.Max(), - h.Mean(), - h.StdDev(), - ps[0], - ps[1], - ps[2], - ps[3], - ps[4], - )) - case Meter: - m := metric.Snapshot() - w.Info(fmt.Sprintf( - "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", - name, - m.Count(), - m.Rate1(), - m.Rate5(), - m.Rate15(), - m.RateMean(), - )) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - w.Info(fmt.Sprintf( - "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f", - name, - t.Count(), - t.Min(), - t.Max(), - t.Mean(), - t.StdDev(), - ps[0], - ps[1], - ps[2], - ps[3], - ps[4], - t.Rate1(), - t.Rate5(), - t.Rate15(), - t.RateMean(), - )) - } - }) - } -} diff --git a/metrics/testdata/opentsb.want b/metrics/testdata/opentsb.want deleted file mode 100644 index 43fe1b2ac2..0000000000 --- a/metrics/testdata/opentsb.want +++ /dev/null @@ -1,23 +0,0 @@ -put pre.elite.count 978307200 1337 host=hal9000 -put pre.elite.one-minute 978307200 0.00 host=hal9000 -put pre.elite.five-minute 978307200 0.00 host=hal9000 -put pre.elite.fifteen-minute 978307200 0.00 host=hal9000 -put pre.elite.mean 978307200 0.00 host=hal9000 -put pre.foo.value 978307200 {"chain_id":"5"} host=hal9000 -put pre.months.count 978307200 12 host=hal9000 -put pre.pi.value 978307200 3.140000 host=hal9000 -put pre.second.count 978307200 1 host=hal9000 -put pre.second.min 978307200 1000 host=hal9000 -put pre.second.max 978307200 1000 host=hal9000 -put pre.second.mean 978307200 1000.00 host=hal9000 -put pre.second.std-dev 978307200 0.00 host=hal9000 -put pre.second.50-percentile 978307200 1000.00 host=hal9000 -put pre.second.75-percentile 978307200 1000.00 host=hal9000 -put pre.second.95-percentile 978307200 1000.00 host=hal9000 -put pre.second.99-percentile 978307200 1000.00 host=hal9000 -put pre.second.999-percentile 978307200 1000.00 host=hal9000 -put pre.second.one-minute 978307200 0.00 host=hal9000 -put pre.second.five-minute 978307200 0.00 host=hal9000 -put pre.second.fifteen-minute 978307200 0.00 host=hal9000 -put pre.second.mean-rate 978307200 0.00 host=hal9000 -put pre.tau.count 978307200 1.570000 host=hal9000 diff --git a/metrics/timer.go b/metrics/timer.go deleted file mode 100644 index bb8def82fb..0000000000 --- a/metrics/timer.go +++ /dev/null @@ -1,182 +0,0 @@ -package metrics - -import ( - "sync" - "time" -) - -type TimerSnapshot interface { - HistogramSnapshot - MeterSnapshot -} - -// Timers capture the duration and rate of events. -type Timer interface { - Snapshot() TimerSnapshot - Stop() - Time(func()) - UpdateSince(time.Time) - Update(time.Duration) -} - -// GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewTimer).(Timer) -} - -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. -// Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m Meter) Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ - histogram: h, - meter: m, - } -} - -// NewRegisteredTimer constructs and registers a new StandardTimer. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { - c := NewTimer() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewTimer constructs a new StandardTimer using an exponentially-decaying -// sample with the same reservoir size and alpha as UNIX load averages. -// Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ - histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), - meter: NewMeter(), - } -} - -// NilTimer is a no-op Timer. -type NilTimer struct{} - -func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } -func (NilTimer) Stop() {} -func (NilTimer) Time(f func()) { f() } -func (NilTimer) Update(time.Duration) {} -func (NilTimer) UpdateSince(time.Time) {} - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { - histogram Histogram - meter Meter - mutex sync.Mutex -} - -// Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() TimerSnapshot { - t.mutex.Lock() - defer t.mutex.Unlock() - return &timerSnapshot{ - histogram: t.histogram.Snapshot(), - meter: t.meter.Snapshot(), - } -} - -// Stop stops the meter. -func (t *StandardTimer) Stop() { - t.meter.Stop() -} - -// Record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { - ts := time.Now() - f() - t.Update(time.Since(ts)) -} - -// Record the duration of an event, in nanoseconds. -func (t *StandardTimer) Update(d time.Duration) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.histogram.Update(d.Nanoseconds()) - t.meter.Mark(1) -} - -// Record the duration of an event that started at a time and ends now. -// The record uses nanoseconds. -func (t *StandardTimer) UpdateSince(ts time.Time) { - t.Update(time.Since(ts)) -} - -// timerSnapshot is a read-only copy of another Timer. -type timerSnapshot struct { - histogram HistogramSnapshot - meter MeterSnapshot -} - -// Count returns the number of events recorded at the time the snapshot was -// taken. -func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } - -// Max returns the maximum value at the time the snapshot was taken. -func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } - -// Size returns the size of the sample at the time the snapshot was taken. -func (t *timerSnapshot) Size() int { return t.histogram.Size() } - -// Mean returns the mean value at the time the snapshot was taken. -func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } - -// Min returns the minimum value at the time the snapshot was taken. -func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } - -// Percentile returns an arbitrary percentile of sampled values at the time the -// snapshot was taken. -func (t *timerSnapshot) Percentile(p float64) float64 { - return t.histogram.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of sampled values at -// the time the snapshot was taken. -func (t *timerSnapshot) Percentiles(ps []float64) []float64 { - return t.histogram.Percentiles(ps) -} - -// Rate1 returns the one-minute moving average rate of events per second at the -// time the snapshot was taken. -func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } - -// Rate5 returns the five-minute moving average rate of events per second at -// the time the snapshot was taken. -func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } - -// Rate15 returns the fifteen-minute moving average rate of events per second -// at the time the snapshot was taken. -func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } - -// RateMean returns the meter's mean rate of events per second at the time the -// snapshot was taken. -func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } - -// StdDev returns the standard deviation of the values at the time the snapshot -// was taken. -func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } - -// Sum returns the sum at the time the snapshot was taken. -func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } - -// Variance returns the variance of the values at the time the snapshot was -// taken. -func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/timer_test.go b/metrics/timer_test.go deleted file mode 100644 index f10de16c9c..0000000000 --- a/metrics/timer_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package metrics - -import ( - "fmt" - "math" - "testing" - "time" -) - -func BenchmarkTimer(b *testing.B) { - tm := NewTimer() - b.ResetTimer() - for i := 0; i < b.N; i++ { - tm.Update(1) - } -} - -func TestGetOrRegisterTimer(t *testing.T) { - r := NewRegistry() - NewRegisteredTimer("foo", r).Update(47) - if tm := GetOrRegisterTimer("foo", r).Snapshot(); tm.Count() != 1 { - t.Fatal(tm) - } -} - -func TestTimerExtremes(t *testing.T) { - tm := NewTimer() - tm.Update(math.MaxInt64) - tm.Update(0) - if stdDev := tm.Snapshot().StdDev(); stdDev != 4.611686018427388e+18 { - t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev) - } -} - -func TestTimerStop(t *testing.T) { - l := len(arbiter.meters) - tm := NewTimer() - if l+1 != len(arbiter.meters) { - t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters)) - } - tm.Stop() - if l != len(arbiter.meters) { - t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters)) - } -} - -func TestTimerFunc(t *testing.T) { - var ( - tm = NewTimer() - testStart = time.Now() - actualTime time.Duration - ) - tm.Time(func() { - time.Sleep(50 * time.Millisecond) - actualTime = time.Since(testStart) - }) - var ( - drift = time.Millisecond * 2 - measured = time.Duration(tm.Snapshot().Max()) - ceil = actualTime + drift - floor = actualTime - drift - ) - if measured > ceil || measured < floor { - t.Errorf("tm.Max(): %v > %v || %v > %v\n", measured, ceil, measured, floor) - } -} - -func TestTimerZero(t *testing.T) { - tm := NewTimer().Snapshot() - if count := tm.Count(); count != 0 { - t.Errorf("tm.Count(): 0 != %v\n", count) - } - if min := tm.Min(); min != 0 { - t.Errorf("tm.Min(): 0 != %v\n", min) - } - if max := tm.Max(); max != 0 { - t.Errorf("tm.Max(): 0 != %v\n", max) - } - if mean := tm.Mean(); mean != 0.0 { - t.Errorf("tm.Mean(): 0.0 != %v\n", mean) - } - if stdDev := tm.StdDev(); stdDev != 0.0 { - t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev) - } - ps := tm.Percentiles([]float64{0.5, 0.75, 0.99}) - if ps[0] != 0.0 { - t.Errorf("median: 0.0 != %v\n", ps[0]) - } - if ps[1] != 0.0 { - t.Errorf("75th percentile: 0.0 != %v\n", ps[1]) - } - if ps[2] != 0.0 { - t.Errorf("99th percentile: 0.0 != %v\n", ps[2]) - } - if rate1 := tm.Rate1(); rate1 != 0.0 { - t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1) - } - if rate5 := tm.Rate5(); rate5 != 0.0 { - t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5) - } - if rate15 := tm.Rate15(); rate15 != 0.0 { - t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15) - } - if rateMean := tm.RateMean(); rateMean != 0.0 { - t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean) - } -} - -func ExampleGetOrRegisterTimer() { - m := "account.create.latency" - t := GetOrRegisterTimer(m, nil) - t.Update(47) - fmt.Println(t.Snapshot().Max()) // Output: 47 -} diff --git a/metrics/validate.sh b/metrics/validate.sh deleted file mode 100755 index 0d8ba28df3..0000000000 --- a/metrics/validate.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# check there are no formatting issues -GOFMT_LINES=$(gofmt -l . | wc -l | xargs) -test "$GOFMT_LINES" -eq 0 || echo "gofmt needs to be run, ${GOFMT_LINES} files have issues" - -# run the tests for the root package -go test -race . diff --git a/metrics/writer.go b/metrics/writer.go deleted file mode 100644 index 098da45c27..0000000000 --- a/metrics/writer.go +++ /dev/null @@ -1,100 +0,0 @@ -package metrics - -import ( - "fmt" - "io" - "strings" - "time" - - "golang.org/x/exp/slices" -) - -// Write sorts writes each metric in the given registry periodically to the -// given io.Writer. -func Write(r Registry, d time.Duration, w io.Writer) { - for range time.Tick(d) { - WriteOnce(r, w) - } -} - -// WriteOnce sorts and writes metrics in the given registry to the given -// io.Writer. -func WriteOnce(r Registry, w io.Writer) { - var namedMetrics []namedMetric - r.Each(func(name string, i interface{}) { - namedMetrics = append(namedMetrics, namedMetric{name, i}) - }) - slices.SortFunc(namedMetrics, namedMetric.cmp) - for _, namedMetric := range namedMetrics { - switch metric := namedMetric.m.(type) { - case Counter: - fmt.Fprintf(w, "counter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: - fmt.Fprintf(w, "counter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count()) - case Gauge: - fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: - fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: - fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String()) - case Healthcheck: - metric.Check() - fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) - fmt.Fprintf(w, " error: %v\n", metric.Error()) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "histogram %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", h.Count()) - fmt.Fprintf(w, " min: %9d\n", h.Min()) - fmt.Fprintf(w, " max: %9d\n", h.Max()) - fmt.Fprintf(w, " mean: %12.2f\n", h.Mean()) - fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev()) - fmt.Fprintf(w, " median: %12.2f\n", ps[0]) - fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) - fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) - fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) - fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "meter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", m.Count()) - fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1()) - fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) - fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) - fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "timer %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", t.Count()) - fmt.Fprintf(w, " min: %9d\n", t.Min()) - fmt.Fprintf(w, " max: %9d\n", t.Max()) - fmt.Fprintf(w, " mean: %12.2f\n", t.Mean()) - fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev()) - fmt.Fprintf(w, " median: %12.2f\n", ps[0]) - fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) - fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) - fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) - fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1()) - fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5()) - fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15()) - fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean()) - } - } -} - -type namedMetric struct { - name string - m interface{} -} - -func (m namedMetric) cmp(other namedMetric) int { - return strings.Compare(m.name, other.name) -} diff --git a/metrics/writer_test.go b/metrics/writer_test.go deleted file mode 100644 index 8376bf8975..0000000000 --- a/metrics/writer_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package metrics - -import ( - "testing" - - "golang.org/x/exp/slices" -) - -func TestMetricsSorting(t *testing.T) { - var namedMetrics = []namedMetric{ - {name: "zzz"}, - {name: "bbb"}, - {name: "fff"}, - {name: "ggg"}, - } - - slices.SortFunc(namedMetrics, namedMetric.cmp) - for i, name := range []string{"bbb", "fff", "ggg", "zzz"} { - if namedMetrics[i].name != name { - t.Fail() - } - } -} diff --git a/peer/peer_tracker.go b/peer/peer_tracker.go index b1a436c067..30f73d854d 100644 --- a/peer/peer_tracker.go +++ b/peer/peer_tracker.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/log" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) const ( diff --git a/peer/stats/stats.go b/peer/stats/stats.go index 57f3b15597..5a3c2918f4 100644 --- a/peer/stats/stats.go +++ b/peer/stats/stats.go @@ -6,7 +6,7 @@ package stats import ( "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) // RequestHandlerStats provides the interface for metrics for app requests. diff --git a/plugin/evm/gossip_stats.go b/plugin/evm/gossip_stats.go index 3a6f552fcc..d4e2e60457 100644 --- a/plugin/evm/gossip_stats.go +++ b/plugin/evm/gossip_stats.go @@ -3,7 +3,7 @@ package evm -import "github.com/ava-labs/subnet-evm/metrics" +import "github.com/ava-labs/libevm/metrics" var _ GossipStats = &gossipStats{} diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go index e54d40e676..2d6acb3f47 100644 --- a/plugin/evm/network_handler.go +++ b/plugin/evm/network_handler.go @@ -8,7 +8,7 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers" syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 349647b525..3a37e9da21 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -24,13 +24,13 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/database" "github.com/ava-labs/subnet-evm/predicate" diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index edf3228f88..7c419edb6d 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -23,6 +23,8 @@ import ( "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/prometheus/client_golang/prometheus" + "github.com/ava-labs/libevm/metrics" + libevmprometheus "github.com/ava-labs/libevm/metrics/prometheus" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" @@ -32,8 +34,6 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/eth" "github.com/ava-labs/subnet-evm/eth/ethconfig" - "github.com/ava-labs/subnet-evm/metrics" - subnetEVMPrometheus "github.com/ava-labs/subnet-evm/metrics/prometheus" "github.com/ava-labs/subnet-evm/miner" "github.com/ava-labs/subnet-evm/node" "github.com/ava-labs/subnet-evm/params" @@ -555,7 +555,7 @@ func (vm *VM) initializeMetrics() error { return nil } - gatherer := subnetEVMPrometheus.Gatherer(metrics.DefaultRegistry) + gatherer := libevmprometheus.NewGatherer(metrics.DefaultRegistry) if err := vm.ctx.Metrics.Register(ethMetricsPrefix, gatherer); err != nil { return err } diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 194b3ff183..e08a360f9e 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/libevm/metrics" accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" @@ -47,7 +48,6 @@ import ( "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/eth" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index f8043e3499..044882db8a 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -17,9 +17,9 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/utils" diff --git a/rpc/handler.go b/rpc/handler.go index e498c590a4..cabbd10cde 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -38,7 +38,7 @@ import ( "sync" "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/log" "golang.org/x/time/rate" ) diff --git a/rpc/metrics.go b/rpc/metrics.go index b4ef5401a0..a4469545d7 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -30,7 +30,7 @@ import ( "fmt" "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) var ( diff --git a/sync/client/stats/stats.go b/sync/client/stats/stats.go index 92519e5da4..6567b00ab2 100644 --- a/sync/client/stats/stats.go +++ b/sync/client/stats/stats.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" ) diff --git a/sync/handlers/stats/stats.go b/sync/handlers/stats/stats.go index 6d19b62a86..0d1171b82b 100644 --- a/sync/handlers/stats/stats.go +++ b/sync/handlers/stats/stats.go @@ -6,7 +6,7 @@ package stats import ( "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) // HandlerStats reports prometheus metrics for the state sync handlers diff --git a/sync/statesync/trie_sync_stats.go b/sync/statesync/trie_sync_stats.go index 490dc88cce..5083b590f9 100644 --- a/sync/statesync/trie_sync_stats.go +++ b/sync/statesync/trie_sync_stats.go @@ -10,7 +10,7 @@ import ( utils_math "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) diff --git a/sync/statesync/trie_sync_stats_test.go b/sync/statesync/trie_sync_stats_test.go index 7432617195..e2c1b0c340 100644 --- a/sync/statesync/trie_sync_stats_test.go +++ b/sync/statesync/trie_sync_stats_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/stretchr/testify/require" ) diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 6b2be7dd7f..77a646173d 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -31,8 +31,8 @@ import ( "errors" "sync" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index d6546186cc..ca733d1d7e 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -20,7 +20,7 @@ import ( "encoding/binary" "sync" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" "github.com/crate-crypto/go-ipa/bandersnatch/fr" "github.com/ethereum/go-ethereum/common/lru" "github.com/gballet/go-verkle" diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index fc513c0c50..23dfebbfcc 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -33,9 +33,9 @@ import ( "sync" "time" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ava-labs/subnet-evm/trie/triestate" "github.com/ava-labs/subnet-evm/utils" diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 27dfe7fede..21038ecfc6 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -26,7 +26,7 @@ package pathdb -import "github.com/ava-labs/subnet-evm/metrics" +import "github.com/ava-labs/libevm/metrics" // nolint: unused var ( diff --git a/utils/metered_cache.go b/utils/metered_cache.go index d554dcb13a..ee1807e36f 100644 --- a/utils/metered_cache.go +++ b/utils/metered_cache.go @@ -9,7 +9,7 @@ import ( "time" "github.com/VictoriaMetrics/fastcache" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) // MeteredCache wraps *fastcache.Cache and periodically pulls stats from it. diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go index ac2a364a78..48e69be4b8 100644 --- a/warp/handlers/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/bls" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/warp" @@ -21,6 +22,11 @@ import ( ) func TestMessageSignatureHandler(t *testing.T) { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + database := memdb.New() snowCtx := utils.TestSnowContext() blsSecretKey, err := bls.NewSigner() @@ -125,6 +131,11 @@ func TestMessageSignatureHandler(t *testing.T) { } func TestBlockSignatureHandler(t *testing.T) { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + database := memdb.New() snowCtx := utils.TestSnowContext() blsSecretKey, err := bls.NewSigner() diff --git a/warp/handlers/stats.go b/warp/handlers/stats.go index 1c7a854e97..6a56d54076 100644 --- a/warp/handlers/stats.go +++ b/warp/handlers/stats.go @@ -6,7 +6,7 @@ package handlers import ( "time" - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) type handlerStats struct { diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go index d58e9e6c90..90e9f258c3 100644 --- a/warp/verifier_backend_test.go +++ b/warp/verifier_backend_test.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/validators" stateinterfaces "github.com/ava-labs/subnet-evm/plugin/evm/validators/state/interfaces" "github.com/ava-labs/subnet-evm/utils" @@ -28,6 +29,11 @@ import ( ) func TestAddressedCallSignatures(t *testing.T) { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + database := memdb.New() snowCtx := utils.TestSnowContext() blsSecretKey, err := bls.NewSigner() @@ -142,6 +148,11 @@ func TestAddressedCallSignatures(t *testing.T) { } func TestBlockSignatures(t *testing.T) { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + database := memdb.New() snowCtx := utils.TestSnowContext() blsSecretKey, err := bls.NewSigner() diff --git a/warp/verifier_stats.go b/warp/verifier_stats.go index d1ef62a50f..64102f2ff4 100644 --- a/warp/verifier_stats.go +++ b/warp/verifier_stats.go @@ -4,7 +4,7 @@ package warp import ( - "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/libevm/metrics" ) type verifierStats struct { From 8c8514c8cbb1163ad38211d7cf811594e32163df Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 13 Jan 2025 15:42:19 +0100 Subject: [PATCH 02/13] Migrate subnet-evm specific files back to metrics/prometheus - Bring over refactoring and fixes done in https://github.com/ava-labs/libevm/pull/103 - Bring over test refactoring done in https://github.com/ava-labs/libevm/pull/103 --- go.mod | 4 +- go.sum | 4 +- metrics/prometheus/interfaces.go | 10 ++ metrics/prometheus/prometheus.go | 193 ++++++++++++++++++++++++++ metrics/prometheus/prometheus_test.go | 91 ++++++++++++ plugin/evm/vm.go | 4 +- 6 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 metrics/prometheus/interfaces.go create mode 100644 metrics/prometheus/prometheus.go create mode 100644 metrics/prometheus/prometheus_test.go diff --git a/go.mod b/go.mod index c214e6a1c5..4ffb2e25ea 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 github.com/ava-labs/avalanchego v1.12.1 - github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f + github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 @@ -31,6 +31,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo/v2 v2.13.1 github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.3.0 github.com/spf13/cast v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 @@ -128,7 +129,6 @@ require ( github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index 2e61401a83..469ca3d9b6 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6Yyuj github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer h1:mRB03tLPUvgNko4nP4VwWQdiHeHaLHtdwsnqwxrsGec= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer/go.mod h1:tqRAe+7bGLo2Rq/Ph4iYMSch72ag/Jn0DiDMDz1Xa9E= -github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f h1:KeKggoIyyF+o/GeGofo2+UO93WN7ulqMcVlP2K3iUzM= -github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f/go.mod h1:M8TCw2g1D5GBB7hu7g1F4aot5bRHGSxnBawNVmHE9Z0= +github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 h1:ughW0E2DUNRnvwJYNU8zUSCUzIWdcOwyXSBpy7oauZE= +github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1/go.mod h1:yBctIV/wnxXTF38h95943jvpuk4aj07TrjbpoGor6LQ= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/metrics/prometheus/interfaces.go b/metrics/prometheus/interfaces.go new file mode 100644 index 0000000000..234627d862 --- /dev/null +++ b/metrics/prometheus/interfaces.go @@ -0,0 +1,10 @@ +// (c) 2025 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package prometheus + +type Registry interface { + // Call the given function for each registered metric. + Each(func(string, any)) + // Get the metric by the given name or nil if none is registered. + Get(string) any +} diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go new file mode 100644 index 0000000000..1061921da7 --- /dev/null +++ b/metrics/prometheus/prometheus.go @@ -0,0 +1,193 @@ +// (c) 2025 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package prometheus + +import ( + "errors" + "fmt" + "sort" + "strings" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/libevm/metrics" + + dto "github.com/prometheus/client_model/go" +) + +type Gatherer struct { + registry Registry +} + +var _ prometheus.Gatherer = (*Gatherer)(nil) + +// NewGatherer returns a gatherer using the given registry. +// Note this gatherer implements the [prometheus.Gatherer] interface. +func NewGatherer(registry Registry) *Gatherer { + return &Gatherer{ + registry: registry, + } +} + +func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) { + // Gather and pre-sort the metrics to avoid random listings + var names []string + g.registry.Each(func(name string, i any) { + names = append(names, name) + }) + sort.Strings(names) + + mfs = make([]*dto.MetricFamily, 0, len(names)) + for _, name := range names { + mf, err := metricFamily(g.registry, name) + if errors.Is(err, errMetricSkip) { + continue + } + mfs = append(mfs, mf) + } + + return mfs, nil +} + +var ( + errMetricSkip = errors.New("metric skipped") +) + +func ptrTo[T any](x T) *T { return &x } + +func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err error) { + metric := registry.Get(name) + name = strings.ReplaceAll(name, "/", "_") + + switch m := metric.(type) { + case metrics.Counter: + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_COUNTER.Enum(), + Metric: []*dto.Metric{{ + Counter: &dto.Counter{ + Value: ptrTo(float64(m.Snapshot().Count())), + }, + }}, + }, nil + case metrics.CounterFloat64: + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_COUNTER.Enum(), + Metric: []*dto.Metric{{ + Counter: &dto.Counter{ + Value: ptrTo(m.Snapshot().Count()), + }, + }}, + }, nil + case metrics.Gauge: + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_GAUGE.Enum(), + Metric: []*dto.Metric{{ + Gauge: &dto.Gauge{ + Value: ptrTo(float64(m.Snapshot().Value())), + }, + }}, + }, nil + case metrics.GaugeFloat64: + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_GAUGE.Enum(), + Metric: []*dto.Metric{{ + Gauge: &dto.Gauge{ + Value: ptrTo(m.Snapshot().Value()), + }, + }}, + }, nil + case metrics.Histogram: + snapshot := m.Snapshot() + + quantiles := []float64{.5, .75, .95, .99, .999, .9999} + thresholds := snapshot.Percentiles(quantiles) + dtoQuantiles := make([]*dto.Quantile, len(quantiles)) + for i := range thresholds { + dtoQuantiles[i] = &dto.Quantile{ + Quantile: ptrTo(quantiles[i]), + Value: ptrTo(thresholds[i]), + } + } + + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_SUMMARY.Enum(), + Metric: []*dto.Metric{{ + Summary: &dto.Summary{ + SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec + SampleSum: ptrTo(float64(snapshot.Sum())), + Quantile: dtoQuantiles, + }, + }}, + }, nil + case metrics.Meter: + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_GAUGE.Enum(), + Metric: []*dto.Metric{{ + Gauge: &dto.Gauge{ + Value: ptrTo(float64(m.Snapshot().Count())), + }, + }}, + }, nil + case metrics.Timer: + snapshot := m.Snapshot() + + quantiles := []float64{.5, .75, .95, .99, .999, .9999} + thresholds := snapshot.Percentiles(quantiles) + dtoQuantiles := make([]*dto.Quantile, len(quantiles)) + for i := range thresholds { + dtoQuantiles[i] = &dto.Quantile{ + Quantile: ptrTo(quantiles[i]), + Value: ptrTo(thresholds[i]), + } + } + + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_SUMMARY.Enum(), + Metric: []*dto.Metric{{ + Summary: &dto.Summary{ + SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec + SampleSum: ptrTo(float64(snapshot.Sum())), + Quantile: dtoQuantiles, + }, + }}, + }, nil + case metrics.ResettingTimer: + snapshot := m.Snapshot() + if snapshot.Count() == 0 { + return nil, fmt.Errorf("%w: resetting timer metric count is zero", errMetricSkip) + } + + pvShortPercent := []float64{50, 95, 99} + thresholds := snapshot.Percentiles(pvShortPercent) + dtoQuantiles := make([]*dto.Quantile, len(pvShortPercent)) + for i := range pvShortPercent { + dtoQuantiles[i] = &dto.Quantile{ + Quantile: ptrTo(pvShortPercent[i]), + Value: ptrTo(thresholds[i]), + } + } + + return &dto.MetricFamily{ + Name: &name, + Type: dto.MetricType_SUMMARY.Enum(), + Metric: []*dto.Metric{{ + Summary: &dto.Summary{ + SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec + // TODO: do we need to specify SampleSum here? and if so + // what should that be? + Quantile: dtoQuantiles, + }, + }}, + }, nil + default: + return nil, fmt.Errorf("metric type is not supported: %T", metric) + } +} diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go new file mode 100644 index 0000000000..4eb7eb994a --- /dev/null +++ b/metrics/prometheus/prometheus_test.go @@ -0,0 +1,91 @@ +// (c) 2025 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package prometheus + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/libevm/metrics" +) + +func TestGatherer_Gather(t *testing.T) { + metricsEnabled := metrics.Enabled + if !metricsEnabled { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + } + + registry := metrics.NewRegistry() + register := func(t *testing.T, name string, collector any) { + t.Helper() + err := registry.Register(name, collector) + require.NoError(t, err) + } + + counter := metrics.NewCounter() + counter.Inc(12345) + register(t, "test/counter", counter) + + gauge := metrics.NewGauge() + gauge.Update(23456) + register(t, "test/gauge", gauge) + + gaugeFloat64 := metrics.NewGaugeFloat64() + gaugeFloat64.Update(34567.89) + register(t, "test/gauge_float64", gaugeFloat64) + + sample := metrics.NewUniformSample(1028) + histogram := metrics.NewHistogram(sample) + register(t, "test/histogram", histogram) + + meter := metrics.NewMeter() + t.Cleanup(meter.Stop) + meter.Mark(9999999) + register(t, "test/meter", meter) + + timer := metrics.NewTimer() + t.Cleanup(timer.Stop) + timer.Update(20 * time.Millisecond) + timer.Update(21 * time.Millisecond) + timer.Update(22 * time.Millisecond) + timer.Update(120 * time.Millisecond) + timer.Update(23 * time.Millisecond) + timer.Update(24 * time.Millisecond) + register(t, "test/timer", timer) + + resettingTimer := metrics.NewResettingTimer() + register(t, "test/resetting_timer", resettingTimer) + resettingTimer.Update(time.Second) // must be after register call + + emptyResettingTimer := metrics.NewResettingTimer() + register(t, "test/empty_resetting_timer", emptyResettingTimer) + + emptyResettingTimer.Update(time.Second) // no effect because of snapshot below + register(t, "test/empty_resetting_timer_snapshot", emptyResettingTimer.Snapshot()) + + g := NewGatherer(registry) + + families, err := g.Gather() + require.NoError(t, err) + familyStrings := make([]string, len(families)) + for i := range families { + familyStrings[i] = families[i].String() + } + want := []string{ + `name:"test_counter" type:COUNTER metric: > `, + `name:"test_gauge" type:GAUGE metric: > `, + `name:"test_gauge_float64" type:GAUGE metric: > `, + `name:"test_histogram" type:SUMMARY metric: quantile: quantile: quantile: quantile: quantile: > > `, + `name:"test_meter" type:GAUGE metric: > `, + `name:"test_resetting_timer" type:SUMMARY metric: quantile: quantile: > > `, + `name:"test_timer" type:SUMMARY metric: quantile: quantile: quantile: quantile: quantile: > > `, + } + assert.Equal(t, want, familyStrings) +} diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 7c419edb6d..1edd6d305e 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -24,7 +24,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/libevm/metrics" - libevmprometheus "github.com/ava-labs/libevm/metrics/prometheus" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" @@ -34,6 +33,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/eth" "github.com/ava-labs/subnet-evm/eth/ethconfig" + subnetevmprometheus "github.com/ava-labs/subnet-evm/metrics/prometheus" "github.com/ava-labs/subnet-evm/miner" "github.com/ava-labs/subnet-evm/node" "github.com/ava-labs/subnet-evm/params" @@ -555,7 +555,7 @@ func (vm *VM) initializeMetrics() error { return nil } - gatherer := libevmprometheus.NewGatherer(metrics.DefaultRegistry) + gatherer := subnetevmprometheus.NewGatherer(metrics.DefaultRegistry) if err := vm.ctx.Metrics.Register(ethMetricsPrefix, gatherer); err != nil { return err } From 17be7132ac9fd969f26c8671b7870d80a683c8ed Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 13 Jan 2025 16:08:00 +0100 Subject: [PATCH 03/13] tests: only enable metrics and revert to disable if already disabled --- warp/handlers/signature_request_test.go | 22 ++++++++++++++-------- warp/verifier_backend_test.go | 22 ++++++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go index 48e69be4b8..2a6922257f 100644 --- a/warp/handlers/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -22,10 +22,13 @@ import ( ) func TestMessageSignatureHandler(t *testing.T) { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) + metricsEnabled := metrics.Enabled + if !metricsEnabled { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + } database := memdb.New() snowCtx := utils.TestSnowContext() @@ -131,10 +134,13 @@ func TestMessageSignatureHandler(t *testing.T) { } func TestBlockSignatureHandler(t *testing.T) { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) + metricsEnabled := metrics.Enabled + if !metricsEnabled { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + } database := memdb.New() snowCtx := utils.TestSnowContext() diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go index 90e9f258c3..b50164eb6a 100644 --- a/warp/verifier_backend_test.go +++ b/warp/verifier_backend_test.go @@ -29,10 +29,13 @@ import ( ) func TestAddressedCallSignatures(t *testing.T) { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) + metricsEnabled := metrics.Enabled + if !metricsEnabled { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + } database := memdb.New() snowCtx := utils.TestSnowContext() @@ -148,10 +151,13 @@ func TestAddressedCallSignatures(t *testing.T) { } func TestBlockSignatures(t *testing.T) { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) + metricsEnabled := metrics.Enabled + if !metricsEnabled { + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) + } database := memdb.New() snowCtx := utils.TestSnowContext() From 737c1c06771b3398f73ce15a036c823fe623c777 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 13 Jan 2025 16:18:34 +0100 Subject: [PATCH 04/13] Enable metrics by default with init block --- metrics/prometheus/prometheus.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 1061921da7..debb77a422 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -16,6 +16,10 @@ import ( dto "github.com/prometheus/client_model/go" ) +func init() { + metrics.Enabled = true +} + type Gatherer struct { registry Registry } From 38fd35fba4769d4cd8688939095c7f329c530ced Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 13 Jan 2025 17:16:19 +0100 Subject: [PATCH 05/13] Change copyright notice from 2025 to 2021-2025 Signed-off-by: Quentin McGaw --- metrics/prometheus/prometheus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index debb77a422..d1edcf95ea 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -1,4 +1,4 @@ -// (c) 2025 Ava Labs, Inc. All rights reserved. +// (c) 2021-2025 Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prometheus From 95750b3d88d7521118e500346eeb1d19ca20f07e Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 13 Jan 2025 17:16:25 +0100 Subject: [PATCH 06/13] Change copyright notice from 2025 to 2021-2025 Signed-off-by: Quentin McGaw --- metrics/prometheus/prometheus_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 4eb7eb994a..2feba6f079 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -1,4 +1,4 @@ -// (c) 2025 Ava Labs, Inc. All rights reserved. +// (c) 2021-2025 Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prometheus From c3b43141df2f84d42dbe7fe63bd875670d564605 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 10:54:05 +0100 Subject: [PATCH 07/13] Use go-ethereum instead of libevm --- core/blockchain.go | 2 +- core/main_test.go | 2 +- core/rawdb/schema.go | 2 +- core/state/metrics.go | 2 +- core/state/snapshot/snapshot.go | 2 +- core/state/state_object.go | 2 +- core/state/statedb.go | 2 +- core/state/trie_prefetcher.go | 2 +- core/state/trie_prefetcher_extra_test.go | 2 +- core/txpool/blobpool/blobpool.go | 2 +- core/txpool/blobpool/metrics.go | 2 +- core/txpool/legacypool/legacypool.go | 2 +- core/txpool/txpool.go | 2 +- go.mod | 1 - go.sum | 2 -- metrics/prometheus/prometheus.go | 2 +- metrics/prometheus/prometheus_test.go | 2 +- peer/peer_tracker.go | 2 +- peer/stats/stats.go | 2 +- plugin/evm/gossip_stats.go | 2 +- plugin/evm/network_handler.go | 2 +- plugin/evm/syncervm_test.go | 2 +- plugin/evm/vm.go | 2 +- plugin/evm/vm_test.go | 2 +- plugin/evm/vm_upgrade_bytes_test.go | 2 +- rpc/handler.go | 2 +- rpc/metrics.go | 2 +- scripts/geth-allowed-packages.txt | 2 ++ sync/client/stats/stats.go | 2 +- sync/handlers/stats/stats.go | 2 +- sync/statesync/trie_sync_stats.go | 2 +- sync/statesync/trie_sync_stats_test.go | 2 +- trie/stacktrie.go | 2 +- trie/utils/verkle.go | 2 +- triedb/hashdb/database.go | 2 +- triedb/pathdb/metrics.go | 2 +- utils/metered_cache.go | 2 +- warp/handlers/signature_request_test.go | 2 +- warp/handlers/stats.go | 2 +- warp/verifier_backend_test.go | 2 +- warp/verifier_stats.go | 2 +- 41 files changed, 40 insertions(+), 41 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 4e40400f6a..56e71e168a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -39,7 +39,6 @@ import ( "sync/atomic" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" @@ -59,6 +58,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/core/main_test.go b/core/main_test.go index 57bd792327..ec815e558a 100644 --- a/core/main_test.go +++ b/core/main_test.go @@ -15,7 +15,7 @@ func TestMain(m *testing.M) { opts := []goleak.Option{ // No good way to shut down these goroutines: goleak.IgnoreTopFunction("github.com/ava-labs/subnet-evm/core/state/snapshot.(*diskLayer).generate"), - goleak.IgnoreTopFunction("github.com/ava-labs/libevm/metrics.(*meterArbiter).tick"), + goleak.IgnoreTopFunction("github.com/ethereum/go-ethereum/metrics.(*meterArbiter).tick"), goleak.IgnoreTopFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain"), } goleak.VerifyTestMain(m, opts...) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index f676b74aba..6dd419bac0 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -32,9 +32,9 @@ import ( "encoding/binary" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/metrics" ) // The fields below define the low level database schema prefixing. diff --git a/core/state/metrics.go b/core/state/metrics.go index 4fbc86c2f5..096bfe504f 100644 --- a/core/state/metrics.go +++ b/core/state/metrics.go @@ -26,7 +26,7 @@ package state -import "github.com/ava-labs/libevm/metrics" +import "github.com/ethereum/go-ethereum/metrics" var ( accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil) diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 6e8a0a0f3f..8ed8b21570 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -34,13 +34,13 @@ import ( "sync" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) const ( diff --git a/core/state/state_object.go b/core/state/state_object.go index 2eb68d0889..979dc67cc3 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -32,11 +32,11 @@ import ( "io" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" ) diff --git a/core/state/statedb.go b/core/state/statedb.go index 17dd82ce0e..382cfae556 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -32,7 +32,6 @@ import ( "sort" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" @@ -45,6 +44,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/holiman/uint256" ) diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index da30e9aa70..e3dce91b63 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -19,10 +19,10 @@ package state import ( "sync" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/libevm/options" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/core/state/trie_prefetcher_extra_test.go b/core/state/trie_prefetcher_extra_test.go index e5632bac71..1c78d047eb 100644 --- a/core/state/trie_prefetcher_extra_test.go +++ b/core/state/trie_prefetcher_extra_test.go @@ -13,7 +13,6 @@ import ( "testing" "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" @@ -21,6 +20,7 @@ import ( "github.com/ava-labs/subnet-evm/triedb/hashdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" ) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index aa88d7758e..e6dfda3c12 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -39,7 +39,6 @@ import ( "sync" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core" @@ -50,6 +49,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/billy" "github.com/holiman/uint256" diff --git a/core/txpool/blobpool/metrics.go b/core/txpool/blobpool/metrics.go index ccd4dbb574..0192e897aa 100644 --- a/core/txpool/blobpool/metrics.go +++ b/core/txpool/blobpool/metrics.go @@ -26,7 +26,7 @@ package blobpool -import "github.com/ava-labs/libevm/metrics" +import "github.com/ethereum/go-ethereum/metrics" var ( // datacapGauge tracks the user's configured capacity for the blob pool. It diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 466dfa081a..29e82064ac 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -36,7 +36,6 @@ import ( "sync/atomic" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/core" @@ -50,6 +49,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/holiman/uint256" ) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 01128e832c..aa5b59d253 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -33,12 +33,12 @@ import ( "sync" "sync/atomic" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/go.mod b/go.mod index 4ffb2e25ea..dce82a6a82 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 github.com/ava-labs/avalanchego v1.12.1 - github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 469ca3d9b6..e989b5c81d 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,6 @@ github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6Yyuj github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer h1:mRB03tLPUvgNko4nP4VwWQdiHeHaLHtdwsnqwxrsGec= github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer/go.mod h1:tqRAe+7bGLo2Rq/Ph4iYMSch72ag/Jn0DiDMDz1Xa9E= -github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 h1:ughW0E2DUNRnvwJYNU8zUSCUzIWdcOwyXSBpy7oauZE= -github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1/go.mod h1:yBctIV/wnxXTF38h95943jvpuk4aj07TrjbpoGor6LQ= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index d1edcf95ea..772260a095 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -11,7 +11,7 @@ import ( "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" dto "github.com/prometheus/client_model/go" ) diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 2feba6f079..f87975da36 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) func TestGatherer_Gather(t *testing.T) { diff --git a/peer/peer_tracker.go b/peer/peer_tracker.go index 30f73d854d..ca11a4faf6 100644 --- a/peer/peer_tracker.go +++ b/peer/peer_tracker.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/log" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) const ( diff --git a/peer/stats/stats.go b/peer/stats/stats.go index 5a3c2918f4..a23772540c 100644 --- a/peer/stats/stats.go +++ b/peer/stats/stats.go @@ -6,7 +6,7 @@ package stats import ( "time" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) // RequestHandlerStats provides the interface for metrics for app requests. diff --git a/plugin/evm/gossip_stats.go b/plugin/evm/gossip_stats.go index d4e2e60457..b756654ba3 100644 --- a/plugin/evm/gossip_stats.go +++ b/plugin/evm/gossip_stats.go @@ -3,7 +3,7 @@ package evm -import "github.com/ava-labs/libevm/metrics" +import "github.com/ethereum/go-ethereum/metrics" var _ GossipStats = &gossipStats{} diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go index 2d6acb3f47..c239590050 100644 --- a/plugin/evm/network_handler.go +++ b/plugin/evm/network_handler.go @@ -8,7 +8,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers" syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" @@ -16,6 +15,7 @@ import ( "github.com/ava-labs/subnet-evm/warp" warpHandlers "github.com/ava-labs/subnet-evm/warp/handlers" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/metrics" ) var _ message.RequestHandler = &networkHandler{} diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 3a37e9da21..a4cd9a5cee 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -24,7 +24,6 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" @@ -41,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" ) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 1edd6d305e..da09af634e 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -23,7 +23,6 @@ import ( "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" @@ -43,6 +42,7 @@ import ( "github.com/ava-labs/subnet-evm/plugin/evm/validators/interfaces" "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ethereum/go-ethereum/metrics" warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/subnet-evm/rpc" diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index e08a360f9e..28f199f15d 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -39,7 +39,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/libevm/metrics" accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" @@ -58,6 +57,7 @@ import ( "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/vmerrs" + "github.com/ethereum/go-ethereum/metrics" avagoconstants "github.com/ava-labs/avalanchego/utils/constants" ) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 044882db8a..1a939284d8 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -17,7 +17,6 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" @@ -28,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/metrics" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/rpc/handler.go b/rpc/handler.go index cabbd10cde..82d1235ab1 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -38,8 +38,8 @@ import ( "sync" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "golang.org/x/time/rate" ) diff --git a/rpc/metrics.go b/rpc/metrics.go index a4469545d7..bafdcf591c 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -30,7 +30,7 @@ import ( "fmt" "time" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/scripts/geth-allowed-packages.txt b/scripts/geth-allowed-packages.txt index c295d5d044..4b84961061 100644 --- a/scripts/geth-allowed-packages.txt +++ b/scripts/geth-allowed-packages.txt @@ -17,4 +17,6 @@ "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" +"github.com/ethereum/go-ethereum/metrics" +"github.com/ethereum/go-ethereum/metrics.(*meterArbiter).tick" "github.com/ethereum/go-ethereum/rlp" diff --git a/sync/client/stats/stats.go b/sync/client/stats/stats.go index 6567b00ab2..fbd178e582 100644 --- a/sync/client/stats/stats.go +++ b/sync/client/stats/stats.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/sync/handlers/stats/stats.go b/sync/handlers/stats/stats.go index 0d1171b82b..d6e3271911 100644 --- a/sync/handlers/stats/stats.go +++ b/sync/handlers/stats/stats.go @@ -6,7 +6,7 @@ package stats import ( "time" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) // HandlerStats reports prometheus metrics for the state sync handlers diff --git a/sync/statesync/trie_sync_stats.go b/sync/statesync/trie_sync_stats.go index 5083b590f9..4cc6a0c5b2 100644 --- a/sync/statesync/trie_sync_stats.go +++ b/sync/statesync/trie_sync_stats.go @@ -10,9 +10,9 @@ import ( utils_math "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/libevm/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) const ( diff --git a/sync/statesync/trie_sync_stats_test.go b/sync/statesync/trie_sync_stats_test.go index e2c1b0c340..1d01cd29ec 100644 --- a/sync/statesync/trie_sync_stats_test.go +++ b/sync/statesync/trie_sync_stats_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" ) diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 77a646173d..b7580d8686 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -31,10 +31,10 @@ import ( "errors" "sync" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) var ( diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index ca733d1d7e..ce059edc64 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -20,9 +20,9 @@ import ( "encoding/binary" "sync" - "github.com/ava-labs/libevm/metrics" "github.com/crate-crypto/go-ipa/bandersnatch/fr" "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/metrics" "github.com/gballet/go-verkle" "github.com/holiman/uint256" ) diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index 23dfebbfcc..5095be4adc 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -33,7 +33,6 @@ import ( "sync" "time" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/trienode" @@ -42,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" ) diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 21038ecfc6..e2df1ca156 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -26,7 +26,7 @@ package pathdb -import "github.com/ava-labs/libevm/metrics" +import "github.com/ethereum/go-ethereum/metrics" // nolint: unused var ( diff --git a/utils/metered_cache.go b/utils/metered_cache.go index ee1807e36f..dfaeac7cca 100644 --- a/utils/metered_cache.go +++ b/utils/metered_cache.go @@ -9,7 +9,7 @@ import ( "time" "github.com/VictoriaMetrics/fastcache" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) // MeteredCache wraps *fastcache.Cache and periodically pulls stats from it. diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go index 2a6922257f..5ab2786296 100644 --- a/warp/handlers/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -13,11 +13,11 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/bls" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/warp" "github.com/ava-labs/subnet-evm/warp/warptest" + "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" ) diff --git a/warp/handlers/stats.go b/warp/handlers/stats.go index 6a56d54076..d81bce0e02 100644 --- a/warp/handlers/stats.go +++ b/warp/handlers/stats.go @@ -6,7 +6,7 @@ package handlers import ( "time" - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) type handlerStats struct { diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go index b50164eb6a..9beee9ec52 100644 --- a/warp/verifier_backend_test.go +++ b/warp/verifier_backend_test.go @@ -18,12 +18,12 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/libevm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/validators" stateinterfaces "github.com/ava-labs/subnet-evm/plugin/evm/validators/state/interfaces" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/warp/messages" "github.com/ava-labs/subnet-evm/warp/warptest" + "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) diff --git a/warp/verifier_stats.go b/warp/verifier_stats.go index 64102f2ff4..a68f2673e0 100644 --- a/warp/verifier_stats.go +++ b/warp/verifier_stats.go @@ -4,7 +4,7 @@ package warp import ( - "github.com/ava-labs/libevm/metrics" + "github.com/ethereum/go-ethereum/metrics" ) type verifierStats struct { From 09326b6dc7d8474a5df82453560787ccfbf57414 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 11:08:13 +0100 Subject: [PATCH 08/13] Enable geth metrics in initializeMetrics --- metrics/prometheus/prometheus.go | 4 ---- plugin/evm/syncervm_test.go | 12 +++++------- plugin/evm/vm.go | 6 +----- plugin/evm/vm_test.go | 11 +++++++---- plugin/evm/vm_upgrade_bytes_test.go | 12 +++++------- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 772260a095..226fd78e7f 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -16,10 +16,6 @@ import ( dto "github.com/prometheus/client_model/go" ) -func init() { - metrics.Enabled = true -} - type Gatherer struct { registry Registry } diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index a4cd9a5cee..3a7d2aab94 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -24,6 +24,7 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" @@ -40,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" ) @@ -83,11 +83,6 @@ func TestStateSyncFromScratchExceedParent(t *testing.T) { func TestStateSyncToggleEnabledToDisabled(t *testing.T) { rand.Seed(1) - // Hack: registering metrics uses global variables, so we need to disable metrics here so that we can initialize the VM twice. - metrics.Enabled = false - defer func() { - metrics.Enabled = true - }() var lock sync.Mutex reqCount := 0 @@ -137,7 +132,8 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { go vmSetup.serverVM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request) return nil } - // Disable metrics to prevent duplicate registerer + // Reset metrics to allow re-initialization + vmSetup.syncerVM.ctx.Metrics = metrics.NewPrefixGatherer() stateSyncDisabledConfigJSON := `{"state-sync-enabled":false}` if err := syncDisabledVM.Initialize( context.Background(), @@ -202,6 +198,8 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { `{"state-sync-enabled":true, "state-sync-min-blocks":%d}`, test.stateSyncMinBlocks, ) + // Reset metrics to allow re-initialization + vmSetup.syncerVM.ctx.Metrics = metrics.NewPrefixGatherer() if err := syncReEnabledVM.Initialize( context.Background(), vmSetup.syncerVM.ctx, diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index da09af634e..3abfd3f06b 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -549,12 +549,8 @@ func (vm *VM) Initialize( } func (vm *VM) initializeMetrics() error { + metrics.Enabled = true vm.sdkMetrics = prometheus.NewRegistry() - // If metrics are enabled, register the default metrics registry - if !metrics.Enabled { - return nil - } - gatherer := subnetevmprometheus.NewGatherer(metrics.DefaultRegistry) if err := vm.ctx.Metrics.Register(ethMetricsPrefix, gatherer); err != nil { return err diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 28f199f15d..46fd658dc3 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/avalanchego/api/metrics" accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus/dummy" @@ -57,7 +58,6 @@ import ( "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/vmerrs" - "github.com/ethereum/go-ethereum/metrics" avagoconstants "github.com/ava-labs/avalanchego/utils/constants" ) @@ -2948,9 +2948,6 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { // disabling dynamic fees and causes a panic since some code assumes that this is enabled. // TODO update this test when there is a future network upgrade that can be skipped in the config. t.Skip("no skippable upgrades") - // Hack: registering metrics uses global variables, so we need to disable metrics here so that we can initialize the VM twice. - metrics.Enabled = false - defer func() { metrics.Enabled = true }() issuer, vm, dbManager, appSender := GenesisVM(t, true, genesisJSONPreSubnetEVM, `{"pruning-enabled":true}`, "") @@ -2995,10 +2992,16 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { genesisWithUpgradeBytes, err := json.Marshal(genesisWithUpgrade) require.NoError(t, err) + // Reset metrics to allow re-initialization + vm.ctx.Metrics = metrics.NewPrefixGatherer() + // this will not be allowed err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, []byte{}, issuer, []*commonEng.Fx{}, appSender) require.ErrorContains(t, err, "mismatching SubnetEVM fork block timestamp in database") + // Reset metrics to allow re-initialization + vm.ctx.Metrics = metrics.NewPrefixGatherer() + // try again with skip-upgrade-check config := []byte(`{"skip-upgrade-check": true}`) err = reinitVM.Initialize(context.Background(), vm.ctx, dbManager, genesisWithUpgradeBytes, []byte{}, config, issuer, []*commonEng.Fx{}, appSender) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 1a939284d8..85d93023f3 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/snow" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" @@ -27,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/metrics" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -94,12 +94,10 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { } // restart the vm - // Hack: registering metrics uses global variables, so we need to disable metrics here so that we - // can initialize the VM twice. - metrics.Enabled = false - defer func() { - metrics.Enabled = true - }() + + // Reset metrics to allow re-initialization + vm.ctx.Metrics = metrics.NewPrefixGatherer() + if err := vm.Initialize( context.Background(), vm.ctx, dbManager, []byte(genesisJSONSubnetEVM), upgradeBytesJSON, []byte{}, issuer, []*commonEng.Fx{}, appSender, ); err != nil { From ba7e9fd54b0aa884e6b29cc13e50d11ff0e9ff15 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 11:09:02 +0100 Subject: [PATCH 09/13] `testutils.WithMetrics` function in plugin/evm --- metrics/prometheus/prometheus_test.go | 9 ++------- plugin/evm/testutils/metrics.go | 20 ++++++++++++++++++++ warp/handlers/signature_request_test.go | 18 +++--------------- warp/verifier_backend_test.go | 18 +++--------------- 4 files changed, 28 insertions(+), 37 deletions(-) create mode 100644 plugin/evm/testutils/metrics.go diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index f87975da36..379f93deb9 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/ava-labs/subnet-evm/plugin/evm/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,13 +15,7 @@ import ( ) func TestGatherer_Gather(t *testing.T) { - metricsEnabled := metrics.Enabled - if !metricsEnabled { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) - } + testutils.WithMetrics(t) registry := metrics.NewRegistry() register := func(t *testing.T, name string, collector any) { diff --git a/plugin/evm/testutils/metrics.go b/plugin/evm/testutils/metrics.go new file mode 100644 index 0000000000..aa2ee19037 --- /dev/null +++ b/plugin/evm/testutils/metrics.go @@ -0,0 +1,20 @@ +package testutils + +import ( + "testing" + + "github.com/ethereum/go-ethereum/metrics" +) + +// WithMetrics enables go-ethereum metrics globally for the test. +// If the [metrics.Enabled] is already true, nothing is done. +// Otherwise, it is set to true and is reverted to false when the test finishes. +func WithMetrics(t *testing.T) { + if metrics.Enabled { + return + } + metrics.Enabled = true + t.Cleanup(func() { + metrics.Enabled = false + }) +} diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go index 5ab2786296..79a27f1fff 100644 --- a/warp/handlers/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -14,21 +14,15 @@ import ( avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ava-labs/subnet-evm/plugin/evm/testutils" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/warp" "github.com/ava-labs/subnet-evm/warp/warptest" - "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" ) func TestMessageSignatureHandler(t *testing.T) { - metricsEnabled := metrics.Enabled - if !metricsEnabled { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) - } + testutils.WithMetrics(t) database := memdb.New() snowCtx := utils.TestSnowContext() @@ -134,13 +128,7 @@ func TestMessageSignatureHandler(t *testing.T) { } func TestBlockSignatureHandler(t *testing.T) { - metricsEnabled := metrics.Enabled - if !metricsEnabled { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) - } + testutils.WithMetrics(t) database := memdb.New() snowCtx := utils.TestSnowContext() diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go index 9beee9ec52..6697e1f148 100644 --- a/warp/verifier_backend_test.go +++ b/warp/verifier_backend_test.go @@ -18,24 +18,18 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/subnet-evm/plugin/evm/testutils" "github.com/ava-labs/subnet-evm/plugin/evm/validators" stateinterfaces "github.com/ava-labs/subnet-evm/plugin/evm/validators/state/interfaces" "github.com/ava-labs/subnet-evm/utils" "github.com/ava-labs/subnet-evm/warp/messages" "github.com/ava-labs/subnet-evm/warp/warptest" - "github.com/ethereum/go-ethereum/metrics" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) func TestAddressedCallSignatures(t *testing.T) { - metricsEnabled := metrics.Enabled - if !metricsEnabled { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) - } + testutils.WithMetrics(t) database := memdb.New() snowCtx := utils.TestSnowContext() @@ -151,13 +145,7 @@ func TestAddressedCallSignatures(t *testing.T) { } func TestBlockSignatures(t *testing.T) { - metricsEnabled := metrics.Enabled - if !metricsEnabled { - metrics.Enabled = true - t.Cleanup(func() { - metrics.Enabled = false - }) - } + testutils.WithMetrics(t) database := memdb.New() snowCtx := utils.TestSnowContext() From 12cf26eb205db090f264e7ac8c57ec2a8478c8eb Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 11:09:53 +0100 Subject: [PATCH 10/13] metrics/prometheus: add test cases for full coverage --- metrics/prometheus/prometheus.go | 3 +++ metrics/prometheus/prometheus_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 226fd78e7f..019f2d8580 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -41,8 +41,11 @@ func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) { mfs = make([]*dto.MetricFamily, 0, len(names)) for _, name := range names { mf, err := metricFamily(g.registry, name) + if err != nil { if errors.Is(err, errMetricSkip) { continue + } + return nil, err } mfs = append(mfs, mf) } diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 379f93deb9..987eab67c3 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -28,6 +28,10 @@ func TestGatherer_Gather(t *testing.T) { counter.Inc(12345) register(t, "test/counter", counter) + counterFloat64 := metrics.NewCounterFloat64() + counterFloat64.Inc(1.1) + register(t, "test/counter_float64", counterFloat64) + gauge := metrics.NewGauge() gauge.Update(23456) register(t, "test/gauge", gauge) @@ -75,6 +79,7 @@ func TestGatherer_Gather(t *testing.T) { } want := []string{ `name:"test_counter" type:COUNTER metric: > `, + `name:"test_counter_float64" type:COUNTER metric: > `, `name:"test_gauge" type:GAUGE metric: > `, `name:"test_gauge_float64" type:GAUGE metric: > `, `name:"test_histogram" type:SUMMARY metric: quantile: quantile: quantile: quantile: quantile: > > `, @@ -83,4 +88,9 @@ func TestGatherer_Gather(t *testing.T) { `name:"test_timer" type:SUMMARY metric: quantile: quantile: quantile: quantile: quantile: > > `, } assert.Equal(t, want, familyStrings) + + register(t, "unsupported", metrics.NewGaugeInfo()) + families, err = g.Gather() + assert.EqualError(t, err, "metric type is not supported: *metrics.StandardGaugeInfo") + assert.Empty(t, families) } From d1c6f4cbee65b259e9b13924b6e050d14ddb4e90 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 11:11:25 +0100 Subject: [PATCH 11/13] metrics/prometheus: add metric name to error wrappings --- metrics/prometheus/prometheus.go | 4 ++-- metrics/prometheus/prometheus_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 019f2d8580..d8e9d86095 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -165,7 +165,7 @@ func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err err case metrics.ResettingTimer: snapshot := m.Snapshot() if snapshot.Count() == 0 { - return nil, fmt.Errorf("%w: resetting timer metric count is zero", errMetricSkip) + return nil, fmt.Errorf("%w: %q resetting timer metric count is zero", errMetricSkip, name) } pvShortPercent := []float64{50, 95, 99} @@ -191,6 +191,6 @@ func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err err }}, }, nil default: - return nil, fmt.Errorf("metric type is not supported: %T", metric) + return nil, fmt.Errorf("metric %q: type is not supported: %T", name, metric) } } diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 987eab67c3..9c59142208 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -91,6 +91,6 @@ func TestGatherer_Gather(t *testing.T) { register(t, "unsupported", metrics.NewGaugeInfo()) families, err = g.Gather() - assert.EqualError(t, err, "metric type is not supported: *metrics.StandardGaugeInfo") + assert.EqualError(t, err, "metric \"unsupported\": type is not supported: *metrics.StandardGaugeInfo") assert.Empty(t, families) } From d6b3024c48d39567e92df60fb1091943c025637f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 11:12:22 +0100 Subject: [PATCH 12/13] =?UTF-8?q?Minor=20changes=20-=20add=20exported=20co?= =?UTF-8?q?mments=20-=20rename=20`g`=20to=20`gatherer`=20in=20test=20-=20F?= =?UTF-8?q?ormat=20copyright=20as=20it=20as=20=E2=84=A2=EF=B8=8F=20-=20Rem?= =?UTF-8?q?ove=20TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metrics/prometheus/interfaces.go | 2 ++ metrics/prometheus/prometheus.go | 17 +++++++++-------- metrics/prometheus/prometheus_test.go | 8 ++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/metrics/prometheus/interfaces.go b/metrics/prometheus/interfaces.go index 234627d862..b9484722dd 100644 --- a/metrics/prometheus/interfaces.go +++ b/metrics/prometheus/interfaces.go @@ -2,6 +2,8 @@ // See the file LICENSE for licensing terms. package prometheus +// Registry is a narrower interface of [prometheus.Registry] containing +// only the required functions for the [Gatherer]. type Registry interface { // Call the given function for each registered metric. Each(func(string, any)) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index d8e9d86095..07c14004ff 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -1,4 +1,4 @@ -// (c) 2021-2025 Ava Labs, Inc. All rights reserved. +// (c) 2021-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prometheus @@ -16,20 +16,23 @@ import ( dto "github.com/prometheus/client_model/go" ) +// Gatherer implements [prometheus.Gatherer] interface by +// gathering all metrics from the given Prometheus registry. type Gatherer struct { registry Registry } var _ prometheus.Gatherer = (*Gatherer)(nil) -// NewGatherer returns a gatherer using the given registry. -// Note this gatherer implements the [prometheus.Gatherer] interface. +// NewGatherer returns a [Gatherer] using the given registry. func NewGatherer(registry Registry) *Gatherer { return &Gatherer{ registry: registry, } } +// Gather gathers metrics from the registry and converts them to +// a slice of metric families. func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) { // Gather and pre-sort the metrics to avoid random listings var names []string @@ -42,8 +45,8 @@ func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) { for _, name := range names { mf, err := metricFamily(g.registry, name) if err != nil { - if errors.Is(err, errMetricSkip) { - continue + if errors.Is(err, errMetricSkip) { + continue } return nil, err } @@ -184,9 +187,7 @@ func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err err Metric: []*dto.Metric{{ Summary: &dto.Summary{ SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec - // TODO: do we need to specify SampleSum here? and if so - // what should that be? - Quantile: dtoQuantiles, + Quantile: dtoQuantiles, }, }}, }, nil diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 9c59142208..e58f0790c5 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -1,4 +1,4 @@ -// (c) 2021-2025 Ava Labs, Inc. All rights reserved. +// (c) 2021-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package prometheus @@ -69,9 +69,9 @@ func TestGatherer_Gather(t *testing.T) { emptyResettingTimer.Update(time.Second) // no effect because of snapshot below register(t, "test/empty_resetting_timer_snapshot", emptyResettingTimer.Snapshot()) - g := NewGatherer(registry) + gatherer := NewGatherer(registry) - families, err := g.Gather() + families, err := gatherer.Gather() require.NoError(t, err) familyStrings := make([]string, len(families)) for i := range families { @@ -90,7 +90,7 @@ func TestGatherer_Gather(t *testing.T) { assert.Equal(t, want, familyStrings) register(t, "unsupported", metrics.NewGaugeInfo()) - families, err = g.Gather() + families, err = gatherer.Gather() assert.EqualError(t, err, "metric \"unsupported\": type is not supported: *metrics.StandardGaugeInfo") assert.Empty(t, families) } From 15866c878a1db26fcc0ea8fa5891b3d4727a3b07 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 15 Jan 2025 12:05:40 +0100 Subject: [PATCH 13/13] Enforce testutils package is only imported by test files outside plugin/evm --- plugin/evm/testutils/metrics.go | 1 + plugin/evm/testutils/testutils.go | 27 +++++++++ plugin/evm/testutils/testutils_test.go | 81 ++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 plugin/evm/testutils/testutils.go create mode 100644 plugin/evm/testutils/testutils_test.go diff --git a/plugin/evm/testutils/metrics.go b/plugin/evm/testutils/metrics.go index aa2ee19037..53be77de60 100644 --- a/plugin/evm/testutils/metrics.go +++ b/plugin/evm/testutils/metrics.go @@ -10,6 +10,7 @@ import ( // If the [metrics.Enabled] is already true, nothing is done. // Otherwise, it is set to true and is reverted to false when the test finishes. func WithMetrics(t *testing.T) { + panicIfCallsFromNonTest() if metrics.Enabled { return } diff --git a/plugin/evm/testutils/testutils.go b/plugin/evm/testutils/testutils.go new file mode 100644 index 0000000000..662aa7d7e4 --- /dev/null +++ b/plugin/evm/testutils/testutils.go @@ -0,0 +1,27 @@ +// Package testutils contains test utilities ONLY to be used outside plugin/evm. +// The aim is to reduce changes in geth tests by using the utilities defined here. +// This package MUST NOT be imported by non-test packages. + +package testutils + +import ( + "runtime" + "strings" +) + +// panicIfCallsFromNonTest should be added at the top of every function defined in this package +// to enforce this package to be used only by tests. +func panicIfCallsFromNonTest() { + pc := make([]uintptr, 64) + runtime.Callers(0, pc) + frames := runtime.CallersFrames(pc) + for { + f, more := frames.Next() + if strings.HasPrefix(f.File, "/testing/") || strings.HasSuffix(f.File, "_test.go") { + return + } + if !more { + panic("no test file in call stack") + } + } +} diff --git a/plugin/evm/testutils/testutils_test.go b/plugin/evm/testutils/testutils_test.go new file mode 100644 index 0000000000..1f2fe1f229 --- /dev/null +++ b/plugin/evm/testutils/testutils_test.go @@ -0,0 +1,81 @@ +package testutils + +import ( + "fmt" + "go/parser" + "go/token" + "io/fs" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestMustNotImport fails if a package in plugin/evm imports the testutils package. +func TestMustNotImport(t *testing.T) { + var evmPackagePaths []string + filepath.WalkDir("../", func(path string, d fs.DirEntry, err error) error { + if !d.IsDir() { + return nil + } else if path == "../testutils" { + return nil + } + evmPackagePaths = append(evmPackagePaths, path) + return nil + }) + + for _, packagePath := range evmPackagePaths { + packageName := "github.com/ava-labs/subnet-evm/plugin/evm/" + strings.TrimPrefix(packagePath, "../") + imports, err := getPackageImports(packagePath) + require.NoError(t, err) + _, ok := imports["github.com/ava-labs/subnet-evm/plugin/evm/testutils"] + assert.Falsef(t, ok, "package %s imports testutils: testutils should only be used outside plugin/evm.", packageName) + } +} + +func getPackageImports(packagePath string) (imports map[string]struct{}, err error) { + imports = make(map[string]struct{}) + + err = filepath.Walk(packagePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } else if path != packagePath && info.IsDir() { + return fs.SkipDir + } else if !strings.HasSuffix(info.Name(), ".go") { + return nil + } + err = parseImportsFromFile(path, imports) + if err != nil { + return fmt.Errorf("failed to parse imports: %s", err) + } + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to walk through package files: %v", err) + } + + return imports, nil +} + +func parseImportsFromFile(filePath string, imports map[string]struct{}) error { + file, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("failed to open file %s: %v", filePath, err) + } + defer file.Close() + + node, err := parser.ParseFile(token.NewFileSet(), filePath, file, parser.ImportsOnly) + if err != nil { + return fmt.Errorf("failed to parse file %s: %v", filePath, err) + } + + for _, nodeImport := range node.Imports { + imports[strings.Trim(nodeImport.Path.Value, `"`)] = struct{}{} + } + + return nil +}