Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ var (
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)

chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)

accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil)
Expand Down Expand Up @@ -278,7 +280,12 @@ func NewBlockChain(db ctxcdb.Database, cacheConfig *CacheConfig, chainConfig *pa
bc.currentFinalizedBlock.Store(nilBlock)
bc.currentSafeBlock.Store(nilBlock)

// Initialize the chain with ancient data if it isn't empty.
// Update chain info data metrics
chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainID.String()})

// If Geth is initialized with an external ancient store, re-initialize the
// missing chain indexes and chain flags. This procedure can survive crash
// and can be resumed in next restart since chain flags are updated in last step.
if bc.empty() {
rawdb.InitDatabaseFromFreezer(bc.db)
}
Expand Down
20 changes: 20 additions & 0 deletions metrics/exp/exp.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ func (exp *exp) getFloat(name string) *expvar.Float {
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.Counter) {
v := exp.getInt(name)
v.Set(metric.Count())
Expand All @@ -113,6 +127,10 @@ func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) {
exp.getFloat(name).Set(metric.Value())
}

func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfo) {
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})
Expand Down Expand Up @@ -178,6 +196,8 @@ func (exp *exp) syncToExpvar() {
exp.publishGauge(name, i)
case metrics.GaugeFloat64:
exp.publishGaugeFloat64(name, i)
case metrics.GaugeInfo:
exp.publishGaugeInfo(name, i)
case metrics.Histogram:
exp.publishHistogram(name, i)
case metrics.Meter:
Expand Down
144 changes: 144 additions & 0 deletions metrics/gauge_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package metrics

import (
"encoding/json"
"sync"
)

// GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily.
type GaugeInfo interface {
Snapshot() GaugeInfo
Update(GaugeInfoValue)
Value() GaugeInfoValue
}

// GaugeInfoValue is a mappng of (string) keys to (string) 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
}

// NewFunctionalGauge constructs a new FunctionalGauge.
func NewFunctionalGaugeInfo(f func() GaugeInfoValue) GaugeInfo {
if !Enabled {
return NilGaugeInfo{}
}
return &FunctionalGaugeInfo{value: f}
}

// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge.
func NewRegisteredFunctionalGaugeInfo(name string, r Registry, f func() GaugeInfoValue) GaugeInfo {
c := NewFunctionalGaugeInfo(f)
if nil == r {
r = DefaultRegistry
}
r.Register(name, c)
return c
}

// GaugeInfoSnapshot is a read-only copy of another GaugeInfo.
type GaugeInfoSnapshot GaugeInfoValue

// Snapshot returns the snapshot.
func (g GaugeInfoSnapshot) Snapshot() GaugeInfo { return g }

// Update panics.
func (GaugeInfoSnapshot) Update(GaugeInfoValue) {
panic("Update called on a GaugeInfoSnapshot")
}

// Value returns the value at the time the snapshot was taken.
func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) }

// NilGauge is a no-op Gauge.
type NilGaugeInfo struct{}

// Snapshot is a no-op.
func (NilGaugeInfo) Snapshot() GaugeInfo { return NilGaugeInfo{} }

// Update is a no-op.
func (NilGaugeInfo) Update(v GaugeInfoValue) {}

// Value is a no-op.
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() GaugeInfo {
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
}

// Value returns the gauge's current value.
func (g *StandardGaugeInfo) Value() GaugeInfoValue {
g.mutex.Lock()
defer g.mutex.Unlock()
return g.value
}

// FunctionalGaugeInfo returns value from given function
type FunctionalGaugeInfo struct {
value func() GaugeInfoValue
}

// Value returns the gauge's current value.
func (g FunctionalGaugeInfo) Value() GaugeInfoValue {
return g.value()
}

// Value returns the gauge's current value in JSON string format
func (g FunctionalGaugeInfo) ValueJsonString() string {
data, _ := json.Marshal(g.value())
return string(data)
}

// Snapshot returns the snapshot.
func (g FunctionalGaugeInfo) Snapshot() GaugeInfo { return GaugeInfoSnapshot(g.Value()) }

// Update panics.
func (FunctionalGaugeInfo) Update(GaugeInfoValue) {
panic("Update called on a FunctionalGaugeInfo")
}
75 changes: 75 additions & 0 deletions metrics/gauge_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package metrics

import (
"strconv"
"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"}`
if have := g.Value().String(); have != want {
t.Errorf("\nhave: %v\nwant: %v\n", have, want)
}
}

func TestGaugeInfoSnapshot(t *testing.T) {
g := NewGaugeInfo()
g.Update(GaugeInfoValue{"value": "original"})
snapshot := g.Snapshot() // Snapshot @chainid 5
g.Update(GaugeInfoValue{"value": "updated"})
// The 'g' should be updated
if have, want := g.Value().String(), `{"value":"updated"}`; have != want {
t.Errorf("\nhave: %v\nwant: %v\n", have, want)
}
// Snapshot should be unupdated
if have, want := snapshot.Value().String(), `{"value":"original"}`; 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)
if have, want := g.Value().String(), `{"chain_id":"5"}`; have != want {
t.Errorf("have\n%v\nwant\n%v\n", have, want)
}
}

func TestFunctionalGaugeInfo(t *testing.T) {
info := GaugeInfoValue{"chain_id": "0"}
counter := 1
// A "functional" gauge invokes the method to obtain the value
fg := NewFunctionalGaugeInfo(func() GaugeInfoValue {
info["chain_id"] = strconv.Itoa(counter)
counter++
return info
})
fg.Value()
fg.Value()
if have, want := info["chain_id"], "2"; have != want {
t.Errorf("have %v want %v", have, want)
}
}

func TestGetOrRegisterFunctionalGaugeInfo(t *testing.T) {
r := NewRegistry()
NewRegisteredFunctionalGaugeInfo("foo", r, func() GaugeInfoValue {
return GaugeInfoValue{
"chain_id": "5",
}
})
want := `{"chain_id":"5"}`
have := GetOrRegisterGaugeInfo("foo", r).Value().String()
if have != want {
t.Errorf("have\n%v\nwant\n%v\n", have, want)
}
}
2 changes: 2 additions & 0 deletions metrics/graphite.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func graphite(c *GraphiteConfig) error {
fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
case GaugeFloat64:
fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
case GaugeInfo:
fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Value().String(), now)
case Histogram:
h := metric.Snapshot()
ps := h.Percentiles(c.Percentiles)
Expand Down
7 changes: 7 additions & 0 deletions metrics/influxdb/influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf
"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 {
Expand Down
Loading