diff --git a/README.md b/README.md index 91c4bda29e7ce32542bc8ab00354625e84666ad3..73357f3cd75ca3d993fa5616a444157aeb2e40c2 100644 --- a/README.md +++ b/README.md @@ -104,3 +104,11 @@ metrics, a "global" dynstats namespace is also published with some additional bo See the [dyn_stats](https://www.rsyslog.com/doc/master/configuration/dyn_stats.html) documentation for more information. + +### IMUDP Workerthread stats +The [imudp](https://www.rsyslog.com/rsyslog-statistic-counter-plugin-imudp/) module can be configured +to run on multiple worker threads and the following metrics are returned: + +* input_called_recvmmsg - Number of recvmmsg called +* input_called_recvmsg -Number of recvmmsg called +* input_received - Messages received diff --git a/exporter.go b/exporter.go index 45f9ddff56e7d3d672d65538a6b388be5a461725..5ee725c340abf07ed548ddd485b66d81e74d6be9 100644 --- a/exporter.go +++ b/exporter.go @@ -21,6 +21,7 @@ const ( rsyslogResource rsyslogDynStat rsyslogDynafileCache + rsyslogInputIMDUP ) type rsyslogExporter struct { @@ -69,6 +70,15 @@ func (re *rsyslogExporter) handleStatLine(rawbuf []byte) error { re.set(p) } + case rsyslogInputIMDUP: + u, err := newInputIMUDPFromJSON(buf) + if err != nil { + return err + } + for _, p := range u.toPoints() { + re.set(p) + } + case rsyslogQueue: q, err := newQueueFromJSON(buf) if err != nil { diff --git a/input_imudp.go b/input_imudp.go new file mode 100644 index 0000000000000000000000000000000000000000..13b5ec0a3eeb3c33aa941ef80822764b33ce6e78 --- /dev/null +++ b/input_imudp.go @@ -0,0 +1,54 @@ +package main + +import ( + "encoding/json" + "fmt" +) + +type inputIMUDP struct { + Name string `json:"name"` + Recvmmsg int64 `json:"called.recvmmsg"` + Recvmsg int64 `json:"called.recvmsg"` + Received int64 `json:"msgs.received"` +} + +func newInputIMUDPFromJSON(b []byte) (*inputIMUDP, error) { + var pstat inputIMUDP + err := json.Unmarshal(b, &pstat) + if err != nil { + return nil, fmt.Errorf("error decoding input stat `%v`: %v", string(b), err) + } + return &pstat, nil +} + +func (i *inputIMUDP) toPoints() []*point { + points := make([]*point, 3) + + points[0] = &point{ + Name: "input_called_recvmmsg", + Type: counter, + Value: i.Recvmmsg, + Description: "Number of recvmmsg called", + LabelName: "worker", + LabelValue: i.Name, + } + points[1] = &point{ + Name: "input_called_recvmsg", + Type: counter, + Value: i.Recvmsg, + Description: "Number of recvmmsg called", + LabelName: "worker", + LabelValue: i.Name, + } + + points[2] = &point{ + Name: "input_received", + Type: counter, + Value: i.Received, + Description: "messages received", + LabelName: "worker", + LabelValue: i.Name, + } + + return points +} diff --git a/input_imudp_test.go b/input_imudp_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8c98b6301a60cb459ca8142ac83d2d58139afd06 --- /dev/null +++ b/input_imudp_test.go @@ -0,0 +1,83 @@ +package main + +import "testing" + +var ( + inputIMUDPLog = []byte(`{ "name": "test_input_imudp", "origin": "imudp", "called.recvmmsg":1000, "called.recvmsg":2000, "msgs.received":500}`) +) + +func TestgetInputIMUDP(t *testing.T) { + logType := getStatType(inputIMUDPLog) + if logType != rsyslogInputIMDUP { + t.Errorf("detected pstat type should be %d but is %d", rsyslogInputIMDUP, logType) + } + + pstat, err := newInputIMUDPFromJSON([]byte(inputLog)) + if err != nil { + t.Fatalf("expected parsing input stat not to fail, got: %v", err) + } + + if want, got := "test_input_imudp", pstat.Name; want != got { + t.Errorf("want '%s', got '%s'", want, got) + } + + if want, got := int64(1000), pstat.Recvmsg; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } + + if want, got := int64(2000), pstat.Recvmmsg; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } + + if want, got := int64(500), pstat.Received; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } +} + +func TestInputIMUDPtoPoints(t *testing.T) { + pstat, err := newInputIMUDPFromJSON([]byte(inputIMUDPLog)) + if err != nil { + t.Fatalf("expected parsing input stat not to fail, got: %v", err) + } + + points := pstat.toPoints() + + point := points[0] + if want, got := "input_called_recvmmsg", point.Name; want != got { + t.Errorf("want '%s', got '%s'", want, got) + } + + if want, got := int64(1000), point.Value; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } + + if want, got := "test_input_imudp", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[1] + if want, got := "input_called_recvmsg", point.Name; want != got { + t.Errorf("want '%s', got '%s'", want, got) + } + + if want, got := int64(2000), point.Value; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } + + if want, got := "test_input_imudp", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } + + point = points[2] + if want, got := "input_received", point.Name; want != got { + t.Errorf("want '%s', got '%s'", want, got) + } + + if want, got := int64(500), point.Value; want != got { + t.Errorf("want '%d', got '%d'", want, got) + } + + if want, got := "test_input_imudp", point.LabelValue; want != got { + t.Errorf("wanted '%s', got '%s'", want, got) + } +} diff --git a/utils.go b/utils.go index ad717d33c0d2a40e142ae139f12defe4235d3a8a..1cd38e8fca5ed31a470412ce896505c99d4dbf70 100644 --- a/utils.go +++ b/utils.go @@ -8,6 +8,8 @@ func getStatType(buf []byte) rsyslogType { return rsyslogAction } else if strings.Contains(line, "submitted") { return rsyslogInput + } else if strings.Contains(line, "called.recvmmsg") { + return rsyslogInputIMDUP } else if strings.Contains(line, "enqueued") { return rsyslogQueue } else if strings.Contains(line, "utime") {