github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/statslogger/config.go (about) 1 package statslogger 2 3 import ( 4 "runtime" 5 "strings" 6 "time" 7 8 "github.com/swiftstack/ProxyFS/bucketstats" 9 "github.com/swiftstack/ProxyFS/conf" 10 "github.com/swiftstack/ProxyFS/logger" 11 "github.com/swiftstack/ProxyFS/stats" 12 "github.com/swiftstack/ProxyFS/swiftclient" 13 "github.com/swiftstack/ProxyFS/transitions" 14 ) 15 16 type globalsStruct struct { 17 collectChan <-chan time.Time // time to collect swiftclient stats 18 logChan <-chan time.Time // time to log statistics 19 stopChan chan bool // time to shutdown and go home 20 doneChan chan bool // shutdown complete 21 statsLogPeriod time.Duration // time between statistics logging 22 verbose bool // verbosity of logging 23 collectTicker *time.Ticker // ticker for collectChan (if any) 24 logTicker *time.Ticker // ticker for logChan (if any) 25 } 26 27 var globals globalsStruct 28 29 func init() { 30 transitions.Register("statslogger", &globals) 31 } 32 33 func parseConfMap(confMap conf.ConfMap) (err error) { 34 globals.statsLogPeriod, err = confMap.FetchOptionValueDuration("StatsLogger", "Period") 35 if err != nil { 36 logger.Warnf("config variable 'StatsLogger.Period' defaulting to '10m': %v", err) 37 globals.statsLogPeriod = time.Duration(10 * time.Minute) 38 } 39 40 // statsLogPeriod must be >= 1 sec, except 0 means disabled 41 if globals.statsLogPeriod < time.Second && globals.statsLogPeriod != 0 { 42 logger.Warnf("config variable 'StatsLogger.Period' value is non-zero and less then 1 min; defaulting to '10m'") 43 globals.statsLogPeriod = time.Duration(10 * time.Minute) 44 } 45 46 globals.verbose, err = confMap.FetchOptionValueBool("StatsLogger", "Verbose") 47 if err != nil { 48 logger.Warnf("config variable 'StatsLogger.Verbose' defaulting to 'true': %v", err) 49 globals.verbose = true 50 } 51 52 err = nil 53 return 54 } 55 56 // Up initializes the package and must successfully return before any API 57 // functions are invoked 58 func (dummy *globalsStruct) Up(confMap conf.ConfMap) (err error) { 59 err = parseConfMap(confMap) 60 if err != nil { 61 // parseConfMap() has logged an error 62 return 63 } 64 65 if globals.statsLogPeriod == 0 { 66 return 67 } 68 69 // collect info about free connections from SwiftClient once per second 70 globals.collectTicker = time.NewTicker(1 * time.Second) 71 globals.collectChan = globals.collectTicker.C 72 73 // record statistics in the log periodically 74 globals.logTicker = time.NewTicker(globals.statsLogPeriod) 75 globals.logChan = globals.logTicker.C 76 77 globals.stopChan = make(chan bool) 78 globals.doneChan = make(chan bool) 79 80 go statsLogger() 81 return 82 } 83 84 func (dummy *globalsStruct) VolumeGroupCreated(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) { 85 return nil 86 } 87 func (dummy *globalsStruct) VolumeGroupMoved(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) { 88 return nil 89 } 90 func (dummy *globalsStruct) VolumeGroupDestroyed(confMap conf.ConfMap, volumeGroupName string) (err error) { 91 return nil 92 } 93 func (dummy *globalsStruct) VolumeCreated(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) { 94 return nil 95 } 96 func (dummy *globalsStruct) VolumeMoved(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) { 97 return nil 98 } 99 func (dummy *globalsStruct) VolumeDestroyed(confMap conf.ConfMap, volumeName string) (err error) { 100 return nil 101 } 102 func (dummy *globalsStruct) ServeVolume(confMap conf.ConfMap, volumeName string) (err error) { 103 return nil 104 } 105 func (dummy *globalsStruct) UnserveVolume(confMap conf.ConfMap, volumeName string) (err error) { 106 return nil 107 } 108 func (dummy *globalsStruct) VolumeToBeUnserved(confMap conf.ConfMap, volumeName string) (err error) { 109 return nil 110 } 111 func (dummy *globalsStruct) SignaledStart(confMap conf.ConfMap) (err error) { 112 return nil 113 } 114 115 func (dummy *globalsStruct) SignaledFinish(confMap conf.ConfMap) (err error) { 116 // read the new confmap; if the log period has changed or there was an 117 // error shutdown the old logger prior to starting a new one 118 oldLogPeriod := globals.statsLogPeriod 119 err = parseConfMap(confMap) 120 if err != nil { 121 logger.ErrorWithError(err, "cannot parse confMap") 122 if oldLogPeriod != 0 { 123 globals.logTicker.Stop() 124 globals.logTicker = nil 125 126 globals.stopChan <- true 127 _ = <-globals.doneChan 128 } 129 return 130 } 131 132 // if no change required, just return 133 if globals.statsLogPeriod == oldLogPeriod { 134 return 135 } 136 137 logger.Infof("statslogger log period changing from %d sec to %d sec", 138 oldLogPeriod/time.Second, globals.statsLogPeriod/time.Second) 139 // shutdown the old logger (if any) and start a new one (if any) 140 if oldLogPeriod != 0 { 141 globals.logTicker.Stop() 142 globals.logTicker = nil 143 144 globals.stopChan <- true 145 _ = <-globals.doneChan 146 } 147 148 err = dummy.Up(confMap) 149 return 150 } 151 152 func (dummy *globalsStruct) Down(confMap conf.ConfMap) (err error) { 153 // shutdown the stats logger (if any) 154 logger.Infof("statslogger.Down() called") 155 if globals.statsLogPeriod != 0 { 156 globals.logTicker.Stop() 157 globals.logTicker = nil 158 159 globals.stopChan <- true 160 _ = <-globals.doneChan 161 } 162 163 // err is already nil 164 return 165 } 166 167 // the statsLogger collects the free connection statistics every collectChan tick 168 // and then logs a batch of statistics, including free connection statistics, 169 // every logChan tick ("statslogger.period" in the conf file. 170 // 171 func statsLogger() { 172 var ( 173 chunkedConnectionStats SimpleStats 174 nonChunkedConnectionStats SimpleStats 175 oldStatsMap map[string]uint64 176 newStatsMap map[string]uint64 177 oldMemStats runtime.MemStats 178 newMemStats runtime.MemStats 179 ) 180 181 chunkedConnectionStats.Clear() 182 nonChunkedConnectionStats.Clear() 183 chunkedConnectionStats.Sample(swiftclient.ChunkedConnectionFreeCnt()) 184 nonChunkedConnectionStats.Sample(swiftclient.NonChunkedConnectionFreeCnt()) 185 186 // memstats "stops the world" 187 oldStatsMap = stats.Dump() 188 runtime.ReadMemStats(&oldMemStats) 189 190 // print an initial round of absolute stats 191 logStats("total", &chunkedConnectionStats, &nonChunkedConnectionStats, &oldMemStats, oldStatsMap) 192 193 mainloop: 194 for stopRequest := false; !stopRequest; { 195 select { 196 case <-globals.stopChan: 197 // print final stats and then exit 198 stopRequest = true 199 200 case <-globals.collectChan: 201 chunkedConnectionStats.Sample(swiftclient.ChunkedConnectionFreeCnt()) 202 nonChunkedConnectionStats.Sample(swiftclient.NonChunkedConnectionFreeCnt()) 203 continue mainloop 204 205 case <-globals.logChan: 206 // fall through to do the logging 207 } 208 209 runtime.ReadMemStats(&newMemStats) 210 newStatsMap = stats.Dump() 211 212 // collect an extra connection stats sample to ensure we have at least one 213 chunkedConnectionStats.Sample(swiftclient.ChunkedConnectionFreeCnt()) 214 nonChunkedConnectionStats.Sample(swiftclient.NonChunkedConnectionFreeCnt()) 215 216 // print absolute stats and then deltas 217 logStats("total", &chunkedConnectionStats, &nonChunkedConnectionStats, &newMemStats, newStatsMap) 218 219 oldMemStats.Sys = newMemStats.Sys - oldMemStats.Sys 220 oldMemStats.TotalAlloc = newMemStats.TotalAlloc - oldMemStats.TotalAlloc 221 oldMemStats.HeapInuse = newMemStats.HeapInuse - oldMemStats.HeapInuse 222 oldMemStats.HeapIdle = newMemStats.HeapIdle - oldMemStats.HeapIdle 223 oldMemStats.HeapReleased = newMemStats.HeapReleased - oldMemStats.HeapReleased 224 oldMemStats.StackSys = newMemStats.StackSys - oldMemStats.StackSys 225 oldMemStats.MSpanSys = newMemStats.MSpanSys - oldMemStats.MSpanSys 226 oldMemStats.MCacheSys = newMemStats.MCacheSys - oldMemStats.MCacheSys 227 oldMemStats.BuckHashSys = newMemStats.BuckHashSys - oldMemStats.BuckHashSys 228 oldMemStats.GCSys = newMemStats.GCSys - oldMemStats.GCSys 229 oldMemStats.OtherSys = newMemStats.OtherSys - oldMemStats.OtherSys 230 231 oldMemStats.NextGC = newMemStats.NextGC - oldMemStats.NextGC 232 oldMemStats.NumGC = newMemStats.NumGC - oldMemStats.NumGC 233 oldMemStats.NumForcedGC = newMemStats.NumForcedGC - oldMemStats.NumForcedGC 234 oldMemStats.PauseTotalNs = newMemStats.PauseTotalNs - oldMemStats.PauseTotalNs 235 oldMemStats.GCCPUFraction = newMemStats.GCCPUFraction - oldMemStats.GCCPUFraction 236 237 for key := range newStatsMap { 238 oldStatsMap[key] = newStatsMap[key] - oldStatsMap[key] 239 } 240 logStats("delta", nil, nil, &oldMemStats, oldStatsMap) 241 242 if globals.verbose { 243 logVerboseStats(&newMemStats, newStatsMap) 244 } 245 246 oldMemStats = newMemStats 247 oldStatsMap = newStatsMap 248 249 // clear the connection stats 250 chunkedConnectionStats.Clear() 251 nonChunkedConnectionStats.Clear() 252 } 253 254 globals.doneChan <- true 255 return 256 } 257 258 // Write interesting statistics to the log in a semi-human readable format 259 // 260 // statsType is "total" or "delta" indicating whether statsMap and memStats are 261 // absolute or relative to the previous sample (doesn't apply to chunkedStats 262 // and nonChunkedStats, though they can be nil). 263 // 264 func logStats(statsType string, chunkedStats *SimpleStats, nonChunkedStats *SimpleStats, 265 memStats *runtime.MemStats, statsMap map[string]uint64) { 266 267 // if we have connection statistics, log them 268 if chunkedStats != nil || nonChunkedStats != nil { 269 logger.Infof("ChunkedFreeConnections: min=%d mean=%d max=%d NonChunkedFreeConnections: min=%d mean=%d max=%d", 270 chunkedStats.Min(), chunkedStats.Mean(), chunkedStats.Max(), 271 nonChunkedStats.Min(), nonChunkedStats.Mean(), nonChunkedStats.Max()) 272 } 273 274 // memory allocation info (see runtime.MemStats for definitions) 275 // no GC stats logged at this point 276 logger.Infof("Memory in Kibyte (%s): Sys=%d StackSys=%d MSpanSys=%d MCacheSys=%d BuckHashSys=%d GCSys=%d OtherSys=%d", 277 statsType, 278 int64(memStats.Sys)/1024, int64(memStats.StackSys)/1024, 279 int64(memStats.MSpanSys)/1024, int64(memStats.MCacheSys)/1024, 280 int64(memStats.BuckHashSys)/1024, int64(memStats.GCSys)/1024, int64(memStats.OtherSys)/1024) 281 logger.Infof("Memory in Kibyte (%s): HeapInuse=%d HeapIdle=%d HeapReleased=%d Cumulative TotalAlloc=%d", 282 statsType, 283 int64(memStats.HeapInuse)/1024, int64(memStats.HeapIdle)/1024, 284 int64(memStats.HeapReleased)/1024, int64(memStats.TotalAlloc)/1024) 285 logger.Infof("GC Stats (%s): NumGC=%d NumForcedGC=%d NextGC=%d KiB PauseTotalMsec=%d GC_CPU=%4.2f%%", 286 statsType, 287 memStats.NumGC, memStats.NumForcedGC, int64(memStats.NextGC)/1024, 288 memStats.PauseTotalNs/1000000, memStats.GCCPUFraction*100) 289 290 // selected proxyfs statistics that show filesystem or swift activity; consolidate all 291 // opps that query an account as SwiftAccountQueryOps, all opps that modify an account as 292 // accountModifyOps, etc. A chunked put counts as 1 SwiftObjModifyOps but is also counted 293 // separated in chunked put statistics. 294 accountQueryOps := statsMap[stats.SwiftAccountGetOps] + statsMap[stats.SwiftAccountHeadOps] 295 accountModifyOps := (statsMap[stats.SwiftAccountDeleteOps] + statsMap[stats.SwiftAccountPostOps] + 296 statsMap[stats.SwiftAccountPutOps]) 297 298 containerQueryOps := statsMap[stats.SwiftContainerGetOps] + statsMap[stats.SwiftContainerHeadOps] 299 containerModifyOps := (statsMap[stats.SwiftContainerDeleteOps] + statsMap[stats.SwiftContainerPostOps] + 300 statsMap[stats.SwiftContainerPutOps]) 301 302 objectQueryOps := (statsMap[stats.SwiftObjGetOps] + statsMap[stats.SwiftObjHeadOps] + 303 statsMap[stats.SwiftObjContentLengthOps] + statsMap[stats.SwiftObjLoadOps] + 304 statsMap[stats.SwiftObjTailOps]) 305 objectModifyOps := (statsMap[stats.SwiftObjDeleteOps] + statsMap[stats.SwiftObjCopyOps] + 306 statsMap[stats.SwiftObjPutCtxFetchOps]) 307 308 chunkedPutFetchOps := statsMap[stats.SwiftObjPutCtxFetchOps] 309 chunkedPutQueryOps := statsMap[stats.SwiftObjPutCtxReadOps] 310 chunkedPutModifyOps := statsMap[stats.SwiftObjPutCtxSendChunkOps] 311 chunkedPutCloseOPs := statsMap[stats.SwiftObjPutCtxCloseOps] 312 313 logger.Infof("Swift Client Ops (%s): Account QueryOps=%d ModifyOps=%d Container QueryOps=%d ModifyOps=%d Object QueryOps=%d ModifyOps=%d", 314 statsType, accountQueryOps, accountModifyOps, 315 containerQueryOps, containerModifyOps, objectQueryOps, objectModifyOps) 316 logger.Infof("Swift Client ChunkedPut Ops (%s): FetchOps=%d ReadOps=%d SendOps=%d CloseOps=%d", 317 statsType, chunkedPutFetchOps, chunkedPutQueryOps, chunkedPutModifyOps, chunkedPutCloseOPs) 318 } 319 320 func logVerboseStats(memStats *runtime.MemStats, statsMap map[string]uint64) { 321 var ( 322 bucketstatsValue string 323 bucketstatsValues string 324 bucketstatsValuesSlice []string 325 i int 326 pauseNsAccumulator uint64 327 statKey string 328 statValue uint64 329 varboseStatKey string 330 varboseStatValue uint64 331 varboseStatsMap map[string]uint64 332 ) 333 334 varboseStatsMap = make(map[string]uint64) 335 336 // General statistics. 337 varboseStatsMap["go_runtime_MemStats_Alloc"] = memStats.Alloc 338 varboseStatsMap["go_runtime_MemStats_TotalAlloc"] = memStats.TotalAlloc 339 varboseStatsMap["go_runtime_MemStats_Sys"] = memStats.Sys 340 varboseStatsMap["go_runtime_MemStats_Lookups"] = memStats.Lookups 341 varboseStatsMap["go_runtime_MemStats_Mallocs"] = memStats.Mallocs 342 varboseStatsMap["go_runtime_MemStats_Frees"] = memStats.Frees 343 344 // Main allocation heap statistics. 345 varboseStatsMap["go_runtime_MemStats_HeapAlloc"] = memStats.HeapAlloc 346 varboseStatsMap["go_runtime_MemStats_HeapSys"] = memStats.HeapSys 347 varboseStatsMap["go_runtime_MemStats_HeapIdle"] = memStats.HeapIdle 348 varboseStatsMap["go_runtime_MemStats_HeapInuse"] = memStats.HeapInuse 349 varboseStatsMap["go_runtime_MemStats_HeapReleased"] = memStats.HeapReleased 350 varboseStatsMap["go_runtime_MemStats_HeapObjects"] = memStats.HeapObjects 351 352 // Low-level fixed-size structure allocator statistics. 353 // Inuse is bytes used now. 354 // Sys is bytes obtained from system. 355 varboseStatsMap["go_runtime_MemStats_StackInuse"] = memStats.StackInuse 356 varboseStatsMap["go_runtime_MemStats_StackSys"] = memStats.StackSys 357 varboseStatsMap["go_runtime_MemStats_MSpanInuse"] = memStats.MSpanInuse 358 varboseStatsMap["go_runtime_MemStats_MSpanSys"] = memStats.MSpanSys 359 varboseStatsMap["go_runtime_MemStats_MCacheInuse"] = memStats.MCacheInuse 360 varboseStatsMap["go_runtime_MemStats_MCacheSys"] = memStats.MCacheSys 361 varboseStatsMap["go_runtime_MemStats_BuckHashSys"] = memStats.BuckHashSys 362 varboseStatsMap["go_runtime_MemStats_GCSys"] = memStats.GCSys 363 varboseStatsMap["go_runtime_MemStats_OtherSys"] = memStats.OtherSys 364 365 // Garbage collector statistics (fixed portion). 366 varboseStatsMap["go_runtime_MemStats_LastGC"] = memStats.LastGC 367 varboseStatsMap["go_runtime_MemStats_PauseTotalNs"] = memStats.PauseTotalNs 368 varboseStatsMap["go_runtime_MemStats_NumGC"] = uint64(memStats.NumGC) 369 varboseStatsMap["go_runtime_MemStats_GCCPUPercentage"] = uint64(100.0 * memStats.GCCPUFraction) 370 371 // Garbage collector statistics (go_runtime_MemStats_PauseAverageNs). 372 if 0 == memStats.NumGC { 373 varboseStatsMap["go_runtime_MemStats_PauseAverageNs"] = 0 374 } else { 375 pauseNsAccumulator = 0 376 if memStats.NumGC < 255 { 377 for i = 0; i < int(memStats.NumGC); i++ { 378 pauseNsAccumulator += memStats.PauseNs[i] 379 } 380 varboseStatsMap["go_runtime_MemStats_PauseAverageNs"] = pauseNsAccumulator / uint64(memStats.NumGC) 381 } else { 382 for i = 0; i < 256; i++ { 383 pauseNsAccumulator += memStats.PauseNs[i] 384 } 385 varboseStatsMap["go_runtime_MemStats_PauseAverageNs"] = pauseNsAccumulator / 256 386 } 387 } 388 389 for statKey, statValue = range statsMap { 390 varboseStatKey = strings.Replace(statKey, ".", "_", -1) 391 varboseStatKey = strings.Replace(varboseStatKey, "-", "_", -1) 392 varboseStatsMap[varboseStatKey] = statValue 393 } 394 395 for varboseStatKey, varboseStatValue = range varboseStatsMap { 396 logger.Infof("metrics %s: %d", varboseStatKey, varboseStatValue) 397 } 398 399 bucketstatsValues = bucketstats.SprintStats(bucketstats.StatFormatParsable1, "*", "*") 400 401 bucketstatsValuesSlice = strings.Split(bucketstatsValues, "\n") 402 403 for _, bucketstatsValue = range bucketstatsValuesSlice { 404 logger.Infof("stats %s", bucketstatsValue) 405 } 406 }