github.com/lukso-network/go-ethereum@v1.8.22/p2p/protocols/reporter.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package protocols 18 19 import ( 20 "encoding/binary" 21 "time" 22 23 "github.com/ethereum/go-ethereum/log" 24 "github.com/ethereum/go-ethereum/metrics" 25 26 "github.com/syndtr/goleveldb/leveldb" 27 ) 28 29 //AccountMetrics abstracts away the metrics DB and 30 //the reporter to persist metrics 31 type AccountingMetrics struct { 32 reporter *reporter 33 } 34 35 //Close will be called when the node is being shutdown 36 //for a graceful cleanup 37 func (am *AccountingMetrics) Close() { 38 close(am.reporter.quit) 39 am.reporter.db.Close() 40 } 41 42 //reporter is an internal structure used to write p2p accounting related 43 //metrics to a LevelDB. It will periodically write the accrued metrics to the DB. 44 type reporter struct { 45 reg metrics.Registry //the registry for these metrics (independent of other metrics) 46 interval time.Duration //duration at which the reporter will persist metrics 47 db *leveldb.DB //the actual DB 48 quit chan struct{} //quit the reporter loop 49 } 50 51 //NewMetricsDB creates a new LevelDB instance used to persist metrics defined 52 //inside p2p/protocols/accounting.go 53 func NewAccountingMetrics(r metrics.Registry, d time.Duration, path string) *AccountingMetrics { 54 var val = make([]byte, 8) 55 var err error 56 57 //Create the LevelDB 58 db, err := leveldb.OpenFile(path, nil) 59 if err != nil { 60 log.Error(err.Error()) 61 return nil 62 } 63 64 //Check for all defined metrics that there is a value in the DB 65 //If there is, assign it to the metric. This means that the node 66 //has been running before and that metrics have been persisted. 67 metricsMap := map[string]metrics.Counter{ 68 "account.balance.credit": mBalanceCredit, 69 "account.balance.debit": mBalanceDebit, 70 "account.bytes.credit": mBytesCredit, 71 "account.bytes.debit": mBytesDebit, 72 "account.msg.credit": mMsgCredit, 73 "account.msg.debit": mMsgDebit, 74 "account.peerdrops": mPeerDrops, 75 "account.selfdrops": mSelfDrops, 76 } 77 //iterate the map and get the values 78 for key, metric := range metricsMap { 79 val, err = db.Get([]byte(key), nil) 80 //until the first time a value is being written, 81 //this will return an error. 82 //it could be beneficial though to log errors later, 83 //but that would require a different logic 84 if err == nil { 85 metric.Inc(int64(binary.BigEndian.Uint64(val))) 86 } 87 } 88 89 //create the reporter 90 rep := &reporter{ 91 reg: r, 92 interval: d, 93 db: db, 94 quit: make(chan struct{}), 95 } 96 97 //run the go routine 98 go rep.run() 99 100 m := &AccountingMetrics{ 101 reporter: rep, 102 } 103 104 return m 105 } 106 107 //run is the goroutine which periodically sends the metrics to the configured LevelDB 108 func (r *reporter) run() { 109 intervalTicker := time.NewTicker(r.interval) 110 111 for { 112 select { 113 case <-intervalTicker.C: 114 //at each tick send the metrics 115 if err := r.save(); err != nil { 116 log.Error("unable to send metrics to LevelDB", "err", err) 117 //If there is an error in writing, exit the routine; we assume here that the error is 118 //severe and don't attempt to write again. 119 //Also, this should prevent leaking when the node is stopped 120 return 121 } 122 case <-r.quit: 123 //graceful shutdown 124 return 125 } 126 } 127 } 128 129 //send the metrics to the DB 130 func (r *reporter) save() error { 131 //create a LevelDB Batch 132 batch := leveldb.Batch{} 133 //for each metric in the registry (which is independent)... 134 r.reg.Each(func(name string, i interface{}) { 135 metric, ok := i.(metrics.Counter) 136 if ok { 137 //assuming every metric here to be a Counter (separate registry) 138 //...create a snapshot... 139 ms := metric.Snapshot() 140 byteVal := make([]byte, 8) 141 binary.BigEndian.PutUint64(byteVal, uint64(ms.Count())) 142 //...and save the value to the DB 143 batch.Put([]byte(name), byteVal) 144 } 145 }) 146 return r.db.Write(&batch, nil) 147 }