Skip to content
Snippets Groups Projects
Commit a11cc758 authored by Matthias Rampke's avatar Matthias Rampke Committed by ale
Browse files
parent e145901f
No related branches found
No related tags found
No related merge requests found
package main
import (
"encoding/json"
"fmt"
)
type dynStat struct {
Name string `json:"name"`
Origin string `json:"origin"`
Values map[string]int64 `json:"values"`
}
func newDynStatFromJSON(b []byte) (*dynStat, error) {
var pstat dynStat
err := json.Unmarshal(b, &pstat)
if err != nil {
return nil, fmt.Errorf("error decoding values stat `%v`: %v", string(b), err)
}
return &pstat, nil
}
func (i *dynStat) toPoints() []*point {
points := make([]*point, 0, len(i.Values))
for name, value := range i.Values {
points = append(points, &point{
Name: fmt.Sprintf("dynstat_%s", i.Name),
Type: counter,
Value: value,
Description: fmt.Sprintf("dynamic statistic bucket %s", i.Name),
LabelName: "counter",
LabelValue: name,
})
}
return points
}
package main
import (
"reflect"
"testing"
)
func TestGetDynStat(t *testing.T) {
log := []byte(`{ "name": "global", "origin": "dynstats", "values": { "msg_per_host.ops_overflow": 1, "msg_per_host.new_metric_add": 3, "msg_per_host.no_metric": 0, "msg_per_host.metrics_purged": 0, "msg_per_host.ops_ignored": 0 } }`)
values := map[string]int64{
"msg_per_host.ops_overflow": 1,
"msg_per_host.new_metric_add": 3,
"msg_per_host.no_metric": 0,
"msg_per_host.metrics_purged": 0,
"msg_per_host.ops_ignored": 0,
}
if want, got := rsyslogDynStat, getStatType(log); want != got {
t.Errorf("detected pstat type should be %d but is %d", want, got)
}
pstat, err := newDynStatFromJSON(log)
if err != nil {
t.Fatalf("expected parsing dynamic stat not to fail, got: %v", err)
}
if want, got := "global", pstat.Name; want != got {
t.Errorf("invalid name, want '%s', got '%s'", want, got)
}
if want, got := values, pstat.Values; !reflect.DeepEqual(want, got) {
t.Errorf("unexpected values, want: %+v got: %+v", want, got)
}
}
func TestDynStatToPoints(t *testing.T) {
log := []byte(`{ "name": "global", "origin": "dynstats", "values": { "msg_per_host.ops_overflow": 1, "msg_per_host.new_metric_add": 3, "msg_per_host.no_metric": 0, "msg_per_host.metrics_purged": 0, "msg_per_host.ops_ignored": 0 } }`)
wants := map[string]point{
"msg_per_host.ops_overflow": point{
Name: "dynstat_global",
Type: counter,
Value: 1,
Description: "dynamic statistic bucket global",
LabelName: "counter",
LabelValue: "msg_per_host.ops_overflow",
},
"msg_per_host.new_metric_add": point{
Name: "dynstat_global",
Type: counter,
Value: 3,
Description: "dynamic statistic bucket global",
LabelName: "counter",
LabelValue: "msg_per_host.new_metric_add",
},
"msg_per_host.no_metric": point{
Name: "dynstat_global",
Type: counter,
Value: 0,
Description: "dynamic statistic bucket global",
LabelName: "counter",
LabelValue: "msg_per_host.no_metric",
},
"msg_per_host.metrics_purged": point{
Name: "dynstat_global",
Type: counter,
Value: 0,
Description: "dynamic statistic bucket global",
LabelName: "counter",
LabelValue: "msg_per_host.metrics_purged",
},
"msg_per_host.ops_ignored": point{
Name: "dynstat_global",
Type: counter,
Value: 0,
Description: "dynamic statistic bucket global",
LabelName: "counter",
LabelValue: "msg_per_host.ops_ignored",
},
}
seen := map[string]bool{}
for name, _ := range wants {
seen[name] = false
}
pstat, err := newDynStatFromJSON(log)
if err != nil {
t.Fatalf("expected parsing dyn stat not to fail, got: %v", err)
}
points := pstat.toPoints()
for _, got := range points {
key := got.LabelValue
want, ok := wants[key]
if !ok {
t.Errorf("unexpected point, got: %+v", got)
continue
}
if !reflect.DeepEqual(want, *got) {
t.Errorf("expected point to be %+v, got %+v", want, got)
}
if seen[key] {
t.Errorf("point seen multiple times: %+v", got)
}
seen[key] = true
}
for name, ok := range seen {
if !ok {
t.Errorf("expected to see point with key %s, but did not", name)
}
}
}
...@@ -19,6 +19,7 @@ const ( ...@@ -19,6 +19,7 @@ const (
rsyslogInput rsyslogInput
rsyslogQueue rsyslogQueue
rsyslogResource rsyslogResource
rsyslogDynStat
) )
type rsyslogExporter struct { type rsyslogExporter struct {
...@@ -84,6 +85,14 @@ func (re *rsyslogExporter) handleStatLine(rawbuf []byte) error { ...@@ -84,6 +85,14 @@ func (re *rsyslogExporter) handleStatLine(rawbuf []byte) error {
for _, p := range r.toPoints() { for _, p := range r.toPoints() {
re.set(p) re.set(p)
} }
case rsyslogDynStat:
s, err := newDynStatFromJSON(buf)
if err != nil {
return err
}
for _, p := range s.toPoints() {
re.set(p)
}
default: default:
return fmt.Errorf("unknown pstat type: %v", pstatType) return fmt.Errorf("unknown pstat type: %v", pstatType)
......
...@@ -196,6 +196,40 @@ func TestHandleLineWithQueue(t *testing.T) { ...@@ -196,6 +196,40 @@ func TestHandleLineWithQueue(t *testing.T) {
testHelper(t, queueLog, tests) testHelper(t, queueLog, tests)
} }
func TestHandleLineWithGlobal(t *testing.T) {
tests := []*testUnit{
&testUnit{
Name: "dynstat_global",
Val: 1,
LabelValue: "msg_per_host.ops_overflow",
},
&testUnit{
Name: "dynstat_global",
Val: 3,
LabelValue: "msg_per_host.new_metric_add",
},
&testUnit{
Name: "dynstat_global",
Val: 0,
LabelValue: "msg_per_host.no_metric",
},
&testUnit{
Name: "dynstat_global",
Val: 0,
LabelValue: "msg_per_host.metrics_purged",
},
&testUnit{
Name: "dynstat_global",
Val: 0,
LabelValue: "msg_per_host.ops_ignored",
},
}
log := []byte(`2018-01-18T09:39:12.763025+00:00 some-node.example.org rsyslogd-pstats: { "name": "global", "origin": "dynstats", "values": { "msg_per_host.ops_overflow": 1, "msg_per_host.new_metric_add": 3, "msg_per_host.no_metric": 0, "msg_per_host.metrics_purged": 0, "msg_per_host.ops_ignored": 0 } }`)
testHelper(t, log, tests)
}
func TestHandleUnknown(t *testing.T) { func TestHandleUnknown(t *testing.T) {
unknownLog := []byte(`2017-08-30T08:10:04.786350+00:00 some-node.example.org rsyslogd-pstats: {"a":"b"}`) unknownLog := []byte(`2017-08-30T08:10:04.786350+00:00 some-node.example.org rsyslogd-pstats: {"a":"b"}`)
......
...@@ -12,6 +12,8 @@ func getStatType(buf []byte) rsyslogType { ...@@ -12,6 +12,8 @@ func getStatType(buf []byte) rsyslogType {
return rsyslogQueue return rsyslogQueue
} else if strings.Contains(line, "utime") { } else if strings.Contains(line, "utime") {
return rsyslogResource return rsyslogResource
} else if strings.Contains(line, "dynstats") {
return rsyslogDynStat
} }
return rsyslogUnknown return rsyslogUnknown
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment