mirror of https://github.com/ethereum/go-ethereum
metrics, cmd/geth: change init-process of metrics (#30814)
This PR modifies how the metrics library handles `Enabled`: previously, the package `init` decided whether to serve real metrics or just dummy-types. This has several drawbacks: - During pkg init, we need to determine whether metrics are enabled or not. So we first hacked in a check if certain geth-specific commandline-flags were enabled. Then we added a similar check for geth-env-vars. Then we almost added a very elaborate check for toml-config-file, plus toml parsing. - Using "real" types and dummy types interchangeably means that everything is hidden behind interfaces. This has a performance penalty, and also it just adds a lot of code. This PR removes the interface stuff, uses concrete types, and allows for the setting of Enabled to happen later. It is still assumed that `metrics.Enable()` is invoked early on. The somewhat 'heavy' operations, such as ticking meters and exp-decay, now checks the enable-flag to prevent resource leak. The change may be large, but it's mostly pretty trivial, and from the last time I gutted the metrics, I ensured that we have fairly good test coverage. --------- Co-authored-by: Felix Lange <fjl@twurst.com>pull/30891/head
parent
4ecf08584c
commit
9045b79bc2
@ -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 |
||||
} |
@ -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}, |
||||
}) |
||||
} |
@ -1,61 +1,35 @@ |
||||
package metrics |
||||
|
||||
// Healthcheck holds an error value describing an arbitrary up/down status.
|
||||
type Healthcheck interface { |
||||
Check() |
||||
Error() error |
||||
Healthy() |
||||
Unhealthy(error) |
||||
} |
||||
|
||||
// NewHealthcheck constructs a new Healthcheck which will use the given
|
||||
// function to update its status.
|
||||
func NewHealthcheck(f func(Healthcheck)) Healthcheck { |
||||
if !Enabled { |
||||
return NilHealthcheck{} |
||||
} |
||||
return &StandardHealthcheck{nil, f} |
||||
func NewHealthcheck(f func(*Healthcheck)) *Healthcheck { |
||||
return &Healthcheck{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
|
||||
// Healthcheck is the standard implementation of a Healthcheck and
|
||||
// stores the status and a function to call to update the status.
|
||||
type StandardHealthcheck struct { |
||||
type Healthcheck struct { |
||||
err error |
||||
f func(Healthcheck) |
||||
f func(*Healthcheck) |
||||
} |
||||
|
||||
// Check runs the healthcheck function to update the healthcheck's status.
|
||||
func (h *StandardHealthcheck) Check() { |
||||
func (h *Healthcheck) Check() { |
||||
h.f(h) |
||||
} |
||||
|
||||
// Error returns the healthcheck's status, which will be nil if it is healthy.
|
||||
func (h *StandardHealthcheck) Error() error { |
||||
func (h *Healthcheck) Error() error { |
||||
return h.err |
||||
} |
||||
|
||||
// Healthy marks the healthcheck as healthy.
|
||||
func (h *StandardHealthcheck) Healthy() { |
||||
func (h *Healthcheck) 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) { |
||||
func (h *Healthcheck) Unhealthy(err error) { |
||||
h.err = err |
||||
} |
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 } |
@ -1,5 +1,5 @@ |
||||
package metrics |
||||
|
||||
func init() { |
||||
Enabled = true |
||||
metricsEnabled = true |
||||
} |
||||
|
Loading…
Reference in new issue