github.com/safing/portbase@v0.19.5/metrics/metrics_host.go (about)

     1  package metrics
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/shirou/gopsutil/disk"
     9  	"github.com/shirou/gopsutil/load"
    10  	"github.com/shirou/gopsutil/mem"
    11  
    12  	"github.com/safing/portbase/api"
    13  	"github.com/safing/portbase/dataroot"
    14  	"github.com/safing/portbase/log"
    15  )
    16  
    17  const hostStatTTL = 1 * time.Second
    18  
    19  func registerHostMetrics() (err error) {
    20  	// Register load average metrics.
    21  	_, err = NewGauge("host/load/avg/1", nil, getFloat64HostStat(LoadAvg1), &Options{Name: "Host Load Avg 1min", Permission: api.PermitUser})
    22  	if err != nil {
    23  		return err
    24  	}
    25  	_, err = NewGauge("host/load/avg/5", nil, getFloat64HostStat(LoadAvg5), &Options{Name: "Host Load Avg 5min", Permission: api.PermitUser})
    26  	if err != nil {
    27  		return err
    28  	}
    29  	_, err = NewGauge("host/load/avg/15", nil, getFloat64HostStat(LoadAvg15), &Options{Name: "Host Load Avg 15min", Permission: api.PermitUser})
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	// Register memory usage metrics.
    35  	_, err = NewGauge("host/mem/total", nil, getUint64HostStat(MemTotal), &Options{Name: "Host Memory Total", Permission: api.PermitUser})
    36  	if err != nil {
    37  		return err
    38  	}
    39  	_, err = NewGauge("host/mem/used", nil, getUint64HostStat(MemUsed), &Options{Name: "Host Memory Used", Permission: api.PermitUser})
    40  	if err != nil {
    41  		return err
    42  	}
    43  	_, err = NewGauge("host/mem/available", nil, getUint64HostStat(MemAvailable), &Options{Name: "Host Memory Available", Permission: api.PermitUser})
    44  	if err != nil {
    45  		return err
    46  	}
    47  	_, err = NewGauge("host/mem/used/percent", nil, getFloat64HostStat(MemUsedPercent), &Options{Name: "Host Memory Used in Percent", Permission: api.PermitUser})
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	// Register disk usage metrics.
    53  	_, err = NewGauge("host/disk/total", nil, getUint64HostStat(DiskTotal), &Options{Name: "Host Disk Total", Permission: api.PermitUser})
    54  	if err != nil {
    55  		return err
    56  	}
    57  	_, err = NewGauge("host/disk/used", nil, getUint64HostStat(DiskUsed), &Options{Name: "Host Disk Used", Permission: api.PermitUser})
    58  	if err != nil {
    59  		return err
    60  	}
    61  	_, err = NewGauge("host/disk/free", nil, getUint64HostStat(DiskFree), &Options{Name: "Host Disk Free", Permission: api.PermitUser})
    62  	if err != nil {
    63  		return err
    64  	}
    65  	_, err = NewGauge("host/disk/used/percent", nil, getFloat64HostStat(DiskUsedPercent), &Options{Name: "Host Disk Used in Percent", Permission: api.PermitUser})
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  func getUint64HostStat(getStat func() (uint64, bool)) func() float64 {
    74  	return func() float64 {
    75  		val, _ := getStat()
    76  		return float64(val)
    77  	}
    78  }
    79  
    80  func getFloat64HostStat(getStat func() (float64, bool)) func() float64 {
    81  	return func() float64 {
    82  		val, _ := getStat()
    83  		return val
    84  	}
    85  }
    86  
    87  var (
    88  	loadAvg        *load.AvgStat
    89  	loadAvgExpires time.Time
    90  	loadAvgLock    sync.Mutex
    91  )
    92  
    93  func getLoadAvg() *load.AvgStat {
    94  	loadAvgLock.Lock()
    95  	defer loadAvgLock.Unlock()
    96  
    97  	// Return cache if still valid.
    98  	if time.Now().Before(loadAvgExpires) {
    99  		return loadAvg
   100  	}
   101  
   102  	// Refresh.
   103  	var err error
   104  	loadAvg, err = load.Avg()
   105  	if err != nil {
   106  		log.Warningf("metrics: failed to get load avg: %s", err)
   107  		loadAvg = nil
   108  	}
   109  	loadAvgExpires = time.Now().Add(hostStatTTL)
   110  
   111  	return loadAvg
   112  }
   113  
   114  // LoadAvg1 returns the 1-minute average system load.
   115  func LoadAvg1() (loadAvg float64, ok bool) {
   116  	if stat := getLoadAvg(); stat != nil {
   117  		return stat.Load1 / float64(runtime.NumCPU()), true
   118  	}
   119  	return 0, false
   120  }
   121  
   122  // LoadAvg5 returns the 5-minute average system load.
   123  func LoadAvg5() (loadAvg float64, ok bool) {
   124  	if stat := getLoadAvg(); stat != nil {
   125  		return stat.Load5 / float64(runtime.NumCPU()), true
   126  	}
   127  	return 0, false
   128  }
   129  
   130  // LoadAvg15 returns the 15-minute average system load.
   131  func LoadAvg15() (loadAvg float64, ok bool) {
   132  	if stat := getLoadAvg(); stat != nil {
   133  		return stat.Load15 / float64(runtime.NumCPU()), true
   134  	}
   135  	return 0, false
   136  }
   137  
   138  var (
   139  	memStat        *mem.VirtualMemoryStat
   140  	memStatExpires time.Time
   141  	memStatLock    sync.Mutex
   142  )
   143  
   144  func getMemStat() *mem.VirtualMemoryStat {
   145  	memStatLock.Lock()
   146  	defer memStatLock.Unlock()
   147  
   148  	// Return cache if still valid.
   149  	if time.Now().Before(memStatExpires) {
   150  		return memStat
   151  	}
   152  
   153  	// Refresh.
   154  	var err error
   155  	memStat, err = mem.VirtualMemory()
   156  	if err != nil {
   157  		log.Warningf("metrics: failed to get load avg: %s", err)
   158  		memStat = nil
   159  	}
   160  	memStatExpires = time.Now().Add(hostStatTTL)
   161  
   162  	return memStat
   163  }
   164  
   165  // MemTotal returns the total system memory.
   166  func MemTotal() (total uint64, ok bool) {
   167  	if stat := getMemStat(); stat != nil {
   168  		return stat.Total, true
   169  	}
   170  	return 0, false
   171  }
   172  
   173  // MemUsed returns the used system memory.
   174  func MemUsed() (used uint64, ok bool) {
   175  	if stat := getMemStat(); stat != nil {
   176  		return stat.Used, true
   177  	}
   178  	return 0, false
   179  }
   180  
   181  // MemAvailable returns the available system memory.
   182  func MemAvailable() (available uint64, ok bool) {
   183  	if stat := getMemStat(); stat != nil {
   184  		return stat.Available, true
   185  	}
   186  	return 0, false
   187  }
   188  
   189  // MemUsedPercent returns the percent of used system memory.
   190  func MemUsedPercent() (usedPercent float64, ok bool) {
   191  	if stat := getMemStat(); stat != nil {
   192  		return stat.UsedPercent, true
   193  	}
   194  	return 0, false
   195  }
   196  
   197  var (
   198  	diskStat        *disk.UsageStat
   199  	diskStatExpires time.Time
   200  	diskStatLock    sync.Mutex
   201  )
   202  
   203  func getDiskStat() *disk.UsageStat {
   204  	diskStatLock.Lock()
   205  	defer diskStatLock.Unlock()
   206  
   207  	// Return cache if still valid.
   208  	if time.Now().Before(diskStatExpires) {
   209  		return diskStat
   210  	}
   211  
   212  	// Check if we have a data root.
   213  	dataRoot := dataroot.Root()
   214  	if dataRoot == nil {
   215  		log.Warning("metrics: cannot get disk stats without data root")
   216  		diskStat = nil
   217  		diskStatExpires = time.Now().Add(hostStatTTL)
   218  		return diskStat
   219  	}
   220  
   221  	// Refresh.
   222  	var err error
   223  	diskStat, err = disk.Usage(dataRoot.Path)
   224  	if err != nil {
   225  		log.Warningf("metrics: failed to get load avg: %s", err)
   226  		diskStat = nil
   227  	}
   228  	diskStatExpires = time.Now().Add(hostStatTTL)
   229  
   230  	return diskStat
   231  }
   232  
   233  // DiskTotal returns the total disk space (from the program's data root).
   234  func DiskTotal() (total uint64, ok bool) {
   235  	if stat := getDiskStat(); stat != nil {
   236  		return stat.Total, true
   237  	}
   238  	return 0, false
   239  }
   240  
   241  // DiskUsed returns the used disk space (from the program's data root).
   242  func DiskUsed() (used uint64, ok bool) {
   243  	if stat := getDiskStat(); stat != nil {
   244  		return stat.Used, true
   245  	}
   246  	return 0, false
   247  }
   248  
   249  // DiskFree returns the available disk space (from the program's data root).
   250  func DiskFree() (free uint64, ok bool) {
   251  	if stat := getDiskStat(); stat != nil {
   252  		return stat.Free, true
   253  	}
   254  	return 0, false
   255  }
   256  
   257  // DiskUsedPercent returns the percent of used disk space (from the program's data root).
   258  func DiskUsedPercent() (usedPercent float64, ok bool) {
   259  	if stat := getDiskStat(); stat != nil {
   260  		return stat.UsedPercent, true
   261  	}
   262  	return 0, false
   263  }