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 e2d7945008..0000000000 --- a/metrics/README.md +++ /dev/null @@ -1,166 +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) -``` - -Periodically emit every metric into InfluxDB: - -**NOTE:** this has been pulled out of the library due to constant fluctuations -in the InfluxDB API. In fact, all client libraries are on their way out. see -issues [#121](https://github.com/rcrowley/go-metrics/issues/121) and -[#124](https://github.com/rcrowley/go-metrics/issues/124) for progress and details. - -```go -import "github.com/vrischmann/go-metrics-influxdb" - -go influxdb.InfluxDB(metrics.DefaultRegistry, - 10e9, - "127.0.0.1:8086", - "database-name", - "username", - "password" -) -``` - -Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato): - -**Note**: the client included with this repository under the `librato` package -has been deprecated and moved to the repository linked above. - -```go -import "github.com/mihasya/go-metrics-librato" - -go librato.Librato(metrics.DefaultRegistry, - 10e9, // interval - "example@example.com", // account owner email address - "token", // Librato API token - "hostname", // source - []float64{0.95}, // percentiles to send - time.Millisecond, // time unit -) -``` - -Periodically emit every metric to StatHat: - -```go -import "github.com/rcrowley/go-metrics/stathat" - -go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com") -``` - -Maintain all metrics along with expvars at `/debug/metrics`: - -This uses the same mechanism as [the official expvar](https://golang.org/pkg/expvar/) -but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars -as well as all your go-metrics. - - -```go -import "github.com/rcrowley/go-metrics/exp" - -exp.Exp(metrics.DefaultRegistry) -``` - -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: - -* Librato - https://github.com/mihasya/go-metrics-librato -* Graphite - https://github.com/cyberdelia/go-metrics-graphite -* InfluxDB - https://github.com/vrischmann/go-metrics-influxdb -* Ganglia - https://github.com/appscode/metlia -* Prometheus - https://github.com/deathowl/go-metrics-prometheus -* DataDog - https://github.com/syntaqx/go-metrics-datadog -* SignalFX - https://github.com/pascallouisperez/go-metrics-signalfx diff --git a/metrics/config.go b/metrics/config.go deleted file mode 100644 index 2eb09fb48a..0000000000 --- a/metrics/config.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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"` - EnableInfluxDB bool `toml:",omitempty"` - InfluxDBEndpoint string `toml:",omitempty"` - InfluxDBDatabase string `toml:",omitempty"` - InfluxDBUsername string `toml:",omitempty"` - InfluxDBPassword string `toml:",omitempty"` - InfluxDBTags string `toml:",omitempty"` - - EnableInfluxDBV2 bool `toml:",omitempty"` - InfluxDBToken string `toml:",omitempty"` - InfluxDBBucket string `toml:",omitempty"` - InfluxDBOrganization string `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, - EnableInfluxDB: false, - InfluxDBEndpoint: "http://localhost:8086", - InfluxDBDatabase: "geth", - InfluxDBUsername: "test", - InfluxDBPassword: "test", - InfluxDBTags: "host=localhost", - - // influxdbv2-specific flags - EnableInfluxDBV2: false, - InfluxDBToken: "test", - InfluxDBBucket: "geth", - InfluxDBOrganization: "geth", -} diff --git a/metrics/counter.go b/metrics/counter.go deleted file mode 100644 index cb81599c21..0000000000 --- a/metrics/counter.go +++ /dev/null @@ -1,112 +0,0 @@ -package metrics - -import ( - "sync/atomic" -) - -type CounterSnapshot interface { - Count() int64 -} - -// Counters 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 3a49cd4249..0000000000 --- a/metrics/cpu.go +++ /dev/null @@ -1,25 +0,0 @@ -// 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 025d97aeb3..0000000000 --- a/metrics/cpu_disabled.go +++ /dev/null @@ -1,24 +0,0 @@ -// 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 2359028a21..0000000000 --- a/metrics/cpu_enabled.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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 465d88c4d2..0000000000 --- a/metrics/cputime_nop.go +++ /dev/null @@ -1,26 +0,0 @@ -// 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 ad4f812fd2..0000000000 --- a/metrics/cputime_unix.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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 25142d2ad1..0000000000 --- a/metrics/disk.go +++ /dev/null @@ -1,25 +0,0 @@ -// 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 8d610cd674..0000000000 --- a/metrics/disk_linux.go +++ /dev/null @@ -1,72 +0,0 @@ -// 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 41bbe9adb2..0000000000 --- a/metrics/disk_nop.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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/exp/exp.go b/metrics/exp/exp.go deleted file mode 100644 index 7e3f82a075..0000000000 --- a/metrics/exp/exp.go +++ /dev/null @@ -1,213 +0,0 @@ -// Hook go-metrics into expvar -// on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler -package exp - -import ( - "expvar" - "fmt" - "net/http" - "sync" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/metrics/prometheus" -) - -type exp struct { - expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely - registry metrics.Registry -} - -func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { - // load our variables into expvar - exp.syncToExpvar() - - // now just run the official expvar handler code (which is not publicly callable, so pasted inline) - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintf(w, "{\n") - first := true - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintf(w, ",\n") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintf(w, "\n}\n") -} - -// Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" -func Exp(r metrics.Registry) { - h := ExpHandler(r) - // this would cause a panic: - // panic: http: multiple registrations for /debug/vars - // http.HandleFunc("/debug/vars", e.expHandler) - // haven't found an elegant way, so just use a different endpoint - http.Handle("/debug/metrics", h) - http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) -} - -// ExpHandler will return an expvar powered metrics handler. -func ExpHandler(r metrics.Registry) http.Handler { - e := exp{sync.Mutex{}, r} - return http.HandlerFunc(e.expHandler) -} - -// Setup starts a dedicated metrics server at the given address. -// This function enables metrics reporting separate from pprof. -func Setup(address string) { - m := http.NewServeMux() - m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry)) - m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry)) - log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address)) - go func() { - if err := http.ListenAndServe(address, m); err != nil { - log.Error("Failure in running metrics server", "err", err) - } - }() -} - -func (exp *exp) getInt(name string) *expvar.Int { - var v *expvar.Int - exp.expvarLock.Lock() - p := expvar.Get(name) - if p != nil { - v = p.(*expvar.Int) - } else { - v = new(expvar.Int) - expvar.Publish(name, v) - } - exp.expvarLock.Unlock() - return v -} - -func (exp *exp) getFloat(name string) *expvar.Float { - var v *expvar.Float - exp.expvarLock.Lock() - p := expvar.Get(name) - if p != nil { - v = p.(*expvar.Float) - } else { - v = new(expvar.Float) - expvar.Publish(name, v) - } - exp.expvarLock.Unlock() - return v -} - -func (exp *exp) getInfo(name string) *expvar.String { - var v *expvar.String - exp.expvarLock.Lock() - p := expvar.Get(name) - if p != nil { - v = p.(*expvar.String) - } else { - v = new(expvar.String) - expvar.Publish(name, v) - } - exp.expvarLock.Unlock() - return v -} - -func (exp *exp) publishCounter(name string, metric metrics.CounterSnapshot) { - v := exp.getInt(name) - v.Set(metric.Count()) -} - -func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64Snapshot) { - v := exp.getFloat(name) - v.Set(metric.Count()) -} - -func (exp *exp) publishGauge(name string, metric metrics.GaugeSnapshot) { - v := exp.getInt(name) - v.Set(metric.Value()) -} -func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64Snapshot) { - exp.getFloat(name).Set(metric.Value()) -} - -func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfoSnapshot) { - exp.getInfo(name).Set(metric.Value().String()) -} - -func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - exp.getInt(name + ".count").Set(h.Count()) - exp.getFloat(name + ".min").Set(float64(h.Min())) - exp.getFloat(name + ".max").Set(float64(h.Max())) - exp.getFloat(name + ".mean").Set(h.Mean()) - exp.getFloat(name + ".std-dev").Set(h.StdDev()) - exp.getFloat(name + ".50-percentile").Set(ps[0]) - exp.getFloat(name + ".75-percentile").Set(ps[1]) - exp.getFloat(name + ".95-percentile").Set(ps[2]) - exp.getFloat(name + ".99-percentile").Set(ps[3]) - exp.getFloat(name + ".999-percentile").Set(ps[4]) -} - -func (exp *exp) publishMeter(name string, metric metrics.Meter) { - m := metric.Snapshot() - exp.getInt(name + ".count").Set(m.Count()) - exp.getFloat(name + ".one-minute").Set(m.Rate1()) - exp.getFloat(name + ".five-minute").Set(m.Rate5()) - exp.getFloat(name + ".fifteen-minute").Set(m.Rate15()) - exp.getFloat(name + ".mean").Set(m.RateMean()) -} - -func (exp *exp) publishTimer(name string, metric metrics.Timer) { - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - exp.getInt(name + ".count").Set(t.Count()) - exp.getFloat(name + ".min").Set(float64(t.Min())) - exp.getFloat(name + ".max").Set(float64(t.Max())) - exp.getFloat(name + ".mean").Set(t.Mean()) - exp.getFloat(name + ".std-dev").Set(t.StdDev()) - exp.getFloat(name + ".50-percentile").Set(ps[0]) - exp.getFloat(name + ".75-percentile").Set(ps[1]) - exp.getFloat(name + ".95-percentile").Set(ps[2]) - exp.getFloat(name + ".99-percentile").Set(ps[3]) - exp.getFloat(name + ".999-percentile").Set(ps[4]) - exp.getFloat(name + ".one-minute").Set(t.Rate1()) - exp.getFloat(name + ".five-minute").Set(t.Rate5()) - exp.getFloat(name + ".fifteen-minute").Set(t.Rate15()) - exp.getFloat(name + ".mean-rate").Set(t.RateMean()) -} - -func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99}) - exp.getInt(name + ".count").Set(int64(t.Count())) - exp.getFloat(name + ".mean").Set(t.Mean()) - exp.getFloat(name + ".50-percentile").Set(ps[0]) - exp.getFloat(name + ".75-percentile").Set(ps[1]) - exp.getFloat(name + ".95-percentile").Set(ps[2]) - exp.getFloat(name + ".99-percentile").Set(ps[3]) -} - -func (exp *exp) syncToExpvar() { - exp.registry.Each(func(name string, i interface{}) { - switch i := i.(type) { - case metrics.Counter: - exp.publishCounter(name, i.Snapshot()) - case metrics.CounterFloat64: - exp.publishCounterFloat64(name, i.Snapshot()) - case metrics.Gauge: - exp.publishGauge(name, i.Snapshot()) - case metrics.GaugeFloat64: - exp.publishGaugeFloat64(name, i.Snapshot()) - case metrics.GaugeInfo: - exp.publishGaugeInfo(name, i.Snapshot()) - case metrics.Histogram: - exp.publishHistogram(name, i) - case metrics.Meter: - exp.publishMeter(name, i) - case metrics.Timer: - exp.publishTimer(name, i) - case metrics.ResettingTimer: - exp.publishResettingTimer(name, i) - default: - panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) - } - }) -} diff --git a/metrics/gauge.go b/metrics/gauge.go deleted file mode 100644 index 68f8f11abc..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 -} - -// Gauges hold 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 valie. -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 967f2bc60e..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) } - -// NilGauge 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 c44b2d85f3..0000000000 --- a/metrics/gauge_info.go +++ /dev/null @@ -1,84 +0,0 @@ -package metrics - -import ( - "encoding/json" - "sync" -) - -type GaugeInfoSnapshot interface { - Value() GaugeInfoValue -} - -// GaugeInfos hold 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 f1ae31e34a..0000000000 --- a/metrics/healthcheck.go +++ /dev/null @@ -1,61 +0,0 @@ -package metrics - -// Healthchecks hold 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 44de588bc1..0000000000 --- a/metrics/histogram.go +++ /dev/null @@ -1,73 +0,0 @@ -package metrics - -type HistogramSnapshot interface { - SampleSnapshot -} - -// Histograms calculate 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/influxdb/LICENSE b/metrics/influxdb/LICENSE deleted file mode 100644 index e5bf20cdb5..0000000000 --- a/metrics/influxdb/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 Vincent Rischmann - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/metrics/influxdb/README.md b/metrics/influxdb/README.md deleted file mode 100644 index b76b1a3f99..0000000000 --- a/metrics/influxdb/README.md +++ /dev/null @@ -1,30 +0,0 @@ -go-metrics-influxdb -=================== - -This is a reporter for the [go-metrics](https://github.com/rcrowley/go-metrics) library which will post the metrics to [InfluxDB](https://influxdb.com/). - -Note ----- - -This is only compatible with InfluxDB 0.9+. - -Usage ------ - -```go -import "github.com/vrischmann/go-metrics-influxdb" - -go influxdb.InfluxDB( - metrics.DefaultRegistry, // metrics registry - time.Second * 10, // interval - "http://localhost:8086", // the InfluxDB url - "mydb", // your InfluxDB database - "myuser", // your InfluxDB user - "mypassword", // your InfluxDB password -) -``` - -License -------- - -go-metrics-influxdb is licensed under the MIT license. See the LICENSE file for details. diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go deleted file mode 100644 index bbc4fc024b..0000000000 --- a/metrics/influxdb/influxdb.go +++ /dev/null @@ -1,119 +0,0 @@ -package influxdb - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/metrics" -) - -func readMeter(namespace, name string, i interface{}) (string, map[string]interface{}) { - switch metric := i.(type) { - case metrics.Counter: - measurement := fmt.Sprintf("%s%s.count", namespace, name) - fields := map[string]interface{}{ - "value": metric.Snapshot().Count(), - } - return measurement, fields - case metrics.CounterFloat64: - measurement := fmt.Sprintf("%s%s.count", namespace, name) - fields := map[string]interface{}{ - "value": metric.Snapshot().Count(), - } - return measurement, fields - case metrics.Gauge: - measurement := fmt.Sprintf("%s%s.gauge", namespace, name) - fields := map[string]interface{}{ - "value": metric.Snapshot().Value(), - } - return measurement, fields - case metrics.GaugeFloat64: - measurement := fmt.Sprintf("%s%s.gauge", namespace, name) - fields := map[string]interface{}{ - "value": metric.Snapshot().Value(), - } - return measurement, fields - case metrics.GaugeInfo: - ms := metric.Snapshot() - measurement := fmt.Sprintf("%s%s.gauge", namespace, name) - fields := map[string]interface{}{ - "value": ms.Value().String(), - } - return measurement, fields - case metrics.Histogram: - ms := metric.Snapshot() - if ms.Count() <= 0 { - break - } - ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - measurement := fmt.Sprintf("%s%s.histogram", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p25": ps[0], - "p50": ps[1], - "p75": ps[2], - "p95": ps[3], - "p99": ps[4], - "p999": ps[5], - "p9999": ps[6], - } - return measurement, fields - case metrics.Meter: - ms := metric.Snapshot() - measurement := fmt.Sprintf("%s%s.meter", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "mean": ms.RateMean(), - } - return measurement, fields - case metrics.Timer: - ms := metric.Snapshot() - ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - - measurement := fmt.Sprintf("%s%s.timer", namespace, name) - fields := map[string]interface{}{ - "count": ms.Count(), - "max": ms.Max(), - "mean": ms.Mean(), - "min": ms.Min(), - "stddev": ms.StdDev(), - "variance": ms.Variance(), - "p50": ps[0], - "p75": ps[1], - "p95": ps[2], - "p99": ps[3], - "p999": ps[4], - "p9999": ps[5], - "m1": ms.Rate1(), - "m5": ms.Rate5(), - "m15": ms.Rate15(), - "meanrate": ms.RateMean(), - } - return measurement, fields - case metrics.ResettingTimer: - t := metric.Snapshot() - if t.Count() == 0 { - break - } - ps := t.Percentiles([]float64{0.50, 0.95, 0.99}) - measurement := fmt.Sprintf("%s%s.span", namespace, name) - fields := map[string]interface{}{ - "count": t.Count(), - "max": t.Max(), - "mean": t.Mean(), - "min": t.Min(), - "p50": int(ps[0]), - "p95": int(ps[1]), - "p99": int(ps[2]), - } - return measurement, fields - } - return "", nil -} diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go deleted file mode 100644 index c6f2eeac62..0000000000 --- a/metrics/influxdb/influxdb_test.go +++ /dev/null @@ -1,114 +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 influxdb - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "net/url" - "os" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/metrics/internal" - influxdb2 "github.com/influxdata/influxdb-client-go/v2" -) - -func TestMain(m *testing.M) { - metrics.Enabled = true - os.Exit(m.Run()) -} - -func TestExampleV1(t *testing.T) { - r := internal.ExampleMetrics() - var have, want string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - haveB, _ := io.ReadAll(r.Body) - have = string(haveB) - r.Body.Close() - })) - defer ts.Close() - u, _ := url.Parse(ts.URL) - rep := &reporter{ - reg: r, - url: *u, - namespace: "goth.", - } - if err := rep.makeClient(); err != nil { - t.Fatal(err) - } - if err := rep.send(978307200); err != nil { - t.Fatal(err) - } - if wantB, err := os.ReadFile("./testdata/influxdbv1.want"); err != nil { - t.Fatal(err) - } else { - want = string(wantB) - } - if have != want { - t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want) - t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) - } -} - -func TestExampleV2(t *testing.T) { - r := internal.ExampleMetrics() - var have, want string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - haveB, _ := io.ReadAll(r.Body) - have = string(haveB) - r.Body.Close() - })) - defer ts.Close() - - rep := &v2Reporter{ - reg: r, - endpoint: ts.URL, - namespace: "goth.", - } - rep.client = influxdb2.NewClient(rep.endpoint, rep.token) - defer rep.client.Close() - rep.write = rep.client.WriteAPI(rep.organization, rep.bucket) - - rep.send(978307200) - - if wantB, err := os.ReadFile("./testdata/influxdbv2.want"); err != nil { - t.Fatal(err) - } else { - want = string(wantB) - } - if 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/influxdb/influxdbv1.go b/metrics/influxdb/influxdbv1.go deleted file mode 100644 index ac58280803..0000000000 --- a/metrics/influxdb/influxdbv1.go +++ /dev/null @@ -1,152 +0,0 @@ -package influxdb - -import ( - "fmt" - uurl "net/url" - "time" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - client "github.com/influxdata/influxdb1-client/v2" -) - -type reporter struct { - reg metrics.Registry - interval time.Duration - - url uurl.URL - database string - username string - password string - namespace string - tags map[string]string - - client client.Client - - cache map[string]int64 -} - -// InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval. -func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) { - InfluxDBWithTags(r, d, url, database, username, password, namespace, nil) -} - -// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags -func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { - u, err := uurl.Parse(url) - if err != nil { - log.Warn("Unable to parse InfluxDB", "url", url, "err", err) - return - } - - rep := &reporter{ - reg: r, - interval: d, - url: *u, - database: database, - username: username, - password: password, - namespace: namespace, - tags: tags, - cache: make(map[string]int64), - } - if err := rep.makeClient(); err != nil { - log.Warn("Unable to make InfluxDB client", "err", err) - return - } - - rep.run() -} - -// InfluxDBWithTagsOnce runs once an InfluxDB reporter and post the given metrics.Registry with the specified tags -func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error { - u, err := uurl.Parse(url) - if err != nil { - return fmt.Errorf("unable to parse InfluxDB. url: %s, err: %v", url, err) - } - - rep := &reporter{ - reg: r, - url: *u, - database: database, - username: username, - password: password, - namespace: namespace, - tags: tags, - cache: make(map[string]int64), - } - if err := rep.makeClient(); err != nil { - return fmt.Errorf("unable to make InfluxDB client. err: %v", err) - } - - if err := rep.send(0); err != nil { - return fmt.Errorf("unable to send to InfluxDB. err: %v", err) - } - - return nil -} - -func (r *reporter) makeClient() (err error) { - r.client, err = client.NewHTTPClient(client.HTTPConfig{ - Addr: r.url.String(), - Username: r.username, - Password: r.password, - Timeout: 10 * time.Second, - }) - - return -} - -func (r *reporter) run() { - intervalTicker := time.NewTicker(r.interval) - pingTicker := time.NewTicker(time.Second * 5) - - defer intervalTicker.Stop() - defer pingTicker.Stop() - - for { - select { - case <-intervalTicker.C: - if err := r.send(0); err != nil { - log.Warn("Unable to send to InfluxDB", "err", err) - } - case <-pingTicker.C: - _, _, err := r.client.Ping(0) - if err != nil { - log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err) - - if err = r.makeClient(); err != nil { - log.Warn("Unable to make InfluxDB client", "err", err) - } - } - } - } -} - -// send sends the measurements. If provided tstamp is >0, it is used. Otherwise, -// a 'fresh' timestamp is used. -func (r *reporter) send(tstamp int64) error { - bps, err := client.NewBatchPoints( - client.BatchPointsConfig{ - Database: r.database, - }) - if err != nil { - return err - } - r.reg.Each(func(name string, i interface{}) { - var now time.Time - if tstamp <= 0 { - now = time.Now() - } else { - now = time.Unix(tstamp, 0) - } - measurement, fields := readMeter(r.namespace, name, i) - if fields == nil { - return - } - if p, err := client.NewPoint(measurement, r.tags, fields, now); err == nil { - bps.AddPoint(p) - } - }) - return r.client.Write(bps) -} diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go deleted file mode 100644 index 0be5137d5e..0000000000 --- a/metrics/influxdb/influxdbv2.go +++ /dev/null @@ -1,96 +0,0 @@ -package influxdb - -import ( - "context" - "time" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - influxdb2 "github.com/influxdata/influxdb-client-go/v2" - "github.com/influxdata/influxdb-client-go/v2/api" -) - -type v2Reporter struct { - reg metrics.Registry - interval time.Duration - - endpoint string - token string - bucket string - organization string - namespace string - tags map[string]string - - client influxdb2.Client - write api.WriteAPI -} - -// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags -func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) { - rep := &v2Reporter{ - reg: r, - interval: d, - endpoint: endpoint, - token: token, - bucket: bucket, - organization: organization, - namespace: namespace, - tags: tags, - } - - rep.client = influxdb2.NewClient(rep.endpoint, rep.token) - defer rep.client.Close() - - // async write client - rep.write = rep.client.WriteAPI(rep.organization, rep.bucket) - errorsCh := rep.write.Errors() - - // have to handle write errors in a separate goroutine like this b/c the channel is unbuffered and will block writes if not read - go func() { - for err := range errorsCh { - log.Warn("write error", "err", err.Error()) - } - }() - rep.run() -} - -func (r *v2Reporter) run() { - intervalTicker := time.NewTicker(r.interval) - pingTicker := time.NewTicker(time.Second * 5) - - defer intervalTicker.Stop() - defer pingTicker.Stop() - - for { - select { - case <-intervalTicker.C: - r.send(0) - case <-pingTicker.C: - _, err := r.client.Health(context.Background()) - if err != nil { - log.Warn("Got error from influxdb client health check", "err", err.Error()) - } - } - } -} - -// send sends the measurements. If provided tstamp is >0, it is used. Otherwise, -// a 'fresh' timestamp is used. -func (r *v2Reporter) send(tstamp int64) { - r.reg.Each(func(name string, i interface{}) { - var now time.Time - if tstamp <= 0 { - now = time.Now() - } else { - now = time.Unix(tstamp, 0) - } - measurement, fields := readMeter(r.namespace, name, i) - if fields == nil { - return - } - pt := influxdb2.NewPoint(measurement, r.tags, fields, now) - r.write.WritePoint(pt) - }) - // Force all unwritten data to be sent - r.write.Flush() -} diff --git a/metrics/influxdb/testdata/influxdbv1.want b/metrics/influxdb/testdata/influxdbv1.want deleted file mode 100644 index 9443faedc5..0000000000 --- a/metrics/influxdb/testdata/influxdbv1.want +++ /dev/null @@ -1,11 +0,0 @@ -goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000 -goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000 -goth.test/counter.count value=12345 978307200000000000 -goth.test/counter_float64.count value=54321.98 978307200000000000 -goth.test/gauge.gauge value=23456i 978307200000000000 -goth.test/gauge_float64.gauge value=34567.89 978307200000000000 -goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 -goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 -goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 -goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/influxdb/testdata/influxdbv2.want b/metrics/influxdb/testdata/influxdbv2.want deleted file mode 100644 index 9443faedc5..0000000000 --- a/metrics/influxdb/testdata/influxdbv2.want +++ /dev/null @@ -1,11 +0,0 @@ -goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000 -goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000 -goth.test/counter.count value=12345 978307200000000000 -goth.test/counter_float64.count value=54321.98 978307200000000000 -goth.test/gauge.gauge value=23456i 978307200000000000 -goth.test/gauge_float64.gauge value=34567.89 978307200000000000 -goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000 -goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000 -goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000 -goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000i,p95=120000000i,p99=120000000i 978307200000000000 -goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000 diff --git a/metrics/init_test.go b/metrics/init_test.go deleted file mode 100644 index 43401e833c..0000000000 --- a/metrics/init_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package metrics - -func init() { - Enabled = true -} diff --git a/metrics/internal/sampledata.go b/metrics/internal/sampledata.go deleted file mode 100644 index de9b207b6d..0000000000 --- a/metrics/internal/sampledata.go +++ /dev/null @@ -1,95 +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 internal - -import ( - "bytes" - "encoding/gob" - metrics2 "runtime/metrics" - "time" - - "github.com/ethereum/go-ethereum/metrics" -) - -// ExampleMetrics returns an ordered registry populated with a sample of metrics. -func ExampleMetrics() metrics.Registry { - var registry = metrics.NewOrderedRegistry() - - metrics.NewRegisteredCounterFloat64("test/counter", registry).Inc(12345) - metrics.NewRegisteredCounterFloat64("test/counter_float64", registry).Inc(54321.98) - metrics.NewRegisteredGauge("test/gauge", registry).Update(23456) - metrics.NewRegisteredGaugeFloat64("test/gauge_float64", registry).Update(34567.89) - metrics.NewRegisteredGaugeInfo("test/gauge_info", registry).Update( - metrics.GaugeInfoValue{ - "version": "1.10.18-unstable", - "arch": "amd64", - "os": "linux", - "commit": "7caa2d8163ae3132c1c2d6978c76610caee2d949", - "protocol_versions": "64 65 66", - }) - - { - s := metrics.NewUniformSample(3) - s.Update(1) - s.Update(2) - s.Update(3) - //metrics.NewRegisteredHistogram("test/histogram", registry, metrics.NewSampleSnapshot(3, []int64{1, 2, 3})) - metrics.NewRegisteredHistogram("test/histogram", registry, s) - } - registry.Register("test/meter", metrics.NewInactiveMeter()) - { - timer := metrics.NewRegisteredResettingTimer("test/resetting_timer", registry) - timer.Update(10 * time.Millisecond) - timer.Update(11 * time.Millisecond) - timer.Update(12 * time.Millisecond) - timer.Update(120 * time.Millisecond) - timer.Update(13 * time.Millisecond) - timer.Update(14 * time.Millisecond) - } - { - timer := metrics.NewRegisteredTimer("test/timer", registry) - 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) - timer.Stop() - } - registry.Register("test/empty_resetting_timer", metrics.NewResettingTimer().Snapshot()) - - { // go runtime metrics - var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" - var gcPauses = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06R\xff\x82\x01\xff\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" - - var secondsToNs = float64(time.Second) - - dserialize := func(data string) *metrics2.Float64Histogram { - var res metrics2.Float64Histogram - if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil { - panic(err) - } - return &res - } - cpuSchedLatency := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(sLatency)) - registry.Register("system/cpu/schedlatency", cpuSchedLatency) - - memPauses := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(gcPauses)) - registry.Register("system/memory/pauses", memPauses) - } - return registry -} diff --git a/metrics/internal/sampledata_test.go b/metrics/internal/sampledata_test.go deleted file mode 100644 index 0013299406..0000000000 --- a/metrics/internal/sampledata_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package internal - -import ( - "bytes" - "encoding/gob" - "fmt" - metrics2 "runtime/metrics" - "testing" - "time" - - "github.com/ethereum/go-ethereum/metrics" -) - -func TestCollectRuntimeMetrics(t *testing.T) { - t.Skip("Only used for generating testdata") - serialize := func(path string, histogram *metrics2.Float64Histogram) { - var f = new(bytes.Buffer) - if err := gob.NewEncoder(f).Encode(histogram); err != nil { - panic(err) - } - fmt.Printf("var %v = %q\n", path, f.Bytes()) - } - time.Sleep(2 * time.Second) - stats := metrics.ReadRuntimeStats() - serialize("schedlatency", stats.SchedLatency) - serialize("gcpauses", stats.GCPauses) -} 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/librato/client.go b/metrics/librato/client.go deleted file mode 100644 index f1b9e1e916..0000000000 --- a/metrics/librato/client.go +++ /dev/null @@ -1,104 +0,0 @@ -package librato - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" -) - -const Operations = "operations" -const OperationsShort = "ops" - -type LibratoClient struct { - Email, Token string -} - -// property strings -const ( - // display attributes - Color = "color" - DisplayMax = "display_max" - DisplayMin = "display_min" - DisplayUnitsLong = "display_units_long" - DisplayUnitsShort = "display_units_short" - DisplayStacked = "display_stacked" - DisplayTransform = "display_transform" - // special gauge display attributes - SummarizeFunction = "summarize_function" - Aggregate = "aggregate" - - // metric keys - Name = "name" - Period = "period" - Description = "description" - DisplayName = "display_name" - Attributes = "attributes" - - // measurement keys - MeasureTime = "measure_time" - Source = "source" - Value = "value" - - // special gauge keys - Count = "count" - Sum = "sum" - Max = "max" - Min = "min" - SumSquares = "sum_squares" - - // batch keys - Counters = "counters" - Gauges = "gauges" - - MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics" -) - -type Measurement map[string]interface{} -type Metric map[string]interface{} - -type Batch struct { - Gauges []Measurement `json:"gauges,omitempty"` - Counters []Measurement `json:"counters,omitempty"` - MeasureTime int64 `json:"measure_time"` - Source string `json:"source"` -} - -func (c *LibratoClient) PostMetrics(batch Batch) (err error) { - var ( - js []byte - req *http.Request - resp *http.Response - ) - - if len(batch.Counters) == 0 && len(batch.Gauges) == 0 { - return nil - } - - if js, err = json.Marshal(batch); err != nil { - return - } - - if req, err = http.NewRequest(http.MethodPost, MetricsPostUrl, bytes.NewBuffer(js)); err != nil { - return - } - - req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(c.Email, c.Token) - - resp, err = http.DefaultClient.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var body []byte - if body, err = io.ReadAll(resp.Body); err != nil { - body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err)) - } - err = fmt.Errorf("unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body)) - } - return -} diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go deleted file mode 100644 index a86f758637..0000000000 --- a/metrics/librato/librato.go +++ /dev/null @@ -1,254 +0,0 @@ -package librato - -import ( - "fmt" - "log" - "math" - "regexp" - "time" - - "github.com/ethereum/go-ethereum/metrics" -) - -// a regexp for extracting the unit from time.Duration.String -var unitRegexp = regexp.MustCompile(`[^\\d]+$`) - -// a helper that turns a time.Duration into librato display attributes for timer metrics -func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) { - attrs = make(map[string]interface{}) - attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d)) - attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String()))) - return -} - -type Reporter struct { - Email, Token string - Namespace string - Source string - Interval time.Duration - Registry metrics.Registry - Percentiles []float64 // percentiles to report on histogram metrics - TimerAttributes map[string]interface{} // units in which timers will be displayed - intervalSec int64 -} - -func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter { - return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)} -} - -func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) { - NewReporter(r, d, e, t, s, p, u).Run() -} - -func (rep *Reporter) Run() { - log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015") - ticker := time.NewTicker(rep.Interval) - defer ticker.Stop() - metricsApi := &LibratoClient{rep.Email, rep.Token} - for now := range ticker.C { - var metrics Batch - var err error - if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil { - log.Printf("ERROR constructing librato request body %s", err) - continue - } - if err := metricsApi.PostMetrics(metrics); err != nil { - log.Printf("ERROR sending metrics to librato %s", err) - continue - } - } -} - -// calculate sum of squares from data provided by metrics.Histogram -// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods -func sumSquares(icount int64, mean, stDev float64) float64 { - count := float64(icount) - sumSquared := math.Pow(count*mean, 2) - sumSquares := math.Pow(count*stDev, 2) + sumSquared/count - if math.IsNaN(sumSquares) { - return 0.0 - } - return sumSquares -} -func sumSquaresTimer(t metrics.TimerSnapshot) float64 { - count := float64(t.Count()) - sumSquared := math.Pow(count*t.Mean(), 2) - sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count - if math.IsNaN(sumSquares) { - return 0.0 - } - return sumSquares -} - -func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { - snapshot = Batch{ - // coerce timestamps to a stepping fn so that they line up in Librato graphs - MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec, - Source: rep.Source, - } - snapshot.Gauges = make([]Measurement, 0) - snapshot.Counters = make([]Measurement, 0) - histogramGaugeCount := 1 + len(rep.Percentiles) - r.Each(func(name string, metric interface{}) { - if rep.Namespace != "" { - name = fmt.Sprintf("%s.%s", rep.Namespace, name) - } - measurement := Measurement{} - measurement[Period] = rep.Interval.Seconds() - switch m := metric.(type) { - case metrics.Counter: - ms := m.Snapshot() - if ms.Count() > 0 { - measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = float64(ms.Count()) - measurement[Attributes] = map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - } - snapshot.Counters = append(snapshot.Counters, measurement) - } - case metrics.CounterFloat64: - if count := m.Snapshot().Count(); count > 0 { - measurement[Name] = fmt.Sprintf("%s.%s", name, "count") - measurement[Value] = count - measurement[Attributes] = map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - } - snapshot.Counters = append(snapshot.Counters, measurement) - } - case metrics.Gauge: - measurement[Name] = name - measurement[Value] = float64(m.Snapshot().Value()) - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.GaugeFloat64: - measurement[Name] = name - measurement[Value] = m.Snapshot().Value() - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.GaugeInfo: - measurement[Name] = name - measurement[Value] = m.Snapshot().Value() - snapshot.Gauges = append(snapshot.Gauges, measurement) - case metrics.Histogram: - ms := m.Snapshot() - if ms.Count() > 0 { - gauges := make([]Measurement, histogramGaugeCount) - measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") - measurement[Count] = uint64(ms.Count()) - measurement[Max] = float64(ms.Max()) - measurement[Min] = float64(ms.Min()) - measurement[Sum] = float64(ms.Sum()) - measurement[SumSquares] = sumSquares(ms.Count(), ms.Mean(), ms.StdDev()) - gauges[0] = measurement - for i, p := range rep.Percentiles { - gauges[i+1] = Measurement{ - Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), - Value: ms.Percentile(p), - Period: measurement[Period], - } - } - snapshot.Gauges = append(snapshot.Gauges, gauges...) - } - case metrics.Meter: - ms := m.Snapshot() - measurement[Name] = name - measurement[Value] = float64(ms.Count()) - snapshot.Counters = append(snapshot.Counters, measurement) - snapshot.Gauges = append(snapshot.Gauges, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "1min"), - Value: ms.Rate1(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "5min"), - Value: ms.Rate5(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "15min"), - Value: ms.Rate15(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - ) - case metrics.Timer: - ms := m.Snapshot() - measurement[Name] = name - measurement[Value] = float64(ms.Count()) - snapshot.Counters = append(snapshot.Counters, measurement) - if ms.Count() > 0 { - libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") - gauges := make([]Measurement, histogramGaugeCount) - gauges[0] = Measurement{ - Name: libratoName, - Count: uint64(ms.Count()), - Sum: ms.Mean() * float64(ms.Count()), - Max: float64(ms.Max()), - Min: float64(ms.Min()), - SumSquares: sumSquaresTimer(ms), - Period: int64(rep.Interval.Seconds()), - Attributes: rep.TimerAttributes, - } - for i, p := range rep.Percentiles { - gauges[i+1] = Measurement{ - Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), - Value: ms.Percentile(p), - Period: int64(rep.Interval.Seconds()), - Attributes: rep.TimerAttributes, - } - } - snapshot.Gauges = append(snapshot.Gauges, gauges...) - snapshot.Gauges = append(snapshot.Gauges, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.1min"), - Value: ms.Rate1(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.5min"), - Value: ms.Rate5(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - Measurement{ - Name: fmt.Sprintf("%s.%s", name, "rate.15min"), - Value: ms.Rate15(), - Period: int64(rep.Interval.Seconds()), - Attributes: map[string]interface{}{ - DisplayUnitsLong: Operations, - DisplayUnitsShort: OperationsShort, - DisplayMin: "0", - }, - }, - ) - } - } - }) - return -} 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 9ca8f115c0..0000000000 --- a/metrics/metrics.go +++ /dev/null @@ -1,250 +0,0 @@ -// Go port of Coda Hale's Metrics library -// -// -// -// Coda Hale's original work: -package metrics - -import ( - "os" - "runtime/metrics" - "runtime/pprof" - "strconv" - "strings" - "syscall" - "time" - - "github.com/ethereum/go-ethereum/log" -) - -// 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 = false - -// 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 - -// enablerFlags is the CLI flag names to use to enable metrics collections. -var enablerFlags = []string{"metrics"} - -// enablerEnvVars is the env var names to use to enable metrics collections. -var enablerEnvVars = []string{"GETH_METRICS"} - -// expensiveEnablerFlags is the CLI flag names to use to enable metrics collections. -var expensiveEnablerFlags = []string{"metrics.expensive"} - -// expensiveEnablerEnvVars is the env var names to use to enable metrics collections. -var expensiveEnablerEnvVars = []string{"GETH_METRICS_EXPENSIVE"} - -// Init enables or disables the metrics system. Since we need this to run before -// any other code gets to create meters and timers, we'll actually do an ugly hack -// and peek into the command line args for the metrics flag. -func init() { - for _, enabler := range enablerEnvVars { - if val, found := syscall.Getenv(enabler); found && !Enabled { - if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later - log.Info("Enabling metrics collection") - Enabled = true - } - } - } - for _, enabler := range expensiveEnablerEnvVars { - if val, found := syscall.Getenv(enabler); found && !EnabledExpensive { - if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later - log.Info("Enabling expensive metrics collection") - EnabledExpensive = true - } - } - } - for _, arg := range os.Args { - flag := strings.TrimLeft(arg, "-") - - for _, enabler := range enablerFlags { - if !Enabled && flag == enabler { - log.Info("Enabling metrics collection") - Enabled = true - } - } - for _, enabler := range expensiveEnablerFlags { - if !EnabledExpensive && flag == enabler { - log.Info("Enabling expensive metrics collection") - EnabledExpensive = true - } - } - } -} - -var threadCreateProfile = pprof.Lookup("threadcreate") - -type runtimeStats struct { - GCPauses *metrics.Float64Histogram - GCAllocBytes uint64 - GCFreedBytes uint64 - - MemTotal uint64 - HeapObjects uint64 - HeapFree uint64 - HeapReleased uint64 - HeapUnused uint64 - - Goroutines uint64 - SchedLatency *metrics.Float64Histogram -} - -var runtimeSamples = []metrics.Sample{ - {Name: "/gc/pauses:seconds"}, // histogram - {Name: "/gc/heap/allocs:bytes"}, - {Name: "/gc/heap/frees:bytes"}, - {Name: "/memory/classes/total:bytes"}, - {Name: "/memory/classes/heap/objects:bytes"}, - {Name: "/memory/classes/heap/free:bytes"}, - {Name: "/memory/classes/heap/released:bytes"}, - {Name: "/memory/classes/heap/unused:bytes"}, - {Name: "/sched/goroutines:goroutines"}, - {Name: "/sched/latencies:seconds"}, // histogram -} - -func ReadRuntimeStats() *runtimeStats { - r := new(runtimeStats) - readRuntimeStats(r) - return r -} - -func readRuntimeStats(v *runtimeStats) { - metrics.Read(runtimeSamples) - for _, s := range runtimeSamples { - // Skip invalid/unknown metrics. This is needed because some metrics - // are unavailable in older Go versions, and attempting to read a 'bad' - // metric panics. - if s.Value.Kind() == metrics.KindBad { - continue - } - - switch s.Name { - case "/gc/pauses:seconds": - v.GCPauses = s.Value.Float64Histogram() - case "/gc/heap/allocs:bytes": - v.GCAllocBytes = s.Value.Uint64() - case "/gc/heap/frees:bytes": - v.GCFreedBytes = s.Value.Uint64() - case "/memory/classes/total:bytes": - v.MemTotal = s.Value.Uint64() - case "/memory/classes/heap/objects:bytes": - v.HeapObjects = s.Value.Uint64() - case "/memory/classes/heap/free:bytes": - v.HeapFree = s.Value.Uint64() - case "/memory/classes/heap/released:bytes": - v.HeapReleased = s.Value.Uint64() - case "/memory/classes/heap/unused:bytes": - v.HeapUnused = s.Value.Uint64() - case "/sched/goroutines:goroutines": - v.Goroutines = s.Value.Uint64() - case "/sched/latencies:seconds": - v.SchedLatency = s.Value.Float64Histogram() - } - } -} - -// CollectProcessMetrics periodically collects various metrics about the running process. -func CollectProcessMetrics(refresh time.Duration) { - // Short circuit if the metrics system is disabled - if !Enabled { - return - } - - // Create the various data collectors - var ( - cpustats = make([]CPUStats, 2) - diskstats = make([]DiskStats, 2) - rstats = make([]runtimeStats, 2) - ) - - // This scale factor is used for the runtime's time metrics. It's useful to convert to - // ns here because the runtime gives times in float seconds, but runtimeHistogram can - // only provide integers for the minimum and maximum values. - const secondsToNs = float64(time.Second) - - // Define the various metrics to collect - var ( - cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) - cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) - cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) - cpuSysLoadTotal = GetOrRegisterCounterFloat64("system/cpu/sysload/total", DefaultRegistry) - cpuSysWaitTotal = GetOrRegisterCounterFloat64("system/cpu/syswait/total", DefaultRegistry) - cpuProcLoadTotal = GetOrRegisterCounterFloat64("system/cpu/procload/total", DefaultRegistry) - cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) - cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) - cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil) - memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil) - memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) - memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) - memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry) - heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) - heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry) - diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) - diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) - diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) - diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) - diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) - diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) - ) - - var lastCollectTime time.Time - - // Iterate loading the different stats and updating the meters. - now, prev := 0, 1 - for ; ; now, prev = prev, now { - // Gather CPU times. - ReadCPUStats(&cpustats[now]) - collectTime := time.Now() - secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds() - lastCollectTime = collectTime - if secondsSinceLastCollect > 0 { - sysLoad := cpustats[now].GlobalTime - cpustats[prev].GlobalTime - sysWait := cpustats[now].GlobalWait - cpustats[prev].GlobalWait - procLoad := cpustats[now].LocalTime - cpustats[prev].LocalTime - // Convert to integer percentage. - cpuSysLoad.Update(int64(sysLoad / secondsSinceLastCollect * 100)) - cpuSysWait.Update(int64(sysWait / secondsSinceLastCollect * 100)) - cpuProcLoad.Update(int64(procLoad / secondsSinceLastCollect * 100)) - // increment counters (ms) - cpuSysLoadTotal.Inc(sysLoad) - cpuSysWaitTotal.Inc(sysWait) - cpuProcLoadTotal.Inc(procLoad) - } - - // Threads - cpuThreads.Update(int64(threadCreateProfile.Count())) - - // Go runtime metrics - readRuntimeStats(&rstats[now]) - - cpuGoroutines.Update(int64(rstats[now].Goroutines)) - cpuSchedLatency.update(rstats[now].SchedLatency) - memPauses.update(rstats[now].GCPauses) - - memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes)) - memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes)) - - memTotal.Update(int64(rstats[now].MemTotal)) - heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased)) - heapObjects.Update(int64(rstats[now].HeapObjects)) - - // Disk - if ReadDiskStats(&diskstats[now]) == nil { - diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount) - diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) - diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount) - diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) - diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) - diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) - } - - time.Sleep(refresh) - } -} diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go deleted file mode 100644 index 2861d5f2ca..0000000000 --- a/metrics/metrics_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package metrics - -import ( - "fmt" - "sync" - "testing" - "time" -) - -const FANOUT = 128 - -func TestReadRuntimeValues(t *testing.T) { - var v runtimeStats - readRuntimeStats(&v) - t.Logf("%+v", v) -} - -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 4548309f9c..0000000000 --- a/metrics/opentsdb_test.go +++ /dev/null @@ -1,66 +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) - } - if have, want := w.String(), string(wantB); 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/collector.go b/metrics/prometheus/collector.go deleted file mode 100644 index 25b258d56a..0000000000 --- a/metrics/prometheus/collector.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2019 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 prometheus - -import ( - "bytes" - "fmt" - "sort" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/metrics" -) - -var ( - typeGaugeTpl = "# TYPE %s gauge\n" - typeCounterTpl = "# TYPE %s counter\n" - typeSummaryTpl = "# TYPE %s summary\n" - keyValueTpl = "%s %v\n\n" - keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n" -) - -// collector is a collection of byte buffers that aggregate Prometheus reports -// for different metric types. -type collector struct { - buff *bytes.Buffer -} - -// newCollector creates a new Prometheus metric aggregator. -func newCollector() *collector { - return &collector{ - buff: &bytes.Buffer{}, - } -} - -// Add adds the metric i to the collector. This method returns an error if the -// metric type is not supported/known. -func (c *collector) Add(name string, i any) error { - switch m := i.(type) { - case metrics.Counter: - c.addCounter(name, m.Snapshot()) - case metrics.CounterFloat64: - c.addCounterFloat64(name, m.Snapshot()) - case metrics.Gauge: - c.addGauge(name, m.Snapshot()) - case metrics.GaugeFloat64: - c.addGaugeFloat64(name, m.Snapshot()) - case metrics.GaugeInfo: - c.addGaugeInfo(name, m.Snapshot()) - case metrics.Histogram: - c.addHistogram(name, m.Snapshot()) - case metrics.Meter: - c.addMeter(name, m.Snapshot()) - case metrics.Timer: - c.addTimer(name, m.Snapshot()) - case metrics.ResettingTimer: - c.addResettingTimer(name, m.Snapshot()) - default: - return fmt.Errorf("unknown prometheus metric type %T", i) - } - return nil -} - -func (c *collector) addCounter(name string, m metrics.CounterSnapshot) { - c.writeGaugeCounter(name, m.Count()) -} - -func (c *collector) addCounterFloat64(name string, m metrics.CounterFloat64Snapshot) { - c.writeGaugeCounter(name, m.Count()) -} - -func (c *collector) addGauge(name string, m metrics.GaugeSnapshot) { - c.writeGaugeCounter(name, m.Value()) -} - -func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64Snapshot) { - c.writeGaugeCounter(name, m.Value()) -} - -func (c *collector) addGaugeInfo(name string, m metrics.GaugeInfoSnapshot) { - c.writeGaugeInfo(name, m.Value()) -} - -func (c *collector) addHistogram(name string, m metrics.HistogramSnapshot) { - pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} - ps := m.Percentiles(pv) - c.writeSummaryCounter(name, m.Count()) - c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) - for i := range pv { - c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) - } - c.buff.WriteRune('\n') -} - -func (c *collector) addMeter(name string, m metrics.MeterSnapshot) { - c.writeGaugeCounter(name, m.Count()) -} - -func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { - pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} - ps := m.Percentiles(pv) - c.writeSummaryCounter(name, m.Count()) - c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) - for i := range pv { - c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i]) - } - c.buff.WriteRune('\n') -} - -func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnapshot) { - if m.Count() <= 0 { - return - } - ps := m.Percentiles([]float64{0.50, 0.95, 0.99}) - c.writeSummaryCounter(name, m.Count()) - c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name))) - c.writeSummaryPercentile(name, "0.50", ps[0]) - c.writeSummaryPercentile(name, "0.95", ps[1]) - c.writeSummaryPercentile(name, "0.99", ps[2]) - c.buff.WriteRune('\n') -} - -func (c *collector) writeGaugeInfo(name string, value metrics.GaugeInfoValue) { - name = mutateKey(name) - c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) - c.buff.WriteString(name) - c.buff.WriteString(" ") - var kvs []string - for k, v := range value { - kvs = append(kvs, fmt.Sprintf("%v=%q", k, v)) - } - sort.Strings(kvs) - c.buff.WriteString(fmt.Sprintf("{%v} 1\n\n", strings.Join(kvs, ", "))) -} - -func (c *collector) writeGaugeCounter(name string, value interface{}) { - name = mutateKey(name) - c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) - c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value)) -} - -func (c *collector) writeSummaryCounter(name string, value interface{}) { - name = mutateKey(name + "_count") - c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name)) - c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value)) -} - -func (c *collector) writeSummaryPercentile(name, p string, value interface{}) { - name = mutateKey(name) - c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value)) -} - -func mutateKey(key string) string { - return strings.ReplaceAll(key, "/", "_") -} diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go deleted file mode 100644 index ea17aac458..0000000000 --- a/metrics/prometheus/collector_test.go +++ /dev/null @@ -1,65 +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 prometheus - -import ( - "fmt" - "os" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/metrics/internal" -) - -func TestMain(m *testing.M) { - metrics.Enabled = true - os.Exit(m.Run()) -} - -func TestCollector(t *testing.T) { - var ( - c = newCollector() - want string - ) - internal.ExampleMetrics().Each(func(name string, i interface{}) { - c.Add(name, i) - }) - if wantB, err := os.ReadFile("./testdata/prometheus.want"); err != nil { - t.Fatal(err) - } else { - want = string(wantB) - } - if have := c.buff.String(); have != want { - t.Logf("have\n%v", have) - t.Logf("have vs want:\n%v", findFirstDiffPos(have, want)) - t.Fatalf("unexpected collector output") - } -} - -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 dbdeae6c7f..0000000000 --- a/metrics/prometheus/prometheus.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 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 prometheus exposes go-metrics into a Prometheus format. -package prometheus - -import ( - "fmt" - "net/http" - "sort" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" -) - -// Handler returns an HTTP handler which dump metrics in Prometheus format. -func Handler(reg metrics.Registry) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Gather and pre-sort the metrics to avoid random listings - var names []string - reg.Each(func(name string, i interface{}) { - names = append(names, name) - }) - sort.Strings(names) - - // Aggregate all the metrics into a Prometheus collector - c := newCollector() - - for _, name := range names { - i := reg.Get(name) - if err := c.Add(name, i); err != nil { - log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i)) - } - } - w.Header().Add("Content-Type", "text/plain") - w.Header().Add("Content-Length", fmt.Sprint(c.buff.Len())) - w.Write(c.buff.Bytes()) - }) -} 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/runtimehistogram.go b/metrics/runtimehistogram.go deleted file mode 100644 index 92fcbcc281..0000000000 --- a/metrics/runtimehistogram.go +++ /dev/null @@ -1,301 +0,0 @@ -package metrics - -import ( - "math" - "runtime/metrics" - "sort" - "sync/atomic" -) - -func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram { - if r == nil { - r = DefaultRegistry - } - constructor := func() Histogram { return newRuntimeHistogram(scale) } - return r.GetOrRegister(name, constructor).(*runtimeHistogram) -} - -// runtimeHistogram wraps a runtime/metrics histogram. -type runtimeHistogram struct { - v atomic.Value // v is a pointer to a metrics.Float64Histogram - scaleFactor float64 -} - -func newRuntimeHistogram(scale float64) *runtimeHistogram { - h := &runtimeHistogram{scaleFactor: scale} - h.update(new(metrics.Float64Histogram)) - return h -} - -func RuntimeHistogramFromData(scale float64, hist *metrics.Float64Histogram) *runtimeHistogram { - h := &runtimeHistogram{scaleFactor: scale} - h.update(hist) - return h -} - -func (h *runtimeHistogram) update(mh *metrics.Float64Histogram) { - if mh == nil { - // The update value can be nil if the current Go version doesn't support a - // requested metric. It's just easier to handle nil here than putting - // conditionals everywhere. - return - } - - s := metrics.Float64Histogram{ - Counts: make([]uint64, len(mh.Counts)), - Buckets: make([]float64, len(mh.Buckets)), - } - copy(s.Counts, mh.Counts) - for i, b := range mh.Buckets { - s.Buckets[i] = b * h.scaleFactor - } - h.v.Store(&s) -} - -func (h *runtimeHistogram) Clear() { - panic("runtimeHistogram does not support Clear") -} -func (h *runtimeHistogram) Update(int64) { - panic("runtimeHistogram does not support Update") -} - -// Snapshot returns a non-changing copy of the histogram. -func (h *runtimeHistogram) Snapshot() HistogramSnapshot { - hist := h.v.Load().(*metrics.Float64Histogram) - return newRuntimeHistogramSnapshot(hist) -} - -type runtimeHistogramSnapshot struct { - internal *metrics.Float64Histogram - calculated bool - // The following fields are (lazily) calculated based on 'internal' - mean float64 - count int64 - min int64 // min is the lowest sample value. - max int64 // max is the highest sample value. - variance float64 -} - -func newRuntimeHistogramSnapshot(h *metrics.Float64Histogram) *runtimeHistogramSnapshot { - return &runtimeHistogramSnapshot{ - internal: h, - } -} - -// calc calculates the values for the snapshot. This method is not threadsafe. -func (h *runtimeHistogramSnapshot) calc() { - h.calculated = true - var ( - count int64 // number of samples - sum float64 // approx sum of all sample values - min int64 - max float64 - ) - if len(h.internal.Counts) == 0 { - return - } - for i, c := range h.internal.Counts { - if c == 0 { - continue - } - if count == 0 { // Set min only first loop iteration - min = int64(math.Floor(h.internal.Buckets[i])) - } - count += int64(c) - sum += h.midpoint(i) * float64(c) - // Set max on every iteration - edge := h.internal.Buckets[i+1] - if math.IsInf(edge, 1) { - edge = h.internal.Buckets[i] - } - if edge > max { - max = edge - } - } - h.min = min - h.max = int64(max) - h.mean = sum / float64(count) - h.count = count -} - -// Count returns the sample count. -func (h *runtimeHistogramSnapshot) Count() int64 { - if !h.calculated { - h.calc() - } - return h.count -} - -// Size returns the size of the sample at the time the snapshot was taken. -func (h *runtimeHistogramSnapshot) Size() int { - return len(h.internal.Counts) -} - -// Mean returns an approximation of the mean. -func (h *runtimeHistogramSnapshot) Mean() float64 { - if !h.calculated { - h.calc() - } - return h.mean -} - -func (h *runtimeHistogramSnapshot) midpoint(bucket int) float64 { - high := h.internal.Buckets[bucket+1] - low := h.internal.Buckets[bucket] - if math.IsInf(high, 1) { - // The edge of the highest bucket can be +Inf, and it's supposed to mean that this - // bucket contains all remaining samples > low. We can't get the middle of an - // infinite range, so just return the lower bound of this bucket instead. - return low - } - if math.IsInf(low, -1) { - // Similarly, we can get -Inf in the left edge of the lowest bucket, - // and it means the bucket contains all remaining values < high. - return high - } - return (low + high) / 2 -} - -// StdDev approximates the standard deviation of the histogram. -func (h *runtimeHistogramSnapshot) StdDev() float64 { - return math.Sqrt(h.Variance()) -} - -// Variance approximates the variance of the histogram. -func (h *runtimeHistogramSnapshot) Variance() float64 { - if len(h.internal.Counts) == 0 { - return 0 - } - if !h.calculated { - h.calc() - } - if h.count <= 1 { - // There is no variance when there are zero or one items. - return 0 - } - // Variance is not calculated in 'calc', because it requires a second iteration. - // Therefore we calculate it lazily in this method, triggered either by - // a direct call to Variance or via StdDev. - if h.variance != 0.0 { - return h.variance - } - var sum float64 - - for i, c := range h.internal.Counts { - midpoint := h.midpoint(i) - d := midpoint - h.mean - sum += float64(c) * (d * d) - } - h.variance = sum / float64(h.count-1) - return h.variance -} - -// Percentile computes the p'th percentile value. -func (h *runtimeHistogramSnapshot) Percentile(p float64) float64 { - threshold := float64(h.Count()) * p - values := [1]float64{threshold} - h.computePercentiles(values[:]) - return values[0] -} - -// Percentiles computes all requested percentile values. -func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 { - // Compute threshold values. We need these to be sorted - // for the percentile computation, but restore the original - // order later, so keep the indexes as well. - count := float64(h.Count()) - thresholds := make([]float64, len(ps)) - indexes := make([]int, len(ps)) - for i, percentile := range ps { - thresholds[i] = count * math.Max(0, math.Min(1.0, percentile)) - indexes[i] = i - } - sort.Sort(floatsAscendingKeepingIndex{thresholds, indexes}) - - // Now compute. The result is stored back into the thresholds slice. - h.computePercentiles(thresholds) - - // Put the result back into the requested order. - sort.Sort(floatsByIndex{thresholds, indexes}) - return thresholds -} - -func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) { - var totalCount float64 - for i, count := range h.internal.Counts { - totalCount += float64(count) - - for len(thresh) > 0 && thresh[0] < totalCount { - thresh[0] = h.internal.Buckets[i] - thresh = thresh[1:] - } - if len(thresh) == 0 { - return - } - } -} - -// Note: runtime/metrics.Float64Histogram is a collection of float64s, but the methods -// below need to return int64 to satisfy the interface. The histogram provided by runtime -// also doesn't keep track of individual samples, so results are approximated. - -// Max returns the highest sample value. -func (h *runtimeHistogramSnapshot) Max() int64 { - if !h.calculated { - h.calc() - } - return h.max -} - -// Min returns the lowest sample value. -func (h *runtimeHistogramSnapshot) Min() int64 { - if !h.calculated { - h.calc() - } - return h.min -} - -// Sum returns the sum of all sample values. -func (h *runtimeHistogramSnapshot) Sum() int64 { - var sum float64 - for i := range h.internal.Counts { - sum += h.internal.Buckets[i] * float64(h.internal.Counts[i]) - } - return int64(math.Ceil(sum)) -} - -type floatsAscendingKeepingIndex struct { - values []float64 - indexes []int -} - -func (s floatsAscendingKeepingIndex) Len() int { - return len(s.values) -} - -func (s floatsAscendingKeepingIndex) Less(i, j int) bool { - return s.values[i] < s.values[j] -} - -func (s floatsAscendingKeepingIndex) Swap(i, j int) { - s.values[i], s.values[j] = s.values[j], s.values[i] - s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i] -} - -type floatsByIndex struct { - values []float64 - indexes []int -} - -func (s floatsByIndex) Len() int { - return len(s.values) -} - -func (s floatsByIndex) Less(i, j int) bool { - return s.indexes[i] < s.indexes[j] -} - -func (s floatsByIndex) Swap(i, j int) { - s.values[i], s.values[j] = s.values[j], s.values[i] - s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i] -} diff --git a/metrics/runtimehistogram_test.go b/metrics/runtimehistogram_test.go deleted file mode 100644 index cf7e36420a..0000000000 --- a/metrics/runtimehistogram_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package metrics - -import ( - "bytes" - "encoding/gob" - "fmt" - "math" - "reflect" - "runtime/metrics" - "testing" - "time" -) - -var _ Histogram = (*runtimeHistogram)(nil) - -type runtimeHistogramTest struct { - h metrics.Float64Histogram - - Count int64 - Min int64 - Max int64 - Sum int64 - Mean float64 - Variance float64 - StdDev float64 - Percentiles []float64 // .5 .8 .9 .99 .995 -} - -// This test checks the results of statistical functions implemented -// by runtimeHistogramSnapshot. -func TestRuntimeHistogramStats(t *testing.T) { - tests := []runtimeHistogramTest{ - 0: { - h: metrics.Float64Histogram{ - Counts: []uint64{}, - Buckets: []float64{}, - }, - Count: 0, - Max: 0, - Min: 0, - Sum: 0, - Mean: 0, - Variance: 0, - StdDev: 0, - Percentiles: []float64{0, 0, 0, 0, 0}, - }, - 1: { - // This checks the case where the highest bucket is +Inf. - h: metrics.Float64Histogram{ - Counts: []uint64{0, 1, 2}, - Buckets: []float64{0, 0.5, 1, math.Inf(1)}, - }, - Count: 3, - Max: 1, - Min: 0, - Sum: 3, - Mean: 0.9166666, - Percentiles: []float64{1, 1, 1, 1, 1}, - Variance: 0.020833, - StdDev: 0.144433, - }, - 2: { - h: metrics.Float64Histogram{ - Counts: []uint64{8, 6, 3, 1}, - Buckets: []float64{12, 16, 18, 24, 25}, - }, - Count: 18, - Max: 25, - Min: 12, - Sum: 270, - Mean: 16.75, - Variance: 10.3015, - StdDev: 3.2096, - Percentiles: []float64{16, 18, 18, 24, 24}, - }, - } - - for i, test := range tests { - t.Run(fmt.Sprint(i), func(t *testing.T) { - s := RuntimeHistogramFromData(1.0, &test.h).Snapshot() - - if v := s.Count(); v != test.Count { - t.Errorf("Count() = %v, want %v", v, test.Count) - } - if v := s.Min(); v != test.Min { - t.Errorf("Min() = %v, want %v", v, test.Min) - } - if v := s.Max(); v != test.Max { - t.Errorf("Max() = %v, want %v", v, test.Max) - } - if v := s.Sum(); v != test.Sum { - t.Errorf("Sum() = %v, want %v", v, test.Sum) - } - if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) { - t.Errorf("Mean() = %v, want %v", v, test.Mean) - } - if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) { - t.Errorf("Variance() = %v, want %v", v, test.Variance) - } - if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) { - t.Errorf("StdDev() = %v, want %v", v, test.StdDev) - } - ps := []float64{.5, .8, .9, .99, .995} - if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) { - t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles) - } - }) - } -} - -func approxEqual(x, y, ε float64) bool { - if math.IsInf(x, -1) && math.IsInf(y, -1) { - return true - } - if math.IsInf(x, 1) && math.IsInf(y, 1) { - return true - } - if math.IsNaN(x) && math.IsNaN(y) { - return true - } - return math.Abs(x-y) < ε -} - -// This test verifies that requesting Percentiles in unsorted order -// returns them in the requested order. -func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) { - s := RuntimeHistogramFromData(1.0, &metrics.Float64Histogram{ - Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }).Snapshot() - result := s.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2}) - expected := []float64{10, 2, 5, 1, 2} - if !reflect.DeepEqual(result, expected) { - t.Fatal("wrong result:", result) - } -} - -func BenchmarkRuntimeHistogramSnapshotRead(b *testing.B) { - var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00" - - dserialize := func(data string) *metrics.Float64Histogram { - var res metrics.Float64Histogram - if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil { - panic(err) - } - return &res - } - latency := RuntimeHistogramFromData(float64(time.Second), dserialize(sLatency)) - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - snap := latency.Snapshot() - // These are the fields that influxdb accesses - _ = snap.Count() - _ = snap.Max() - _ = snap.Mean() - _ = snap.Min() - _ = snap.StdDev() - _ = snap.Variance() - _ = snap.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) - } -} diff --git a/metrics/sample.go b/metrics/sample.go deleted file mode 100644 index 5398dd42d5..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) {} - -// SamplePercentiles 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 c4ae91e642..0000000000 --- a/metrics/validate.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/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() - } - } -}