Files
zeekay 7cf87b230b node-unified is the node
The module already called itself github.com/luxfi/node while containing no node
in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and
the Go column of the matrix measured a VM plugin. A repo that fetches the thing
it claims to be has not replaced it, so the Go sources move in — 1515 files,
the daemon and everything under it — and the build reaches outside this module
for nothing.

Rust and C++ sit beside it unchanged and are still measured against it by the
differential. That is the point of one repo: the reference implementation and
the two ports are the same checkout, so parity is a thing you can run rather
than a thing you assert.

Verified: builds, and the closure carries no lux-private — checked at build time
rather than believed. The daemon starts twelve chains on a local network
(P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
2026-09-07 16:15:25 -07:00

190 lines
4.7 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package metric
import (
"errors"
"fmt"
"slices"
"strings"
"github.com/luxfi/metric"
. "github.com/luxfi/node/vms/evm/metrics"
)
var (
_ metric.Gatherer = (*Gatherer)(nil)
errMetricSkip = errors.New("metric skipped")
errMetricTypeNotSupported = errors.New("metric type is not supported")
quantiles = []float64{.5, .75, .95, .99, .999, .9999}
pvShortPercent = []float64{50, 95, 99}
)
// Gatherer implements the [metric.Gatherer] interface by gathering all
// metrics from a [Registry].
type Gatherer struct {
registry Registry
}
// Gather gathers metrics from the registry and converts them to
// a slice of metric families.
func (g *Gatherer) Gather() ([]*metric.MetricFamily, error) {
// Gather and pre-sort the metrics to avoid random listings
var names []string
g.registry.Each(func(name string, _ any) {
names = append(names, name)
})
slices.Sort(names)
var (
mfs = make([]*metric.MetricFamily, 0, len(names))
errs []error
)
for _, name := range names {
mf, err := metricFamily(g.registry, name)
switch {
case err == nil:
mfs = append(mfs, mf)
case !errors.Is(err, errMetricSkip):
errs = append(errs, err)
}
}
return mfs, errors.Join(errs...)
}
// NewGatherer returns a [Gatherer] using the given registry.
func NewGatherer(registry Registry) *Gatherer {
return &Gatherer{
registry: registry,
}
}
func metricFamily(registry Registry, name string) (mf *metric.MetricFamily, err error) {
m := registry.Get(name)
name = strings.ReplaceAll(name, "/", "_")
switch mt := m.(type) {
case Counter:
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeCounter,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
Value: float64(mt.Snapshot().Count()),
},
}},
}, nil
case CounterFloat64:
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeCounter,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
Value: mt.Snapshot().Count(),
},
}},
}, nil
case Gauge:
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeGauge,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
Value: float64(mt.Snapshot().Value()),
},
}},
}, nil
case GaugeFloat64:
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeGauge,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
Value: mt.Snapshot().Value(),
},
}},
}, nil
case GaugeInfo:
return nil, fmt.Errorf("%w: %q is a %T", errMetricSkip, name, mt)
case Histogram:
snapshot := mt.Snapshot()
thresholds := snapshot.Percentiles(quantiles)
metricQuantiles := make([]metric.Quantile, len(quantiles))
for i := range thresholds {
metricQuantiles[i] = metric.Quantile{
Quantile: quantiles[i],
Value: thresholds[i],
}
}
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeSummary,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
SampleCount: uint64(snapshot.Count()),
SampleSum: float64(snapshot.Sum()),
Quantiles: metricQuantiles,
},
}},
}, nil
case Meter:
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeGauge,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
Value: float64(mt.Snapshot().Count()),
},
}},
}, nil
case Timer:
snapshot := mt.Snapshot()
thresholds := snapshot.Percentiles(quantiles)
metricQuantiles := make([]metric.Quantile, len(quantiles))
for i := range thresholds {
metricQuantiles[i] = metric.Quantile{
Quantile: quantiles[i],
Value: thresholds[i],
}
}
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeSummary,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
SampleCount: uint64(snapshot.Count()),
SampleSum: float64(snapshot.Sum()),
Quantiles: metricQuantiles,
},
}},
}, nil
case ResettingTimer:
snapshot := mt.Snapshot()
thresholds := snapshot.Percentiles(pvShortPercent)
metricQuantiles := make([]metric.Quantile, len(pvShortPercent))
for i := range pvShortPercent {
metricQuantiles[i] = metric.Quantile{
Quantile: pvShortPercent[i],
Value: thresholds[i],
}
}
count := snapshot.Count()
return &metric.MetricFamily{
Name: name,
Type: metric.MetricTypeSummary,
Metrics: []metric.Metric{{
Value: metric.MetricValue{
SampleCount: uint64(count),
SampleSum: float64(count) * snapshot.Mean(),
Quantiles: metricQuantiles,
},
}},
}, nil
default:
return nil, fmt.Errorf("%w: metric %q type %T", errMetricTypeNotSupported, name, m)
}
}