diff --git a/common/metrics.go b/common/metrics.go index 34e3c82..694c920 100644 --- a/common/metrics.go +++ b/common/metrics.go @@ -13,7 +13,7 @@ import ( ) // GetCPULoad calculates the CPU load for the service, system, and total. -func GetCPULoad() (serviceCPU, systemCPU, totalCPU string) { +func GetCPULoad() (serviceCPU, systemCPU, totalCPU string, serviceCPUF, systemCPUF, totalCPUF float64) { proc := GetProcessObject() // Getting process details serviceCPUF, err := proc.CPUPercent() // Measure CPU percent for the current process @@ -26,41 +26,49 @@ func GetCPULoad() (serviceCPU, systemCPU, totalCPU string) { cpuPercents, err := cpu.Percent(time.Second, false) // Get total system CPU percentage if err != nil { log.Printf("[MoniGo] Error fetching CPU load for the system: %v\n", err) - return serviceCPU, "0%", "0%" + return serviceCPU, "0%", "0%", serviceCPUF, 0, 0 } if len(cpuPercents) > 0 { - systemCPU = ParseFloat64ToString(cpuPercents[0]-serviceCPUF) + "%" // System CPU usage percentage + systemCPUF = cpuPercents[0] - serviceCPUF + if systemCPUF < 0 { + systemCPUF = 0 + } + systemCPU = ParseFloat64ToString(systemCPUF) + "%" // System CPU usage percentage + totalCPUF = cpuPercents[0] } - totalCPU = ParseFloat64ToString(serviceCPUF+cpuPercents[0]) + "%" // Total CPU usage percentage - return serviceCPU, systemCPU, totalCPU + totalCPU = ParseFloat64ToString(totalCPUF) + "%" // Total CPU usage percentage + return serviceCPU, systemCPU, totalCPU, serviceCPUF, systemCPUF, totalCPUF } // GetMemoryLoad calculates the memory load for the service, system, and total. -func GetMemoryLoad() (serviceMem, systemMem, totalMem string) { +func GetMemoryLoad() (serviceMem, systemMem, totalMem string, serviceMemF, systemMemF, totalMemF float64) { // Get system memory statistics vmStat, err := mem.VirtualMemory() if err != nil { log.Printf("[MoniGo] Error fetching memory load for the system: %v\n", err) - return "0%", "0%", "0%" + return "0%", "0%", "0%", 0, 0, 0 } - systemMem = ParseFloat64ToString(vmStat.UsedPercent) + "%" // Calculate system memory as a percentage of total memory - totalMem = ParseFloat64ToString(ParseUint64ToFloat64(vmStat.Total)) // Total memory in bytes Total amount of RAM on this system + systemMemF = vmStat.UsedPercent + systemMem = ParseFloat64ToString(systemMemF) + "%" // Calculate system memory as a percentage of total memory + totalMemF = float64(vmStat.Total) + totalMem = ParseFloat64ToString(totalMemF) // Total memory in bytes Total amount of RAM on this system proc := GetProcessObject() memInfo, err := proc.MemoryInfo() if err != nil { log.Printf("[MoniGo] Error fetching memory load for the service: %v\n", err) - return "0%", systemMem, totalMem + return "0%", systemMem, totalMem, 0, systemMemF, totalMemF } - serviceMem = ParseFloat64ToString(float64(memInfo.RSS)/float64(vmStat.Total)*100) + "%" // Calculate service memory as a percentage of total memory + serviceMemF = (float64(memInfo.RSS) / float64(vmStat.Total)) * 100 + serviceMem = ParseFloat64ToString(serviceMemF) + "%" // Calculate service memory as a percentage of total memory - return serviceMem, systemMem, totalMem + return serviceMem, systemMem, totalMem, serviceMemF, systemMemF, totalMemF } // GetDiskLoad calculates the disk load for the service, system, and total. -func GetDiskLoad() (serviceDisk, systemDisk, totalDisk string) { +func GetDiskLoad() (serviceDisk, systemDisk, totalDisk string, systemDiskF, totalDiskF float64) { // For disk, "Service" usage handles read/write bytes or handle count, but normally "Load" implies storage usage. // However, gathering "Disk Usage by Process" is complex and often requires root or specific tracking. // For now, we will track System Disk Usage (Root Partition). @@ -68,20 +76,22 @@ func GetDiskLoad() (serviceDisk, systemDisk, totalDisk string) { diskUsage, err := disk.Usage("/") if err != nil { log.Printf("[MoniGo] Error fetching disk usage: %v\n", err) // Changed from Panic to Printf as agreed in plan - return "0%", "0%", "0%" + return "0%", "0%", "0%", 0, 0 } // ServiceDiskLoad is complex to calculate per process without cgroups/root. // We will mistakenly leave it as 0% or maybe revisit if we can get FD count as proxy? // For now, let's just return System Disk Usage. - systemDisk = ParseFloat64ToString(diskUsage.UsedPercent) + "%" - totalDisk = ParseFloat64ToString(float64(diskUsage.Total)) // Total disk size in bytes + systemDiskF = diskUsage.UsedPercent + systemDisk = ParseFloat64ToString(systemDiskF) + "%" + totalDiskF = float64(diskUsage.Total) + totalDisk = ParseFloat64ToString(totalDiskF) // Total disk size in bytes // ServiceDiskLoad: Not easily available. serviceDisk = "0%" - return serviceDisk, systemDisk, totalDisk + return serviceDisk, systemDisk, totalDisk, systemDiskF, totalDiskF } // GetProcessDetails returns the process ID and process object. diff --git a/core/core.go b/core/core.go index beb6d4a..c02395c 100644 --- a/core/core.go +++ b/core/core.go @@ -50,6 +50,10 @@ func GetServiceStats() models.ServiceStats { stats.HeapAllocBySystem = common.BytesToUnit(memStats.HeapSys) stats.TotalAllocByService = common.BytesToUnit(memStats.TotalAlloc) stats.TotalMemoryByOS = common.BytesToUnit(memStats.Sys) + stats.HeapAllocByServiceRaw = memStats.HeapAlloc + stats.HeapAllocBySystemRaw = memStats.HeapSys + stats.TotalAllocByServiceRaw = memStats.TotalAlloc + stats.TotalMemoryByOSRaw = memStats.Sys }() // Goroutine to fetch network I/O statistics @@ -108,35 +112,39 @@ func GetCoreStatistics() models.CoreStatistics { func GetLoadStatistics() models.LoadStatistics { // Fetch CPU load statistics - serviceCPULoad, systemCPULoad, totalCPULoad := common.GetCPULoad() + serviceCPULoad, systemCPULoad, totalCPULoad, serviceCPUF, systemCPUF, _ := common.GetCPULoad() // Fetch memory load statistics - serviceMemLoad, systemMemLoad, totalMemAvailable := common.GetMemoryLoad() + serviceMemLoad, systemMemLoad, totalMemAvailable, serviceMemF, systemMemF, _ := common.GetMemoryLoad() // Fetch disk load statistics - serviceDisk, systemDisk, totalDisk := common.GetDiskLoad() + serviceDisk, systemDisk, totalDisk, systemDiskF, totalDiskF := common.GetDiskLoad() + + overallLoadF, overallLoadStr := CalculateOverallLoad(serviceCPUF, serviceMemF) return models.LoadStatistics{ - ServiceCPULoad: serviceCPULoad, - SystemCPULoad: systemCPULoad, - TotalCPULoad: totalCPULoad, - ServiceMemLoad: serviceMemLoad, - SystemMemLoad: systemMemLoad, - TotalMemLoad: common.ConvertToReadableUnit(totalMemAvailable), - OverallLoadOfService: CalculateOverallLoad(serviceCPULoad, serviceMemLoad), - ServiceDiskLoad: serviceDisk, - SystemDiskLoad: systemDisk, - TotalDiskLoad: totalDisk, + ServiceCPULoad: serviceCPULoad, + SystemCPULoad: systemCPULoad, + TotalCPULoad: totalCPULoad, + ServiceMemLoad: serviceMemLoad, + SystemMemLoad: systemMemLoad, + TotalMemLoad: totalMemAvailable, + OverallLoadOfService: overallLoadStr, + ServiceDiskLoad: serviceDisk, + SystemDiskLoad: systemDisk, + TotalDiskLoad: totalDisk, + ServiceCPULoadRaw: serviceCPUF, + SystemCPULoadRaw: systemCPUF, + ServiceMemLoadRaw: serviceMemF, + SystemMemLoadRaw: systemMemF, + OverallLoadOfServiceRaw: overallLoadF, + SystemDiskLoadRaw: systemDiskF, + TotalDiskLoadRaw: totalDiskF, } } // Function to calculate overall load -func CalculateOverallLoad(serviceCPU, serviceMem string) string { - - // string to float64 conversion - serviceCPUF := common.ParseStringToFloat64(serviceCPU) - serviceMemF := common.ParseStringToFloat64(serviceMem) - +func CalculateOverallLoad(serviceCPUF, serviceMemF float64) (float64, string) { cpuWeight := 0.5 // Weight for CPU load memWeight := 0.5 // Weight for memory usage @@ -146,7 +154,7 @@ func CalculateOverallLoad(serviceCPU, serviceMem string) string { overallLoad = 100 } - return common.ParseFloat64ToString(overallLoad) + "%" + return overallLoad, common.ParseFloat64ToString(overallLoad) + "%" } // GetCPUStatistics retrieves the CPU statistics. @@ -205,16 +213,22 @@ func GetMemoryStatistics() models.MemoryStatistics { m := ReadMemStats() // Get the memory statistics for the service return models.MemoryStatistics{ - TotalSystemMemory: common.BytesToUnit(memInfo.Total), - MemoryUsedBySystem: common.BytesToUnit(memInfo.Used), - AvailableMemory: common.BytesToUnit(memInfo.Available), - TotalSwapMemory: common.BytesToUnit(swapInfo.Total), - FreeSwapMemory: common.BytesToUnit(swapInfo.Free), - MemoryUsedByService: common.BytesToUnit(m.Alloc), // Example metric - StackMemoryUsage: common.BytesToUnit(m.StackInuse), - GCPauseDuration: fmt.Sprintf("%.2f ms", float64(m.PauseTotalNs)/float64(time.Millisecond)), // Convert nanoseconds to milliseconds - MemStatsRecords: ConstructMemStats(m), - RawMemStatsRecords: ConstructRawMemStats(m), + TotalSystemMemory: common.BytesToUnit(memInfo.Total), + MemoryUsedBySystem: common.BytesToUnit(memInfo.Used), + AvailableMemory: common.BytesToUnit(memInfo.Available), + TotalSwapMemory: common.BytesToUnit(swapInfo.Total), + FreeSwapMemory: common.BytesToUnit(swapInfo.Free), + MemoryUsedByService: common.BytesToUnit(m.Alloc), // Example metric + StackMemoryUsage: common.BytesToUnit(m.StackInuse), + GCPauseDuration: fmt.Sprintf("%.2f ms", float64(m.PauseTotalNs)/float64(time.Millisecond)), // Convert nanoseconds to milliseconds + MemStatsRecords: ConstructMemStats(m), + RawMemStatsRecords: ConstructRawMemStats(m), + TotalSystemMemoryRaw: float64(memInfo.Total), + MemoryUsedBySystemRaw: float64(memInfo.Used), + MemoryUsedByServiceRaw: float64(m.Alloc), + AvailableMemoryRaw: float64(memInfo.Available), + GCPauseDurationRaw: float64(m.PauseTotalNs) / float64(time.Millisecond), + StackMemoryUsageRaw: float64(m.StackInuse), } } diff --git a/core/function-metrics.go b/core/function-metrics.go index e1136c1..9cbc093 100644 --- a/core/function-metrics.go +++ b/core/function-metrics.go @@ -258,16 +258,44 @@ func executeFunctionWithProfiling(name string, fn func()) { // ViewFunctionMetrics generates the function metrics func ViewFunctionMetrics(name, reportType string, metrics *models.FunctionMetrics) models.FunctionTraceDetails { + // Check if 'go' command is available + _, err := exec.LookPath("go") + if err != nil { + log.Printf("[MoniGo] Warning: 'go' command not found in PATH. pprof reports will be unavailable.") + return models.FunctionTraceDetails{ + FunctionName: name, + CoreProfile: models.Profiles{ + CPU: "Error: 'go' command not found. pprof reports require the Go SDK.", + Mem: "Error: 'go' command not found. pprof reports require the Go SDK.", + }, + FunctionCodeTrace: "Error: 'go' command not found.", + } + } + // Function to execute the pprof command and return the output or log an error executePprof := func(profileFilePath, reportType string) string { + if profileFilePath == "" { + return "Error: Profile file path is empty" + } cmd := exec.Command("go", "tool", "pprof", "-"+reportType, profileFilePath) - output, _ := cmd.Output() + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Sprintf("Error executing pprof: %v\nOutput: %s", err, string(output)) + } return string(output) } // Generating the function code stack trace for CPU profile - codeStackView := exec.Command("go", "tool", "pprof", "-list", name, metrics.CPUProfileFilePath) - codeStack, _ := codeStackView.Output() + var codeStack string + if metrics.CPUProfileFilePath != "" { + codeStackView := exec.Command("go", "tool", "pprof", "-list", name, metrics.CPUProfileFilePath) + output, err := codeStackView.CombinedOutput() + if err != nil { + codeStack = fmt.Sprintf("Error generating code trace: %v\nOutput: %s", err, string(output)) + } else { + codeStack = string(output) + } + } // Return the function trace details return models.FunctionTraceDetails{ @@ -276,6 +304,6 @@ func ViewFunctionMetrics(name, reportType string, metrics *models.FunctionMetric CPU: executePprof(metrics.CPUProfileFilePath, reportType), Mem: executePprof(metrics.MemProfileFilePath, reportType), }, - FunctionCodeTrace: string(codeStack), + FunctionCodeTrace: codeStack, } } diff --git a/example/go.mod b/example/go.mod index ad6c577..9f2705c 100644 --- a/example/go.mod +++ b/example/go.mod @@ -6,6 +6,8 @@ require github.com/iyashjayesh/monigo v0.0.2 require ( github.com/andybalholm/brotli v1.2.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -15,14 +17,21 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/tstorage v0.3.6 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.68.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/sys v0.39.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect ) replace github.com/iyashjayesh/monigo => ../ diff --git a/example/go.sum b/example/go.sum index 5185d95..e301151 100644 --- a/example/go.sum +++ b/example/go.sum @@ -1,34 +1,58 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY= github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nakabonne/tstorage v0.3.6 h1:usp7pTohax8mynnFiUSUQ2QVBCKLCkYx3gmb3+rJo54= github.com/nakabonne/tstorage v0.3.6/go.mod h1:1xUrK3s1MXSlU6dn96xHerHx/MdO4BGmsAHEUbsaOxU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -41,10 +65,19 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/models/apiModels.go b/models/apiModels.go index adab7e9..48330e5 100644 --- a/models/apiModels.go +++ b/models/apiModels.go @@ -16,7 +16,14 @@ type ServiceStats struct { HeapAllocBySystem string `json:"heap_alloc_by_system"` TotalAllocByService string `json:"total_alloc_by_service"` TotalMemoryByOS string `json:"total_memory_by_os"` - DiskIO struct { + + // Raw values for storage + HeapAllocByServiceRaw uint64 `json:"-"` + HeapAllocBySystemRaw uint64 `json:"-"` + TotalAllocByServiceRaw uint64 `json:"-"` + TotalMemoryByOSRaw uint64 `json:"-"` + + DiskIO struct { ReadBytes uint64 `json:"read_bytes"` WriteBytes uint64 `json:"write_bytes"` } `json:"disk_io"` // Disk Use percentage @@ -49,6 +56,14 @@ type LoadStatistics struct { ServiceDiskLoad string `json:"service_disk_load"` SystemDiskLoad string `json:"system_disk_load"` TotalDiskLoad string `json:"total_disk_load"` + // Raw values for storage + ServiceCPULoadRaw float64 `json:"-"` + SystemCPULoadRaw float64 `json:"-"` + ServiceMemLoadRaw float64 `json:"-"` + SystemMemLoadRaw float64 `json:"-"` + OverallLoadOfServiceRaw float64 `json:"-"` + SystemDiskLoadRaw float64 `json:"-"` + TotalDiskLoadRaw float64 `json:"-"` } // CPUStatistics represents the CPU statistics of the service. @@ -73,6 +88,13 @@ type MemoryStatistics struct { FreeSwapMemory string `json:"free_swap_memory"` MemStatsRecords []Record `json:"mem_stats_records"` // List of memory statistic records. RawMemStatsRecords []RawMemStatsRecords `json:"raw_mem_stats_records"` // RawMemStatsRecords holds a list of raw memory statistic records. + // Raw values for storage + TotalSystemMemoryRaw float64 `json:"-"` + MemoryUsedBySystemRaw float64 `json:"-"` + MemoryUsedByServiceRaw float64 `json:"-"` + AvailableMemoryRaw float64 `json:"-"` + GCPauseDurationRaw float64 `json:"-"` + StackMemoryUsageRaw float64 `json:"-"` } // ServiceHealth represents the health of the service. diff --git a/monigo.go b/monigo.go index 89f1c40..6cd897a 100644 --- a/monigo.go +++ b/monigo.go @@ -176,15 +176,13 @@ func (m *Monigo) MonigoInstanceConstructorWithoutPort() { m.ServiceStartTime = time.Now().In(location) // Setting the service start time } -// Initialize initializes the monigo service without starting the dashboard -// This is useful when you want to integrate MoniGo with your existing HTTP server -func (m *Monigo) Initialize() error { +// setup contains common initialization logic for both Initialize and Start +func (m *Monigo) setup() error { // Validate service name if m.ServiceName == "" { return fmt.Errorf("[MoniGo] service_name is required, please provide the service name") } - m.MonigoInstanceConstructorWithoutPort() // Use constructor without port binding if err := timeseries.PurgeStorage(); err != nil { return fmt.Errorf("[MoniGo] Warning: failed to purge storage: %v", err) } @@ -200,7 +198,6 @@ func (m *Monigo) Initialize() error { cache := common.Cache{Data: make(map[string]time.Time)} if err := cache.LoadFromFile(cachePath); err != nil { log.Printf("[MoniGo] Warning: failed to load cache from file: %v. Starting with fresh cache.", err) - // Continue with empty cache instead of panicking } // Updating the service start time in the cache @@ -214,7 +211,6 @@ func (m *Monigo) Initialize() error { // Save the cache data to file if err := cache.SaveToFile(cachePath); err != nil { log.Printf("[MoniGo] Warning: failed to save cache to file: %v", err) - // Continue without saving cache } // Setting common service information @@ -226,7 +222,7 @@ func (m *Monigo) Initialize() error { m.DataRetentionPeriod, ) - // Initialize storage to ensure it's available for API calls + // Initialize storage and core settings if m.StorageType != "" { timeseries.SetStorageType(m.StorageType) } @@ -239,64 +235,28 @@ func (m *Monigo) Initialize() error { log.Printf("[MoniGo] Warning: failed to initialize storage: %v", err) return fmt.Errorf("failed to initialize storage: %w", err) } + return nil } -// Function to start the monigo service -func (m *Monigo) Start() error { - // Validate service name - if m.ServiceName == "" { - return fmt.Errorf("[MoniGo] service_name is required, please provide the service name") - } +// Initialize initializes the monigo service without starting the dashboard +func (m *Monigo) Initialize() error { + m.MonigoInstanceConstructorWithoutPort() + return m.setup() +} +// Start starts the monigo service with dashboard +func (m *Monigo) Start() error { if err := m.MonigoInstanceConstructor(); err != nil { return err } - if err := timeseries.PurgeStorage(); err != nil { - return fmt.Errorf("[MoniGo] Warning: failed to purge storage: %v", err) - } - if err := timeseries.SetDataPointsSyncFrequency(m.DataPointsSyncFrequency); err != nil { - return fmt.Errorf("[MoniGo] failed to set data points sync frequency: %v", err) - } - - // Fetching runtime details - m.ProcessId = common.GetProcessId() - m.GoVersion = runtime.Version() - - cachePath := BasePath + "/cache.dat" - cache := common.Cache{Data: make(map[string]time.Time)} - if err := cache.LoadFromFile(cachePath); err != nil { - log.Printf("[MoniGo] Warning: failed to load cache from file: %v. Starting with fresh cache.", err) - // Continue with empty cache instead of panicking - } - // Updating the service start time in the cache - if startTime, exists := cache.Data[m.ServiceName]; exists { - m.ServiceStartTime = startTime - } else { - m.ServiceStartTime = time.Now() - cache.Data[m.ServiceName] = m.ServiceStartTime - } - - // Save the cache data to file - if err := cache.SaveToFile(cachePath); err != nil { - log.Printf("[MoniGo] Warning: failed to save cache to file: %v", err) - // Continue without saving cache + if err := m.setup(); err != nil { + return err } - // Setting common service information - common.SetServiceInfo( - m.ServiceName, - m.ServiceStartTime, - m.GoVersion, - m.ProcessId, - m.DataRetentionPeriod, - ) - if m.Headless { log.Println("[MoniGo] Running in headless mode. Dashboard disabled.") - // Keep the process alive if only monigo is running, or just return nil - // Since this is embedded, we usually just return nil anyway return nil } @@ -895,8 +855,22 @@ func RateLimitMiddleware(requests int, window time.Duration) func(http.Handler) lastReset time.Time } - clients := make(map[string]*clientInfo) - var mu sync.RWMutex + var mu sync.Mutex + var clients = make(map[string]*clientInfo) + + // Start a cleanup goroutine to prevent memory leak + go func() { + ticker := time.NewTicker(window * 2) + for range ticker.C { + mu.Lock() + for ip, info := range clients { + if time.Since(info.lastReset) > window*2 { + delete(clients, ip) + } + } + mu.Unlock() + } + }() return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/timeseries/monigodb.go b/timeseries/monigodb.go index 92f6d03..0b7ff3b 100644 --- a/timeseries/monigodb.go +++ b/timeseries/monigodb.go @@ -3,9 +3,11 @@ package timeseries import ( "context" "errors" + "fmt" "log" "os" "path/filepath" + "strings" "sync" "time" @@ -14,15 +16,6 @@ import ( "github.com/nakabonne/tstorage" ) -var ( - once sync.Once // Ensures that the storage is initialized only once - basePath string // Base path for storage - storage Storage // Storage instance - closeOnce sync.Once // Ensures that the storage is closed only once - ctx context.Context // Context for goroutines - cancel context.CancelFunc // Cancel function for goroutines -) - // Storage defines the methods required for storage operations. type Storage interface { InsertRows(rows []tstorage.Row) error @@ -102,8 +95,19 @@ func (s *StorageWrapper) Close() error { return s.storage.Close() } -// storageType defines which storage to use -var storageType = "disk" // "disk" or "memory" +type storageManager struct { + storage Storage + ctx context.Context + cancel context.CancelFunc + once sync.Once + closeOnce sync.Once + mu sync.Mutex +} + +var ( + manager = &storageManager{} + storageType = "disk" // "disk" or "memory" +) // SetStorageType sets the storage type func SetStorageType(t string) { @@ -113,14 +117,14 @@ func SetStorageType(t string) { // GetStorageInstance initializes and returns a Storage instance. func GetStorageInstance() (Storage, error) { var err error - once.Do(func() { + manager.once.Do(func() { if storageType == "memory" { - storage = NewInMemoryStorage() - ctx, cancel = context.WithCancel(context.Background()) + manager.storage = NewInMemoryStorage() + manager.ctx, manager.cancel = context.WithCancel(context.Background()) return } - basePath = common.GetBasePath() + basePath := common.GetBasePath() storageInstance, initErr := tstorage.NewStorage( tstorage.WithDataPath(filepath.Join(basePath, "data")), tstorage.WithRetention(common.GetDataRetentionPeriod()), @@ -130,22 +134,22 @@ func GetStorageInstance() (Storage, error) { log.Printf("[MoniGo] Error initializing storage: %v\n", err) return } - storage = &StorageWrapper{storage: storageInstance} + manager.storage = &StorageWrapper{storage: storageInstance} // Initialize context and cancel function for goroutines - ctx, cancel = context.WithCancel(context.Background()) + manager.ctx, manager.cancel = context.WithCancel(context.Background()) }) - return storage, err + return manager.storage, err } // CloseStorage closes the storage instance and stops any running goroutines. func CloseStorage() error { var err error - closeOnce.Do(func() { - if cancel != nil { - cancel() // Stop any goroutines + manager.closeOnce.Do(func() { + if manager.cancel != nil { + manager.cancel() // Stop any goroutines } - if storage != nil { - if closeErr := storage.Close(); closeErr != nil { + if manager.storage != nil { + if closeErr := manager.storage.Close(); closeErr != nil { log.Printf("[MoniGo] Error closing storage: %v\n", closeErr) err = closeErr } @@ -154,13 +158,26 @@ func CloseStorage() error { return err } -// PurgeStorage removes all storage data and closes the storage. +// PurgeStorage removes only the monigo data directory to avoid accidental deletions of other files. func PurgeStorage() error { basePath := common.GetBasePath() + + // Safety check: ensure we are only deleting the 'monigo' directory + if !strings.HasSuffix(basePath, "monigo") { + return fmt.Errorf("[MoniGo] Refusing to purge storage: basePath %q does not end with 'monigo'", basePath) + } + if err := os.RemoveAll(basePath); err != nil { log.Printf("[MoniGo] Error purging storage: %v\n", err) return err } + + // Recreate the directory + if err := os.MkdirAll(basePath, os.ModePerm); err != nil { + log.Printf("[MoniGo] Error recreating storage directory: %v\n", err) + return err + } + return nil } @@ -177,25 +194,29 @@ func SetDataPointsSyncFrequency(frequency ...string) error { freqTime = 5 * time.Minute } + // Ensure storage is initialized before starting the sync loop + if _, err := GetStorageInstance(); err != nil { + return err + } + // Initializing service metrics once serviceMetrics := core.GetServiceStats() if err := StoreServiceMetrics(&serviceMetrics); err != nil { return errors.New("[MoniGo] error storing service metrics, err: " + err.Error()) } - timer := time.NewTimer(freqTime) + ticker := time.NewTicker(freqTime) go func() { - defer timer.Stop() + defer ticker.Stop() for { select { - case <-ctx.Done(): + case <-manager.ctx.Done(): return - case <-timer.C: + case <-ticker.C: serviceMetrics := core.GetServiceStats() if err := StoreServiceMetrics(&serviceMetrics); err != nil { log.Printf("[MoniGo] Error storing service metrics: %v\n", err) } - timer.Reset(freqTime) } } }() diff --git a/timeseries/storeMetrics.go b/timeseries/storeMetrics.go index 5b80e61..f738838 100644 --- a/timeseries/storeMetrics.go +++ b/timeseries/storeMetrics.go @@ -2,9 +2,6 @@ package timeseries import ( "fmt" - "regexp" - "strconv" - "strings" "time" "github.com/iyashjayesh/monigo/models" @@ -49,20 +46,6 @@ func StoreServiceMetrics(serviceMetrics *models.ServiceStats) error { return nil } -// Helper function to remove percentage from a string. -func RemovePercentage(s string) float64 { - var val float64 - fmt.Sscanf(s, "%f", &val) - return val -} - -// Helper function to convert a string to a formatted float. -func StringToFloat(s string) float64 { - val, _ := strconv.ParseFloat(s, 64) - val, _ = strconv.ParseFloat(fmt.Sprintf("%.4f", val), 64) - return val -} - // generateCoreStatsRows generates rows for core statistics. func generateCoreStatsRows(serviceMetrics *models.ServiceStats, label tstorage.Label, timestamp int64) []tstorage.Row { return []tstorage.Row{ @@ -71,41 +54,40 @@ func generateCoreStatsRows(serviceMetrics *models.ServiceStats, label tstorage.L DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.CoreStatistics.Goroutines)}, Labels: []tstorage.Label{label}, }, - // { - // Metric: "request_count", - // DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.CoreStatistics.RequestCount)}, - // Labels: []tstorage.Label{label}, - // }, } } // generateLoadStatsRows generates rows for load statistics. func generateLoadStatsRows(serviceMetrics *models.ServiceStats, label tstorage.Label, timestamp int64) []tstorage.Row { - return []tstorage.Row{ { Metric: "overall_load_of_service", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: RemovePercentage(serviceMetrics.LoadStatistics.OverallLoadOfService)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.OverallLoadOfServiceRaw}, Labels: []tstorage.Label{label}, }, { Metric: "service_cpu_load", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: RemovePercentage(serviceMetrics.LoadStatistics.ServiceCPULoad)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.ServiceCPULoadRaw}, Labels: []tstorage.Label{label}, }, { Metric: "service_memory_load", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: RemovePercentage(serviceMetrics.LoadStatistics.ServiceMemLoad)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.ServiceMemLoadRaw}, Labels: []tstorage.Label{label}, }, { Metric: "system_cpu_load", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: RemovePercentage(serviceMetrics.LoadStatistics.SystemCPULoad)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.SystemCPULoadRaw}, Labels: []tstorage.Label{label}, }, { Metric: "system_memory_load", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: RemovePercentage(serviceMetrics.LoadStatistics.SystemMemLoad)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.SystemMemLoadRaw}, + Labels: []tstorage.Label{label}, + }, + { + Metric: "system_disk_load", + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.SystemDiskLoadRaw}, Labels: []tstorage.Label{label}, }, } @@ -132,52 +114,37 @@ func generateCPUStatsRows(serviceMetrics *models.ServiceStats, label tstorage.La } } -func extractFloat(s string) float64 { - re := regexp.MustCompile(`\d+(\.\d+)?`) - match := re.FindString(strings.TrimSpace(s)) - if match == "" { - return 0.0 - } - - value, err := strconv.ParseFloat(match, 64) - if err != nil { - return 0.0 - } - - return value -} - // generateMemoryStatsRows generates rows for memory statistics. func generateMemoryStatsRows(serviceMetrics *models.ServiceStats, label tstorage.Label, timestamp int64) []tstorage.Row { rows := []tstorage.Row{ { Metric: "total_system_memory", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.TotalSystemMemory)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.TotalSystemMemoryRaw}, Labels: []tstorage.Label{label}, }, { Metric: "memory_used_by_system", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.MemoryUsedBySystem)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.MemoryUsedBySystemRaw}, Labels: []tstorage.Label{label}, }, { Metric: "memory_used_by_service", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.MemoryUsedByService)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.MemoryUsedByServiceRaw}, Labels: []tstorage.Label{label}, }, { Metric: "available_memory", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.AvailableMemory)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.AvailableMemoryRaw}, Labels: []tstorage.Label{label}, }, { Metric: "gc_pause_duration", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.GCPauseDuration)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.GCPauseDurationRaw}, Labels: []tstorage.Label{label}, }, { Metric: "stack_memory_usage", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.MemoryStatistics.StackMemoryUsage)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.MemoryStatistics.StackMemoryUsageRaw}, Labels: []tstorage.Label{label}, }, } @@ -195,22 +162,27 @@ func generateMemoryStatsRows(serviceMetrics *models.ServiceStats, label tstorage rows = append(rows, []tstorage.Row{ { Metric: "heap_alloc_by_service", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.HeapAllocByService)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.HeapAllocByServiceRaw)}, Labels: []tstorage.Label{label}, }, { Metric: "heap_alloc_by_system", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.HeapAllocBySystem)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.HeapAllocBySystemRaw)}, Labels: []tstorage.Label{label}, }, { Metric: "total_alloc_by_service", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.TotalAllocByService)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.TotalAllocByServiceRaw)}, Labels: []tstorage.Label{label}, }, { Metric: "total_memory_by_os", - DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: extractFloat(serviceMetrics.TotalMemoryByOS)}, + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: float64(serviceMetrics.TotalMemoryByOSRaw)}, + Labels: []tstorage.Label{label}, + }, + { + Metric: "total_disk_size", + DataPoint: tstorage.DataPoint{Timestamp: timestamp, Value: serviceMetrics.LoadStatistics.TotalDiskLoadRaw}, Labels: []tstorage.Label{label}, }, }...)