From 510abbab11c6bc9bc3b8728c9425b1d312d9dce0 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 13 Nov 2019 14:58:35 -0500 Subject: [PATCH] Initial MVP --- collector.go | 64 +++++++++++++++++++++ main.go | 135 +++++++++++++++++++++++++++++++++++++++++++++ rpc.go | 27 +++++++++ version/version.go | 23 ++++++++ 4 files changed, 249 insertions(+) create mode 100644 collector.go create mode 100644 main.go create mode 100644 rpc.go create mode 100644 version/version.go diff --git a/collector.go b/collector.go new file mode 100644 index 0000000..cd989c4 --- /dev/null +++ b/collector.go @@ -0,0 +1,64 @@ +package main + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +//Define the metrics we wish to expose +var ( + zcashdBlocks = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_blocks", Help: "the current number of blocks processed in the server"}) + zcashdDifficulty = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_difficulty", Help: "the current difficulty"}) + zcashdSizeOnDisk = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_size_on_disk", Help: "the estimated size of the block and undo files on disk"}) + zcashdVerificationProgress = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_verification_progress", Help: "estimate of verification progress"}) + zcashdMemPoolSize = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_mempool_size", Help: "Current tx count"}) + zcashdMemPoolBytes = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_mempool_bytes", Help: "Sum of all tx sizes"}) + zcashdMemPoolUsage = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zcash_mempool_usage", Help: "Total memory usage for the mempool"}) + zcashdWalletBalance = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "zcash_wallet_balance", + Help: "Node's wallet balance"}, + []string{ + "type", + }) +) + +// ZCASH_PEERS = Gauge("zcash_peers", "Number of peers") +// ZCASH_SOLS = Gauge("zcash_sols", "Estimated network solutions per second") + +// ZCASH_ERRORS = Counter("zcash_errors", "Number of errors detected") + +// ZCASH_LATEST_BLOCK_SIZE = Gauge("zcash_latest_block_size", "Size of latest block in bytes") +// ZCASH_LATEST_BLOCK_TXS = Gauge("zcash_latest_block_txs", "Number of transactions in latest block") + +// ZCASH_CHAINFORK_LOCATION = Gauge("zcash_chainfork_location", "Block height of chain fork") +// ZCASH_CHAINFORK_SIZE = Gauge("zcash_chainfork_size", "Length of chain fork") + +// ZCASH_TOTAL_BYTES_RECV = Gauge("zcash_total_bytes_recv", "Total bytes received") +// ZCASH_TOTAL_BYTES_SENT = Gauge("zcash_total_bytes_sent", "Total bytes sent") + +// ZCASH_LATEST_BLOCK_INPUTS = Gauge("zcash_latest_block_inputs", "Number of inputs in transactions of latest block") +// ZCASH_LATEST_BLOCK_OUTPUTS = Gauge("zcash_latest_block_outputs", "Number of outputs in transactions of latest block") +// ZCASH_LATEST_BLOCK_JOINSPLITS = Gauge("zcash_latest_block_joinsplits", "Number of joinsplits in transactions of latest block") + +// ZCASH_NUM_TRANSPARENT_TX = Gauge("zcash_num_transparent_tx", "Number of fully transparent transactions in latest block") +// ZCASH_NUM_SHIELDED_TX = Gauge("zcash_num_shielded_tx", "Number of fully shielded transactions in latest block") +// ZCASH_NUM_MIXED_TX = Gauge("zcash_num_mixed_tx", "Number of mixed transactions in latest block") + +func init() { + //Register metrics with prometheus + prometheus.MustRegister(zcashdBlocks) + prometheus.MustRegister(zcashdDifficulty) + prometheus.MustRegister(zcashdSizeOnDisk) + prometheus.MustRegister(zcashdVerificationProgress) + prometheus.MustRegister(zcashdMemPoolSize) + prometheus.MustRegister(zcashdMemPoolBytes) + prometheus.MustRegister(zcashdMemPoolUsage) + prometheus.MustRegister(zcashdWalletBalance) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..25065cc --- /dev/null +++ b/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "encoding/base64" + "net/http" + "strconv" + "time" + + log "github.com/Sirupsen/logrus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/ybbus/jsonrpc" + "github.com/zcash-hackworks/zcashd_exporter/version" + "gopkg.in/alecthomas/kingpin.v2" +) + +var ( + listenAddress = kingpin.Flag( + "web.listen-address", + "Address on which to expose metrics and web interface.", + ).Default(":9100").String() + rpcHost = kingpin.Flag( + "rpc.host", + "Host address for RPC endpoint.", + ).Default("127.0.0.1").String() + rpcPort = kingpin.Flag( + "rpc.port", + "Post for RPC endpoint", + ).Default("18232").String() + rpcUser = kingpin.Flag( + "rpc.user", + "User for RPC endpoint auth.", + ).Default("zcashrpc").String() + rpcPassword = kingpin.Flag( + "rpc.password", + "Password for RPC endpoint auth.", + ).Default("notsecure").String() +) + +func main() { + kingpin.HelpFlag.Short('h') + kingpin.Parse() + log.Infoln("Starting zcashd_exporter", version.Info()) + log.Infoln("Build context", version.BuildContext()) + + http.Handle("/metrics", promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + Zcashd Exporter + +

Zcashd Exporter

+

Metrics

+ + `)) + }) + go getBlockchainInfo() + go getMemPoolInfo() + go getWalletInfo() + log.Infoln("Listening on", *listenAddress) + if err := http.ListenAndServe(*listenAddress, nil); err != nil { + log.Fatal(err) + } + +} + +func getBlockchainInfo() { + basicAuth := base64.StdEncoding.EncodeToString([]byte(*rpcUser + ":" + *rpcPassword)) + rpcClient := jsonrpc.NewClientWithOpts("http://"+*rpcHost+":"+*rpcPort, + &jsonrpc.RPCClientOpts{ + CustomHeaders: map[string]string{ + "Authorization": "Basic " + basicAuth, + }}) + var blockinfo *GetBlockchainInfo + + for { + if err := rpcClient.CallFor(&blockinfo, "getblockchaininfo"); err != nil { + log.Warnln("Error calling getblockchaininfo", err) + } else { + zcashdBlocks.Set(float64(blockinfo.Blocks)) + zcashdDifficulty.Set(blockinfo.Difficulty) + zcashdVerificationProgress.Set(blockinfo.VerificationProgress) + } + time.Sleep(time.Duration(30) * time.Second) + } + +} + +func getMemPoolInfo() { + basicAuth := base64.StdEncoding.EncodeToString([]byte(*rpcUser + ":" + *rpcPassword)) + rpcClient := jsonrpc.NewClientWithOpts("http://"+*rpcHost+":"+*rpcPort, + &jsonrpc.RPCClientOpts{ + CustomHeaders: map[string]string{ + "Authorization": "Basic " + basicAuth, + }}) + var mempoolinfo *GetMemPoolInfo + + for { + if err := rpcClient.CallFor(&mempoolinfo, "getmempoolinfo"); err != nil { + log.Warnln("Error calling getmempoolinfo", err) + } else { + zcashdMemPoolSize.Set(float64(mempoolinfo.Size)) + zcashdMemPoolBytes.Set(mempoolinfo.Bytes) + zcashdMemPoolUsage.Set(mempoolinfo.Usage) + } + time.Sleep(time.Duration(30) * time.Second) + } + +} + +func getWalletInfo() { + basicAuth := base64.StdEncoding.EncodeToString([]byte(*rpcUser + ":" + *rpcPassword)) + rpcClient := jsonrpc.NewClientWithOpts("http://"+*rpcHost+":"+*rpcPort, + &jsonrpc.RPCClientOpts{ + CustomHeaders: map[string]string{ + "Authorization": "Basic " + basicAuth, + }}) + var walletinfo *ZGetTotalBalance + + for { + if err := rpcClient.CallFor(&walletinfo, "z_gettotalbalance"); err != nil { + log.Warnln("Error calling z_gettotalbalance", err) + } else { + if t, err := strconv.ParseFloat(walletinfo.Transparent, 64); err == nil { + zcashdWalletBalance.WithLabelValues("transparent").Set(t) + } + if p, err := strconv.ParseFloat(walletinfo.Private, 64); err == nil { + zcashdWalletBalance.WithLabelValues("private").Set(p) + } + if total, err := strconv.ParseFloat(walletinfo.Total, 64); err == nil { + zcashdWalletBalance.WithLabelValues("total").Set(total) + } + } + time.Sleep(time.Duration(30) * time.Second) + } + +} diff --git a/rpc.go b/rpc.go new file mode 100644 index 0000000..4ea6496 --- /dev/null +++ b/rpc.go @@ -0,0 +1,27 @@ +package main + +// GetBlockchainInfo return the zcashd rpc `getblockchaininfo` status +// https://zcash-rpc.github.io/getblockchaininfo.html +type GetBlockchainInfo struct { + Chain string `json:"chain"` + Blocks int `json:"blocks"` + Difficulty float64 `json:"difficulty"` + VerificationProgress float64 `json:"verificationprogress"` + SizeOnDisk float64 `json:"size_on_disk"` +} + +// GetMemPoolInfo return the zcashd rpc `getmempoolinfo` +// https://zcash-rpc.github.io/getmempoolinfo.html +type GetMemPoolInfo struct { + Size float64 `json:"size"` + Bytes float64 `json:"bytes"` + Usage float64 `json:"usage"` +} + +// ZGetTotalBalance return the node's wallet balances +// https://zcash-rpc.github.io/z_gettotalbalance.html +type ZGetTotalBalance struct { + Transparent string `json:"transparent"` + Private string `json:"private"` + Total string `json:"total"` +} diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000..4247529 --- /dev/null +++ b/version/version.go @@ -0,0 +1,23 @@ +package version + +import ( + "fmt" + "runtime" +) + +var ( + Version string + Revision string + Branch string + BuildUser string + BuildDate string + GoVersion = runtime.Version() +) + +func Info() string { + return fmt.Sprintf("(version=%s, branch=%s, revision=%s)", Version, Branch, Revision) +} + +func BuildContext() string { + return fmt.Sprintf("(go=%s, user=%s, date=%s)", GoVersion, BuildUser, BuildDate) +}