Skip to content

Commit

Permalink
Add automatic Conter64 wrapping
Browse files Browse the repository at this point in the history
Automatically wrap Counter64 PDU values every 2^53 to avoid float64
precision loss.

Add a command line flag to enable/disable this feature, enable by
default.

Signed-off-by: Ben Kochie <[email protected]>
  • Loading branch information
SuperQ committed Nov 2, 2018
1 parent 5f4dfb4 commit 3d47144
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,10 @@ scrape_configs:
This setup allows Prometheus to provide scheduling and service discovery, as
unlike all other exporters running an exporter on the machine from which we are
getting the metrics from is not possible.
## Large counter value handling
In order to provide accurate counters for large Counter64 values, the exporter will automatically
wrap the value every 2^53 to avoid 64-bit float rounding.
To disable this feature, use the command line flag `--no-wrap-large-counters`.
7 changes: 6 additions & 1 deletion collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@ PduLoop:
func getPduValue(pdu *gosnmp.SnmpPDU) float64 {
switch pdu.Type {
case gosnmp.Counter64:
return float64(gosnmp.ToBigInt(pdu.Value).Uint64())
if *wrapCounters {
// Wrap by 2^53
return float64(gosnmp.ToBigInt(pdu.Value).Uint64() % 9007199254740992)
} else {
return float64(gosnmp.ToBigInt(pdu.Value).Uint64())
}
case gosnmp.OpaqueFloat:
return float64(pdu.Value.(float32))
case gosnmp.OpaqueDouble:
Expand Down
34 changes: 34 additions & 0 deletions collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (

"github.com/prometheus/client_model/go"
"github.com/soniah/gosnmp"
"github.com/prometheus/common/log"
kingpin "gopkg.in/alecthomas/kingpin.v2"

"github.com/prometheus/snmp_exporter/config"
)
Expand Down Expand Up @@ -462,6 +464,38 @@ func TestGetPduValue(t *testing.T) {
}
}

func TestGetPduLargeValue(t *testing.T) {
// Setup default flags and suppress logging.
log.AddFlags(kingpin.CommandLine)
_, err := kingpin.CommandLine.Parse([]string{"--log.level", "fatal"})
if err != nil {
t.Fatal(err)
}

pdu := &gosnmp.SnmpPDU{
Value: uint64(19007199254740992),
Type: gosnmp.Counter64,
}
value := getPduValue(pdu)
if value != 992800745259008.0 {
t.Fatalf("Got incorrect counter wrapping for Counter64: %v", value)
}

_, err = kingpin.CommandLine.Parse([]string{"--log.level", "fatal", "--no-wrap-large-counters"})
if err != nil {
t.Fatal(err)
}

pdu = &gosnmp.SnmpPDU{
Value: uint64(19007199254740992),
Type: gosnmp.Counter64,
}
value = getPduValue(pdu)
if value != 19007199254740990.0 {
t.Fatalf("Got incorrect rounded float for Counter64: %v", value)
}
}

func TestOidToList(t *testing.T) {
cases := []struct {
oid string
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
var (
configFile = kingpin.Flag("config.file", "Path to configuration file.").Default("snmp.yml").String()
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9116").String()
wrapCounters = kingpin.Flag("wrap-large-counters", "Wrap 64-bit counters to avoid floating point rounding.").Default("true").Bool()

// Metrics about the SNMP exporter itself.
snmpDuration = prometheus.NewSummaryVec(
Expand Down

0 comments on commit 3d47144

Please sign in to comment.