From 73f8c984038b2af44611ae554d0e85371d08a405 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 25 Jun 2018 15:11:28 -0700 Subject: [PATCH] metrics reporting to a file --- server/metrics/metrics.go | 47 ++++++++++++++++++++++++++-- server/metrics/monitors.go | 63 ++++++++++++++++++++++++++++++-------- 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go index 47b81e2..779dcb8 100644 --- a/server/metrics/metrics.go +++ b/server/metrics/metrics.go @@ -1,6 +1,9 @@ package metrics import ( + "bufio" + "fmt" + "strings" "sync" "sync/atomic" "time" @@ -46,7 +49,21 @@ func (c *counter) GetStarttime() time.Time { return c.startTime } +// MonitorType controls how the monitor will report itself +type MonitorType int + +const ( + // Count indicates the monitor should report in interger format + Count MonitorType = iota + // Percent indicates the monitor should report in decimal format with 2 places + Percent + // MegaBytes indicates the monitor should transform the raw number into MBs + MegaBytes +) + type monitorHistory struct { + monitorType MonitorType + starttime time.Time perMinutePerHour [60]float64 timeLastHourRotate time.Time @@ -74,11 +91,13 @@ type MonitorHistory interface { Days() []float64 Weeks() []float64 Months() []float64 + + Report(w *bufio.Writer) } // NewMonitorHistory returns a new MonitorHistory with starttime of time.Now and Started running with supplied monitor -func NewMonitorHistory(monitor func() float64) MonitorHistory { - mh := &monitorHistory{starttime: time.Now(), monitor: monitor, breakChannel: make(chan bool)} +func NewMonitorHistory(t MonitorType, monitor func() float64) MonitorHistory { + mh := &monitorHistory{monitorType: t, starttime: time.Now(), monitor: monitor, breakChannel: make(chan bool)} mh.Start() return mh } @@ -118,6 +137,30 @@ func (mh *monitorHistory) Months() []float64 { return mh.returnCopy(mh.perMonthForYear[:]) } +func (mh *monitorHistory) Report(w *bufio.Writer) { + fmt.Fprintln(w, "Minutes:", reportLine(mh.monitorType, mh.perMinutePerHour[:])) + fmt.Fprintln(w, "Hours: ", reportLine(mh.monitorType, mh.perHourForDay[:])) + fmt.Fprintln(w, "Days: ", reportLine(mh.monitorType, mh.perDayForWeek[:])) + fmt.Fprintln(w, "Weeks: ", reportLine(mh.monitorType, mh.perWeekForMonth[:])) + fmt.Fprintln(w, "Months: ", reportLine(mh.monitorType, mh.perMonthForYear[:])) +} + +func reportLine(t MonitorType, array []float64) string { + switch t { + case Count: + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(array)), " "), "[]") + case Percent: + return strings.Trim(strings.Join(strings.Fields(fmt.Sprintf("%.2f", array)), " "), "[]") + case MegaBytes: + mbs := make([]int, len(array)) + for i, b := range array { + mbs[i] = int(b) / 1024 / 1024 + } + return strings.Trim(strings.Join(strings.Fields(fmt.Sprintf("%d", mbs)), "MBs "), "[]") + "MBs" + } + return "" +} + func (mh *monitorHistory) returnCopy(slice []float64) []float64 { retSlice := make([]float64, len(slice)) mh.lock.Lock() diff --git a/server/metrics/monitors.go b/server/metrics/monitors.go index 72b0d02..654f979 100644 --- a/server/metrics/monitors.go +++ b/server/metrics/monitors.go @@ -1,6 +1,8 @@ package metrics import ( + "bufio" + "fmt" "git.openprivacy.ca/openprivacy/libricochet-go/application" "github.com/struCoder/pidusage" "log" @@ -8,24 +10,30 @@ import ( "time" ) +const ( + reportFile = "serverMonitorReport.txt" +) + // Monitors is a package of metrics for a Cwtch Server including message count, CPU, Mem, and conns type Monitors struct { - MessageCounter Counter - Messages MonitorHistory - CPU MonitorHistory - Memory MonitorHistory - ClientListenConns MonitorHistory - breakChannel chan bool + MessageCounter Counter + Messages MonitorHistory + CPU MonitorHistory + Memory MonitorHistory + ClientConns MonitorHistory + starttime time.Time + breakChannel chan bool } // Start initializes a Monitors's monitors func (mp *Monitors) Start(ra *application.RicochetApplication) { + mp.starttime = time.Now() mp.breakChannel = make(chan bool) mp.MessageCounter = NewCounter() - mp.Messages = NewMonitorHistory(func() (c float64) { c = float64(mp.MessageCounter.Count()); mp.MessageCounter.Reset(); return }) - mp.CPU = NewMonitorHistory(func() float64 { sysInfo, _ := pidusage.GetStat(os.Getpid()); return float64(sysInfo.CPU) }) - mp.Memory = NewMonitorHistory(func() float64 { sysInfo, _ := pidusage.GetStat(os.Getpid()); return float64(sysInfo.Memory) }) - mp.ClientListenConns = NewMonitorHistory(func() float64 { return float64(ra.ConnectionCount()) }) + mp.Messages = NewMonitorHistory(Count, func() (c float64) { c = float64(mp.MessageCounter.Count()); mp.MessageCounter.Reset(); return }) + mp.CPU = NewMonitorHistory(Percent, func() float64 { sysInfo, _ := pidusage.GetStat(os.Getpid()); return float64(sysInfo.CPU) }) + mp.Memory = NewMonitorHistory(MegaBytes, func() float64 { sysInfo, _ := pidusage.GetStat(os.Getpid()); return float64(sysInfo.Memory) }) + mp.ClientConns = NewMonitorHistory(Count, func() float64 { return float64(ra.ConnectionCount()) }) // Todo: replace with proper reporting go mp.log() @@ -38,19 +46,48 @@ func (mp *Monitors) log() { messageMinutes := mp.Messages.Minutes() cpuMinutes := mp.CPU.Minutes() memoryMinutes := mp.Memory.Minutes() - listenConnsMinutes := mp.ClientListenConns.Minutes() - log.Printf("METRICS: Messages: %.0f ClientListenConns: %.0f CPU: %.2f Memory: %.0f\n", messageMinutes[0], listenConnsMinutes[0], cpuMinutes[0], memoryMinutes[0]) + listenConnsMinutes := mp.ClientConns.Minutes() + log.Printf("METRICS: Messages: %.0f ClientConns: %.0f CPU: %.2f Memory: %.0fMBs\n", messageMinutes[0], listenConnsMinutes[0], cpuMinutes[0], memoryMinutes[0]/1024/1024) + mp.report() case <-mp.breakChannel: return } } } +func (mp *Monitors) report() { + f, err := os.Create(reportFile) + if err != nil { + log.Println("ERROR: Could not open monitor reporting file: ", err) + return + } + defer f.Close() + + w := bufio.NewWriter(f) + + fmt.Fprintln(w, "Started: ", mp.starttime, "\n") + + fmt.Fprintln(w, "Messages:") + mp.Messages.Report(w) + + fmt.Fprintln(w, "\nClient Connections:") + mp.ClientConns.Report(w) + + fmt.Fprintln(w, "\nCPU:") + mp.CPU.Report(w) + + fmt.Fprintln(w, "\nMemory:") + mp.Memory.Report(w) + + w.Flush() + +} + // Stop stops all the monitors in a Monitors func (mp *Monitors) Stop() { mp.breakChannel <- true mp.Messages.Stop() mp.CPU.Stop() mp.Memory.Stop() - mp.ClientListenConns.Stop() + mp.ClientConns.Stop() }