github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-uwsgi-vassal/lib/uwsgi_vassal.go (about)

     1  package mpuwsgivassal
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"flag"
     7  	"net"
     8  	"net/http"
     9  	"strings"
    10  
    11  	mp "github.com/mackerelio/go-mackerel-plugin"
    12  	"golang.org/x/text/cases"
    13  	"golang.org/x/text/language"
    14  )
    15  
    16  // UWSGIVassalPlugin mackerel plugin for uWSGI
    17  type UWSGIVassalPlugin struct {
    18  	Socket      string
    19  	Prefix      string
    20  	LabelPrefix string
    21  }
    22  
    23  // {
    24  //   "workers": [{
    25  //     "id": 1,
    26  //     "pid": 31759,
    27  //     "requests": 0,
    28  //     "exceptions": 0,
    29  //     "status": "idle",
    30  //     "rss": 0,
    31  //     "vsz": 0,
    32  //     "running_time": 0,
    33  //     "last_spawn": 1317235041,
    34  //     "respawn_count": 1,
    35  //     "tx": 0,
    36  //     "avg_rt": 0,
    37  //     "apps": [{
    38  //       "id": 0,
    39  //       "modifier1": 0,
    40  //       "mountpoint": "",
    41  //       "requests": 0,
    42  //       "exceptions": 0,
    43  //       "chdir": ""
    44  //     }]
    45  //   }, {
    46  //     "id": 2,
    47  //     "pid": 31760,
    48  //     "requests": 0,
    49  //     "exceptions": 0,
    50  //     "status": "idle",
    51  //     "rss": 0,
    52  //     "vsz": 0,
    53  //     "running_time": 0,
    54  //     "last_spawn": 1317235041,
    55  //     "respawn_count": 1,
    56  //     "tx": 0,
    57  //     "avg_rt": 0,
    58  //     "apps": [{
    59  //       "id": 0,
    60  //       "modifier1": 0,
    61  //       "mountpoint": "",
    62  //       "requests": 0,
    63  //       "exceptions": 0,
    64  //       "chdir": ""
    65  //     }]
    66  //   }, {
    67  //     "id": 3,
    68  //     "pid": 31761,
    69  //     "requests": 0,
    70  //     "exceptions": 0,
    71  //     "status": "idle",
    72  //     "rss": 0,
    73  //     "vsz": 0,
    74  //     "running_time": 0,
    75  //     "last_spawn": 1317235041,
    76  //     "respawn_count": 1,
    77  //     "tx": 0,
    78  //     "avg_rt": 0,
    79  //     "apps": [{
    80  //       "id": 0,
    81  //       "modifier1": 0,
    82  //       "mountpoint": "",
    83  //       "requests": 0,
    84  //       "exceptions": 0,
    85  //       "chdir": ""
    86  //     }]
    87  //   }, {
    88  //     "id": 4,
    89  //     "pid": 31762,
    90  //     "requests": 0,
    91  //     "exceptions": 0,
    92  //     "status": "idle",
    93  //     "rss": 0,
    94  //     "vsz": 0,
    95  //     "running_time": 0,
    96  //     "last_spawn": 1317235041,
    97  //     "respawn_count": 1,
    98  //     "tx": 0,
    99  //     "avg_rt": 0,
   100  //     "apps": [{
   101  //       "id": 0,
   102  //       "modifier1": 0,
   103  //       "mountpoint": "",
   104  //       "requests": 0,
   105  //       "exceptions": 0,
   106  //       "chdir": ""
   107  //     }]
   108  //   }
   109  // }
   110  
   111  // field types vary between versions
   112  
   113  // UWSGIWorker struct
   114  type UWSGIWorker struct {
   115  	Requests uint64 `json:"requests"`
   116  	Status   string `json:"status"`
   117  }
   118  
   119  // UWSGIWorkers sturct for json struct
   120  type UWSGIWorkers struct {
   121  	Workers []UWSGIWorker `json:"workers"`
   122  }
   123  
   124  // FetchMetrics interface for mackerelplugin
   125  func (p UWSGIVassalPlugin) FetchMetrics() (map[string]float64, error) {
   126  	stat := make(map[string]float64)
   127  	stat["busy"] = 0.0
   128  	stat["idle"] = 0.0
   129  	stat["cheap"] = 0.0
   130  	stat["pause"] = 0.0
   131  	stat["requests"] = 0.0
   132  
   133  	var decoder *json.Decoder
   134  	if strings.HasPrefix(p.Socket, "unix://") {
   135  		conn, err := net.Dial("unix", strings.TrimPrefix(p.Socket, "unix://"))
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  		defer conn.Close()
   140  		decoder = json.NewDecoder(conn)
   141  	} else if strings.HasPrefix(p.Socket, "http://") {
   142  		req, err := http.NewRequest(http.MethodGet, p.Socket, nil)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		req.Header.Set("User-Agent", "mackerel-plugin-uwsgi-vessal")
   147  
   148  		resp, err := http.DefaultClient.Do(req)
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  		defer resp.Body.Close()
   153  		decoder = json.NewDecoder(resp.Body)
   154  	} else {
   155  		err := errors.New("'--socket' is neither http endpoint nor the unix domain socket, try '--help' for more information")
   156  		return nil, err
   157  	}
   158  
   159  	var workers UWSGIWorkers
   160  	if err := decoder.Decode(&workers); err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	for _, worker := range workers.Workers {
   165  		switch worker.Status {
   166  		case "idle", "busy", "cheap", "pause":
   167  			stat[worker.Status]++
   168  		}
   169  		stat["requests"] += float64(worker.Requests)
   170  	}
   171  
   172  	return stat, nil
   173  }
   174  
   175  // GraphDefinition interface for mackerelplugin
   176  func (p UWSGIVassalPlugin) GraphDefinition() map[string]mp.Graphs {
   177  	labelPrefix := cases.Title(language.Und, cases.NoLower).String(p.Prefix)
   178  
   179  	var graphdef = map[string]mp.Graphs{
   180  		(p.Prefix + ".workers"): {
   181  			Label: labelPrefix + " Workers",
   182  			Unit:  "integer",
   183  			Metrics: []mp.Metrics{
   184  				{Name: "busy", Label: "Busy", Diff: false, Stacked: true},
   185  				{Name: "idle", Label: "Idle", Diff: false, Stacked: true},
   186  				{Name: "cheap", Label: "Cheap", Diff: false, Stacked: true},
   187  				{Name: "pause", Label: "Pause", Diff: false, Stacked: true},
   188  			},
   189  		},
   190  		(p.Prefix + ".req"): {
   191  			Label: labelPrefix + " Requests",
   192  			Unit:  "float",
   193  			Metrics: []mp.Metrics{
   194  				{Name: "requests", Label: "Requests", Diff: true},
   195  			},
   196  		},
   197  	}
   198  
   199  	return graphdef
   200  }
   201  
   202  // MetricKeyPrefix interface for PluginWithPrefix
   203  func (p UWSGIVassalPlugin) MetricKeyPrefix() string {
   204  	if p.Prefix == "" {
   205  		p.Prefix = "uWSGI"
   206  	}
   207  	return p.Prefix
   208  }
   209  
   210  // Do the plugin
   211  func Do() {
   212  	optSocket := flag.String("socket", "", "Socket (must be with prefix of 'http://' or 'unix://')")
   213  	optPrefix := flag.String("metric-key-prefix", "uWSGI", "Prefix")
   214  	optTempfile := flag.String("tempfile", "", "Temp file name")
   215  	flag.Parse()
   216  
   217  	uwsgi := UWSGIVassalPlugin{Socket: *optSocket, Prefix: *optPrefix}
   218  	uwsgi.LabelPrefix = cases.Title(language.Und, cases.NoLower).String(uwsgi.Prefix)
   219  
   220  	helper := mp.NewMackerelPlugin(uwsgi)
   221  	helper.Tempfile = *optTempfile
   222  	helper.Run()
   223  }