go.dedis.ch/onet/v4@v4.0.0-pre1/simul/monitor/monitor.go (about) 1 // Package monitor package handle the logging, collection and computation of 2 // statistical data. Every application can send some Measure (for the moment, 3 // we mostly measure the CPU time but it can be applied later for any kind of 4 // measures). The Monitor receives them and updates a Stats struct. This Stats 5 // struct can hold many different kinds of Measurements (the measure of a 6 // specific action such as "round time" or "verify time" etc). These 7 // measurements contain Values which compute the actual min/max/dev/avg values. 8 // 9 // The Proxy allows to relay Measure from 10 // clients to the listening Monitor. A starter feature is also the DataFilter 11 // which can apply some filtering rules to the data before making any 12 // statistics about them. 13 package monitor 14 15 import ( 16 "encoding/json" 17 "fmt" 18 "io" 19 "net" 20 "strconv" 21 "strings" 22 "sync" 23 24 "go.dedis.ch/onet/v4/log" 25 "golang.org/x/xerrors" 26 ) 27 28 // This file handles the collection of measurements, aggregates them and 29 // write CSV file reports 30 31 // Sink is the address where to listen for the monitor. The endpoint can be a 32 // monitor.Proxy or a direct connection with measure.go 33 const Sink = "0.0.0.0" 34 35 // DefaultSinkPort is the default port where a monitor will listen and a proxy 36 // will contact the monitor. 37 const DefaultSinkPort = 10000 38 39 // Monitor struct is used to collect measures and make the statistics about 40 // them. It takes a stats object so it update that in a concurrent-safe manner 41 // for each new measure it receives. 42 type Monitor struct { 43 listener net.Listener 44 listenerLock *sync.Mutex 45 46 // Current conections 47 conns map[string]net.Conn 48 // and the mutex to play with it 49 mutexConn sync.Mutex 50 51 // Current stats 52 stats *Stats 53 buckets *BucketStats 54 55 // channel to give new measures 56 measures chan *singleMeasure 57 58 // channel to notify the end of a connection 59 // send the name of the connection when finishd 60 done chan string 61 62 SinkPort uint16 63 sinkPortChan chan uint16 64 } 65 66 // NewMonitor returns a new monitor given the stats 67 func NewMonitor(stats *Stats) *Monitor { 68 return &Monitor{ 69 conns: make(map[string]net.Conn), 70 stats: stats, 71 buckets: newBucketStats(), 72 SinkPort: DefaultSinkPort, 73 measures: make(chan *singleMeasure), 74 done: make(chan string), 75 listenerLock: new(sync.Mutex), 76 sinkPortChan: make(chan uint16, 1), 77 } 78 } 79 80 // InsertBucket creates a bucket at the given index that will use the rules 81 // to filter the incoming measures 82 func (m *Monitor) InsertBucket(index int, rules []string, stats *Stats) { 83 m.buckets.Set(index, rules, stats) 84 } 85 86 // Listen will start listening for incoming connections on this address 87 // It needs the stats struct pointer to update when measures come 88 // Return an error if something went wrong during the connection setup 89 func (m *Monitor) Listen() error { 90 ln, err := net.Listen("tcp", Sink+":"+strconv.Itoa(int(m.SinkPort))) 91 if err != nil { 92 return xerrors.Errorf("Error while monitor is binding address: %v", err) 93 } 94 if m.SinkPort == 0 { 95 _, p, _ := net.SplitHostPort(ln.Addr().String()) 96 var p2 uint16 97 fmt.Sscanf(p, "%d", &p2) 98 m.sinkPortChan <- p2 99 } 100 m.listenerLock.Lock() 101 m.listener = ln 102 m.listenerLock.Unlock() 103 log.Lvl2("Monitor listening for stats on", Sink, ":", m.SinkPort) 104 finished := false 105 go func() { 106 for { 107 if finished { 108 break 109 } 110 conn, err := ln.Accept() 111 if err != nil { 112 operr, ok := err.(*net.OpError) 113 // We cant accept anymore we closed the listener 114 if ok && operr.Op == "accept" { 115 break 116 } 117 log.Lvl2("Error while monitor accept connection:", operr) 118 continue 119 } 120 log.Lvl3("Monitor: new connection from", conn.RemoteAddr().String()) 121 m.mutexConn.Lock() 122 m.conns[conn.RemoteAddr().String()] = conn 123 go m.handleConnection(conn) 124 m.mutexConn.Unlock() 125 } 126 }() 127 for !finished { 128 select { 129 // new stats 130 case measure := <-m.measures: 131 m.update(measure) 132 // end of a peer conn 133 case peer := <-m.done: 134 m.mutexConn.Lock() 135 log.Lvl3("Connections left:", len(m.conns)) 136 delete(m.conns, peer) 137 // end of monitoring, 138 if len(m.conns) == 0 { 139 m.listenerLock.Lock() 140 if err := m.listener.Close(); err != nil { 141 log.Lvl2("Couldn't close listener:", 142 err) 143 } 144 m.listener = nil 145 finished = true 146 m.listenerLock.Unlock() 147 } 148 m.mutexConn.Unlock() 149 } 150 } 151 log.Lvl2("Monitor finished waiting") 152 m.mutexConn.Lock() 153 m.conns = make(map[string]net.Conn) 154 m.mutexConn.Unlock() 155 return nil 156 } 157 158 // Stop will close every connections it has 159 // And will stop updating the stats 160 func (m *Monitor) Stop() { 161 log.Lvl2("Monitor Stop") 162 m.listenerLock.Lock() 163 if m.listener != nil { 164 if err := m.listener.Close(); err != nil { 165 log.Error("Couldn't close listener:", err) 166 } 167 } 168 m.listenerLock.Unlock() 169 m.mutexConn.Lock() 170 for _, c := range m.conns { 171 if err := c.Close(); err != nil { 172 log.Error("Couldn't close connection:", err) 173 } 174 } 175 m.mutexConn.Unlock() 176 } 177 178 // handleConnection will decode the data received and aggregates it into its 179 // stats 180 func (m *Monitor) handleConnection(conn net.Conn) { 181 dec := json.NewDecoder(conn) 182 nerr := 0 183 for { 184 measure := &singleMeasure{} 185 if err := dec.Decode(measure); err != nil { 186 // if end of connection 187 if err == io.EOF || strings.Contains(err.Error(), "closed") { 188 break 189 } 190 // otherwise log it 191 log.Lvl2("Error: monitor decoding from", conn.RemoteAddr().String(), ":", err) 192 nerr++ 193 if nerr > 1 { 194 log.Lvl2("Monitor: too many errors from", conn.RemoteAddr().String(), ": Abort.") 195 break 196 } 197 } 198 199 log.Lvlf3("Monitor: received a Measure from %s: %+v", conn.RemoteAddr().String(), measure) 200 // Special case where the measurement is indicating a FINISHED step 201 switch strings.ToLower(measure.Name) { 202 case "end": 203 log.Lvl3("Finishing monitor") 204 break 205 default: 206 m.measures <- measure 207 } 208 } 209 m.done <- conn.RemoteAddr().String() 210 } 211 212 // updateBucket will add that specific measure to all the bucket 213 // that match the network address. 214 func (m *Monitor) update(meas *singleMeasure) { 215 // global stats 216 m.stats.Update(meas) 217 // per bucket stats if defined 218 m.buckets.Update(meas) 219 }