github.com/netdata/go.d.plugin@v0.58.1/modules/supervisord/collect.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package supervisord
     4  
     5  import (
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/netdata/go.d.plugin/agent/module"
    10  )
    11  
    12  func (s *Supervisord) collect() (map[string]int64, error) {
    13  	info, err := s.client.getAllProcessInfo()
    14  	if err != nil {
    15  		return nil, err
    16  	}
    17  
    18  	ms := make(map[string]int64)
    19  	s.collectAllProcessInfo(ms, info)
    20  
    21  	return ms, nil
    22  }
    23  
    24  func (s *Supervisord) collectAllProcessInfo(ms map[string]int64, info []processStatus) {
    25  	s.resetCache()
    26  	ms["running_processes"] = 0
    27  	ms["non_running_processes"] = 0
    28  	for _, p := range info {
    29  		if _, ok := s.cache[p.group]; !ok {
    30  			s.cache[p.group] = make(map[string]bool)
    31  			s.addProcessGroupCharts(p)
    32  		}
    33  		if _, ok := s.cache[p.group][p.name]; !ok {
    34  			s.addProcessToCharts(p)
    35  		}
    36  		s.cache[p.group][p.name] = true
    37  
    38  		ms["group_"+p.group+"_running_processes"] += 0
    39  		ms["group_"+p.group+"_non_running_processes"] += 0
    40  		if isProcRunning(p) {
    41  			ms["running_processes"] += 1
    42  			ms["group_"+p.group+"_running_processes"] += 1
    43  		} else {
    44  			ms["non_running_processes"] += 1
    45  			ms["group_"+p.group+"_non_running_processes"] += 1
    46  		}
    47  		id := procID(p)
    48  		ms[id+"_state_code"] = int64(p.state)
    49  		ms[id+"_exit_status"] = int64(p.exitStatus)
    50  		ms[id+"_uptime"] = calcProcessUptime(p)
    51  		ms[id+"_downtime"] = calcProcessDowntime(p)
    52  	}
    53  	s.cleanupCache()
    54  }
    55  
    56  func (s *Supervisord) resetCache() {
    57  	for _, procs := range s.cache {
    58  		for name := range procs {
    59  			procs[name] = false
    60  		}
    61  	}
    62  }
    63  
    64  func (s *Supervisord) cleanupCache() {
    65  	for group, procs := range s.cache {
    66  		for name, ok := range procs {
    67  			if !ok {
    68  				s.removeProcessFromCharts(group, name)
    69  				delete(s.cache[group], name)
    70  			}
    71  		}
    72  		if len(s.cache[group]) == 0 {
    73  			s.removeProcessGroupCharts(group)
    74  			delete(s.cache, group)
    75  		}
    76  	}
    77  }
    78  
    79  func calcProcessUptime(p processStatus) int64 {
    80  	if !isProcRunning(p) {
    81  		return 0
    82  	}
    83  	return int64(p.now - p.start)
    84  }
    85  
    86  func calcProcessDowntime(p processStatus) int64 {
    87  	if isProcRunning(p) || p.stop == 0 {
    88  		return 0
    89  	}
    90  	return int64(p.now - p.stop)
    91  }
    92  
    93  func (s *Supervisord) addProcessGroupCharts(p processStatus) {
    94  	charts := newProcGroupCharts(p.group)
    95  	if err := s.Charts().Add(*charts...); err != nil {
    96  		s.Warning(err)
    97  	}
    98  }
    99  
   100  func (s *Supervisord) addProcessToCharts(p processStatus) {
   101  	id := procID(p)
   102  	for _, c := range *s.Charts() {
   103  		var dimID string
   104  		switch c.ID {
   105  		case fmt.Sprintf(groupProcessesStateCodeChartTmpl.ID, p.group):
   106  			dimID = id + "_state_code"
   107  		case fmt.Sprintf(groupProcessesExitStatusChartTmpl.ID, p.group):
   108  			dimID = id + "_exit_status"
   109  		case fmt.Sprintf(groupProcessesUptimeChartTmpl.ID, p.group):
   110  			dimID = id + "_uptime"
   111  		case fmt.Sprintf(groupProcessesDowntimeChartTmpl.ID, p.group):
   112  			dimID = id + "_downtime"
   113  		default:
   114  			continue
   115  		}
   116  		dim := &module.Dim{ID: dimID, Name: p.name}
   117  		if err := c.AddDim(dim); err != nil {
   118  			s.Warning(err)
   119  			return
   120  		}
   121  		c.MarkNotCreated()
   122  	}
   123  }
   124  
   125  func (s *Supervisord) removeProcessGroupCharts(group string) {
   126  	prefix := "group_" + group
   127  	for _, c := range *s.Charts() {
   128  		if strings.HasPrefix(c.ID, prefix) {
   129  			c.MarkRemove()
   130  			c.MarkNotCreated()
   131  		}
   132  	}
   133  }
   134  
   135  func (s *Supervisord) removeProcessFromCharts(group, name string) {
   136  	id := procID(processStatus{name: name, group: group})
   137  	for _, c := range *s.Charts() {
   138  		var dimID string
   139  		switch c.ID {
   140  		case fmt.Sprintf(groupProcessesStateCodeChartTmpl.ID, group):
   141  			dimID = id + "_state_code"
   142  		case fmt.Sprintf(groupProcessesExitStatusChartTmpl.ID, group):
   143  			dimID = id + "_exit_status"
   144  		case fmt.Sprintf(groupProcessesUptimeChartTmpl.ID, group):
   145  			dimID = id + "_uptime"
   146  		case fmt.Sprintf(groupProcessesDowntimeChartTmpl.ID, group):
   147  			dimID = id + "_downtime"
   148  		default:
   149  			continue
   150  		}
   151  		if err := c.MarkDimRemove(dimID, true); err != nil {
   152  			s.Warning(err)
   153  			return
   154  		}
   155  		c.MarkNotCreated()
   156  	}
   157  }
   158  
   159  func procID(p processStatus) string {
   160  	return fmt.Sprintf("group_%s_process_%s", p.group, p.name)
   161  }
   162  
   163  func isProcRunning(p processStatus) bool {
   164  	// http://supervisord.org/subprocess.html#process-states
   165  	// STOPPED  (0)
   166  	// STARTING (10)
   167  	// RUNNING (20)
   168  	// BACKOFF (30)
   169  	// STOPPING (40)
   170  	// EXITED (100)
   171  	// FATAL (200)
   172  	// UNKNOWN (1000)
   173  	return p.state == 20
   174  }