github.com/ergo-services/ergo@v1.999.224/apps/system/metrics.go (about) 1 package system 2 3 import ( 4 "crypto/rand" 5 "crypto/rsa" 6 "crypto/sha256" 7 "crypto/x509" 8 "encoding/base64" 9 "encoding/binary" 10 "net" 11 "runtime" 12 "time" 13 14 "github.com/ergo-services/ergo/etf" 15 "github.com/ergo-services/ergo/gen" 16 "github.com/ergo-services/ergo/lib" 17 "github.com/ergo-services/ergo/lib/osdep" 18 "github.com/ergo-services/ergo/node" 19 ) 20 21 var ( 22 defaultMetricsPeriod = time.Minute 23 ) 24 25 type systemMetrics struct { 26 gen.Server 27 } 28 29 type systemMetricsState struct { 30 // gather last 10 stats 31 stats [10]nodeFullStats 32 i int 33 } 34 type messageSystemAnonInfo struct{} 35 type messageSystemGatherStats struct{} 36 37 type nodeFullStats struct { 38 timestamp int64 39 utime int64 40 stime int64 41 42 memAlloc uint64 43 memTotalAlloc uint64 44 memFrees uint64 45 memSys uint64 46 memNumGC uint32 47 48 node node.NodeStats 49 network []node.NetworkStats 50 } 51 52 func (sb *systemMetrics) Init(process *gen.ServerProcess, args ...etf.Term) error { 53 lib.Log("[%s] SYSTEM_METRICS: Init: %#v", process.NodeName(), args) 54 if err := RegisterTypes(); err != nil { 55 return err 56 } 57 options := args[0].(node.System) 58 process.State = &systemMetricsState{} 59 if options.DisableAnonMetrics == false { 60 process.CastAfter(process.Self(), messageSystemAnonInfo{}, defaultMetricsPeriod) 61 } 62 process.CastAfter(process.Self(), messageSystemGatherStats{}, defaultMetricsPeriod) 63 return nil 64 } 65 66 func (sb *systemMetrics) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus { 67 lib.Log("[%s] SYSTEM_METRICS: HandleCast: %#v", process.NodeName(), message) 68 state := process.State.(*systemMetricsState) 69 switch message.(type) { 70 case messageSystemAnonInfo: 71 ver := process.Env(node.EnvKeyVersion).(node.Version) 72 sendAnonInfo(process.NodeName(), ver) 73 74 case messageSystemGatherStats: 75 stats := gatherStats(process) 76 if state.i > len(state.stats)-1 { 77 state.i = 0 78 } 79 state.stats[state.i] = stats 80 state.i++ 81 process.CastAfter(process.Self(), messageSystemGatherStats{}, defaultMetricsPeriod) 82 } 83 return gen.ServerStatusOK 84 } 85 86 func (sb *systemMetrics) Terminate(process *gen.ServerProcess, reason string) { 87 lib.Log("[%s] SYSTEM_METRICS: Terminate with reason %q", process.NodeName(), reason) 88 } 89 90 // private routines 91 92 func sendAnonInfo(name string, ver node.Version) { 93 metricsHost := "metrics.ergo.services" 94 95 values, err := net.LookupTXT(metricsHost) 96 if err != nil || len(values) == 0 { 97 return 98 } 99 100 v, err := base64.StdEncoding.DecodeString(values[0]) 101 if err != nil { 102 return 103 } 104 105 pk, err := x509.ParsePKCS1PublicKey([]byte(v)) 106 if err != nil { 107 return 108 } 109 110 c, err := net.Dial("udp", metricsHost+":4411") 111 if err != nil { 112 return 113 } 114 defer c.Close() 115 116 // FIXME get it back before the release 117 // nameHash := crc32.Checksum([]byte(name), lib.CRC32Q) 118 nameHash := name 119 120 b := lib.TakeBuffer() 121 defer lib.ReleaseBuffer(b) 122 123 message := MessageSystemAnonMetrics{ 124 Name: nameHash, 125 Arch: runtime.GOARCH, 126 OS: runtime.GOOS, 127 NumCPU: runtime.NumCPU(), 128 GoVersion: runtime.Version(), 129 ErgoVersion: ver.Release, 130 } 131 if err := etf.Encode(message, b, etf.EncodeOptions{}); err != nil { 132 return 133 } 134 135 hash := sha256.New() 136 cipher, err := rsa.EncryptOAEP(hash, rand.Reader, pk, b.B, nil) 137 if err != nil { 138 return 139 } 140 141 // 2 (magic: 1144) + 2 (length) + len(cipher) 142 b.Reset() 143 b.Allocate(4) 144 b.Append(cipher) 145 binary.BigEndian.PutUint16(b.B[0:2], uint16(1144)) 146 binary.BigEndian.PutUint16(b.B[2:4], uint16(len(cipher))) 147 c.Write(b.B) 148 } 149 150 func gatherStats(process *gen.ServerProcess) nodeFullStats { 151 fullStats := nodeFullStats{} 152 153 // CPU (windows doesn't support this feature) 154 fullStats.utime, fullStats.stime = osdep.ResourceUsage() 155 156 // Memory 157 mem := runtime.MemStats{} 158 runtime.ReadMemStats(&mem) 159 fullStats.memAlloc = mem.Alloc 160 fullStats.memTotalAlloc = mem.TotalAlloc 161 fullStats.memSys = mem.Sys 162 fullStats.memFrees = mem.Frees 163 fullStats.memNumGC = mem.NumGC 164 165 // Network 166 node := process.Env(node.EnvKeyNode).(node.Node) 167 for _, name := range node.Nodes() { 168 ns, err := node.NetworkStats(name) 169 if err != nil { 170 continue 171 } 172 fullStats.network = append(fullStats.network, ns) 173 } 174 175 fullStats.node = node.Stats() 176 fullStats.timestamp = time.Now().Unix() 177 return fullStats 178 }