Skip to content

Commit

Permalink
Merge pull request #5 from heroku/handle-summaries
Browse files Browse the repository at this point in the history
Support SUMMARY in serialization to Measurements.
  • Loading branch information
apg authored Mar 22, 2017
2 parents 5c39d35 + 15e1487 commit 7184b1d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 7 deletions.
36 changes: 35 additions & 1 deletion prom/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,23 @@ func familyToMeasurements(mf *dto.MetricFamily) (out []*ag.Measurement, ok bool)
})
ok = true
}
case dto.MetricType_SUMMARY:
for _, m := range mf.Metric {
summary := m.GetSummary()
out = append(out, &ag.Measurement{
Name: name + suffixFor(m),
Timestamp: msToTime(m.GetTimestampMs()),
Type: "g",
Value: summary.GetSampleSum(),
})
out = append(out, &ag.Measurement{
Name: name + suffixFor(m),
Timestamp: msToTime(m.GetTimestampMs()),
Type: "c",
Value: float64(summary.GetSampleCount()),
})
ok = true
}
}
return
}
Expand Down Expand Up @@ -203,11 +220,28 @@ func suffixFor(m *dto.Metric) string {
result := make([]string, 0, len(m.Label))

for _, lp := range m.Label {
result = append(result, lp.GetName()+"_"+lp.GetValue())
labelName := strings.Map(charMapper, lp.GetName())
labelVal := strings.Map(charMapper, lp.GetValue())
result = append(result, labelName+"_"+labelVal)
}

if len(result) == 0 {
return ""
}
return "." + strings.Join(result, ".")
}

func charMapper(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':
return r
case r >= 'a' && r <= 'z':
return r
case r >= '0' && r <= '9':
return r
case r == '-' || r == '_' || r == '.':
return r
default:
return '_'
}
}
77 changes: 71 additions & 6 deletions prom/poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ func setup() (*url.URL, map[string]float64, func()) {
)
gaugeVec.WithLabelValues("office").Add(75)
gaugeVec.WithLabelValues("kitchen").Add(76)
gaugeVec.WithLabelValues("pantry #1").Add(71)
reg.MustRegister(gaugeVec)

expectations := map[string]float64{
"some_counter.code_200.type_http": 1,
"some_counter.code_500.type_http": 1,
"some_gauge.location_office.type_temperature": 75,
"some_gauge.location_kitchen.type_temperature": 76,
"some_counter.code_200.type_http": 1,
"some_counter.code_500.type_http": 1,
"some_gauge.location_office.type_temperature": 75,
"some_gauge.location_kitchen.type_temperature": 76,
"some_gauge.location_pantry__1.type_temperature": 71,
}

buf := &bytes.Buffer{}
Expand Down Expand Up @@ -121,7 +123,70 @@ func testPollerForType(t *testing.T, u *url.URL, exp map[string]float64, acceptH
}
}

func fakeMetricFamily() (*dto.MetricFamily, []*am.Measurement) {
func fakeSummaryFamily() (*dto.MetricFamily, []*am.Measurement) {
name := "some_summary"
path := "path"
index := "index"
typ := dto.MetricType_SUMMARY
cnt := uint64(2)
sum := float64(20.0)

return &dto.MetricFamily{
Name: &name,
Type: &typ,
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
&dto.LabelPair{Name: &path, Value: &index},
},
Summary: &dto.Summary{SampleCount: &cnt, SampleSum: &sum},
},
},
}, []*am.Measurement{
{
Name: "some_summary.path_index",
Value: 20,
Type: "g",
},
{
Name: "some_summary.path_index",
Value: 2,
Type: "c",
},
}
}

// Summaries are time based, and so very hard to actually test as
// integration tests with a web handler as we can with counters and
// gauges. This test provides much of the same functionality as
// TestPromPoller, but assumes summaries will simply be included in
// the exposition.
func TestSummaryNaming(t *testing.T) {
family, exps := fakeSummaryFamily()

out, ok := familyToMeasurements(family)
if !ok {
t.Fatalf("got %b, want true", ok)
}
if len(out) != 2 {
t.Fatalf("got len(%d), want len(2)", len(out))
}

for i, got := range out {
want := exps[i]
if want.Name != got.Name {
t.Errorf("want(name) = %v, got(name) = %v", want.Name, got.Name)
}
if want.Value != got.Value {
t.Errorf("want(value) = %f, got(value) = %f", want.Value, got.Value)
}
if want.Type != got.Type {
t.Errorf("want(type) = %v, got(type) = %v", want.Type, got.Type)
}
}
}

func fakeCounterFamily() (*dto.MetricFamily, []*am.Measurement) {
sc := "some_counter"
mt := dto.MetricType_COUNTER
t00 := "200"
Expand Down Expand Up @@ -166,7 +231,7 @@ func fakeMetricFamily() (*dto.MetricFamily, []*am.Measurement) {
}

func TestPollerSync(t *testing.T) {
mf, expected := fakeMetricFamily()
mf, expected := fakeCounterFamily()

inbox := make(chan *am.Measurement, 2)
poller := Poller{Inbox: inbox}
Expand Down

0 comments on commit 7184b1d

Please sign in to comment.