Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit

Permalink
Add handling of resources in metricdata.Metric (#20)
Browse files Browse the repository at this point in the history
* Add resource handling

* refactoring

* Update go.mod for opencensus

* Improve tests

* Add a test case for mixed overlap and non-overlap
  • Loading branch information
jjzeng-seattle authored Jun 9, 2020
1 parent 2b9ada2 commit 6bcf6f8
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 71 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module contrib.go.opencensus.io/exporter/prometheus
require (
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
github.com/google/go-cmp v0.3.1
github.com/prometheus/client_golang v1.2.1
github.com/prometheus/procfs v0.0.6 // indirect
github.com/prometheus/statsd_exporter v0.15.0
go.opencensus.io v0.22.2
go.opencensus.io v0.22.4-0.20200608061201-1901b56b9515
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4-0.20200608061201-1901b56b9515 h1:YS3N5IEX0gd5pYMAT6Am+LQPPuQTNRv11JaZY0+BV0Y=
go.opencensus.io v0.22.4-0.20200608061201-1901b56b9515/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down
19 changes: 18 additions & 1 deletion prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,28 @@ func newCollector(opts Options, registrar prometheus.Registerer) *collector {
}

func (c *collector) toDesc(metric *metricdata.Metric) *prometheus.Desc {
var labels prometheus.Labels
if metric.Resource == nil {
labels = c.opts.ConstLabels
} else if c.opts.ConstLabels == nil {
labels = metric.Resource.Labels
} else {
labels = prometheus.Labels{}
for k, v := range c.opts.ConstLabels {
labels[k] = v
}
// Resource labels overwrite const labels.
for k, v := range metric.Resource.Labels {
labels[k] = v
}
}

return prometheus.NewDesc(
metricName(c.opts.Namespace, metric),
metric.Descriptor.Description,
toPromLabels(metric.Descriptor.LabelKeys),
c.opts.ConstLabels)
labels)

}

type metricExporter struct {
Expand Down
220 changes: 151 additions & 69 deletions prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import (
"testing"
"time"

"go.opencensus.io/resource"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"

"github.com/google/go-cmp/cmp"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
Expand Down Expand Up @@ -290,90 +292,170 @@ func TestHistogramUnorderedBucketBounds(t *testing.T) {
}
}

func TestConstLabelsIncluded(t *testing.T) {
constLabels := prometheus.Labels{
"service": "spanner",
}
func TestConstLabelsAndResource(t *testing.T) {
testCases := []struct {
name string
constLabels prometheus.Labels
resource *resource.Resource
want string
}{{
name: "neither const labels nor resource",
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961"} 1
`,
}, {
name: "const labels only",
constLabels: prometheus.Labels{"service": "spanner"},
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961",service="spanner"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961",service="spanner"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961",service="spanner"} 1
`,
}, {
name: "resource only",
resource: &resource.Resource{Type: "test resource", Labels: map[string]string{"region": "us-east"}},
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961",region="us-east"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961",region="us-east"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961",region="us-east"} 1
`,
}, {
name: "both const labels and resource",
constLabels: prometheus.Labels{"service": "spanner"},
resource: &resource.Resource{Type: "test resource", Labels: map[string]string{"region": "us-east"}},
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961",region="us-east",service="spanner"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961",region="us-east",service="spanner"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961",region="us-east",service="spanner"} 1
`,
}, {
name: "const labels and resource overlap",
constLabels: prometheus.Labels{"service": "spanner"},
resource: &resource.Resource{Type: "test resource", Labels: map[string]string{"service": "bigtable"}},
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961",service="bigtable"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961",service="bigtable"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961",service="bigtable"} 1
`,
}, {
name: "const labels and resource with overlap and non-overlap",
constLabels: prometheus.Labels{"service": "spanner", "account": "test"},
resource: &resource.Resource{Type: "test resource", Labels: map[string]string{"service": "bigtable", "region": "us-east"}},
want: `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{account="test",method="issue961",region="us-east",service="bigtable"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{account="test",method="issue961",region="us-east",service="bigtable"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{account="test",method="issue961",region="us-east",service="bigtable"} 1
`,
}}
measureLabel, _ := tag.NewKey("method")

exporter, err := NewExporter(Options{
ConstLabels: constLabels,
})
if err != nil {
t.Fatalf("failed to create prometheus exporter: %v", err)
}

names := []string{"foo", "bar", "baz"}

var measures mSlice
for _, name := range names {
measures.createAndAppend("tests/"+name, name, "")
}

var vc vCreator
for _, m := range measures {
vc.createAndAppend(m.Name(), m.Description(), []tag.Key{measureLabel}, m, view.Count())
}

if err := view.Register(vc...); err != nil {
t.Fatalf("failed to create views: %v", err)
}
defer view.Unregister(vc...)
for _, testCase := range testCases {

ctx, _ := tag.New(context.Background(), tag.Upsert(measureLabel, "issue961"))
for _, m := range measures {
stats.Record(ctx, m.M(1))
}
exporter, err := NewExporter(Options{
ConstLabels: testCase.constLabels,
})
if err != nil {
t.Fatalf("failed to create prometheus exporter: %v", err)
}

srv := httptest.NewServer(exporter)
defer srv.Close()
names := []string{"foo", "bar", "baz"}

var i int
var output string
for {
time.Sleep(10 * time.Millisecond)
if i == 10 {
t.Fatal("no output at /metrics (100ms wait)")
var measures mSlice
for _, name := range names {
measures.createAndAppend("tests/"+name, name, "")
}
i++

resp, err := http.Get(srv.URL)
if err != nil {
t.Fatalf("failed to get /metrics: %v", err)
var vc vCreator
for _, m := range measures {
vc.createAndAppend(m.Name(), m.Description(), []tag.Key{measureLabel}, m, view.Count())
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("failed to read body: %v", err)
meter := view.NewMeter()
meter.SetResource(testCase.resource)
meter.Start()
if err := meter.Register(vc...); err != nil {
t.Fatalf("failed to create views: %v", err)
}
resp.Body.Close()
defer meter.Unregister(vc...)

output = string(body)
if output != "" {
break
ctx, _ := tag.New(context.Background(), tag.Upsert(measureLabel, "issue961"))
for _, m := range measures {
opt := stats.WithRecorder(meter)
stats.RecordWithOptions(ctx, opt, stats.WithMeasurements(m.M(1)))
}
}

if strings.Contains(output, "collected before with the same name and label values") {
t.Fatal("metric name and labels being duplicated but must be unique")
}
srv := httptest.NewServer(exporter)
defer srv.Close()

var i int
var output string
for {
time.Sleep(10 * time.Millisecond)
if i == 10 {
t.Fatal("no output at /metrics (100ms wait)")
}
i++

resp, err := http.Get(srv.URL)
if err != nil {
t.Fatalf("failed to get /metrics: %v", err)
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("failed to read body: %v", err)
}
resp.Body.Close()

output = string(body)
if output != "" {
break
}
}

if strings.Contains(output, "error(s) occurred") {
t.Fatal("error reported by prometheus registry")
}
if strings.Contains(output, "collected before with the same name and label values") {
t.Fatal("metric name and labels being duplicated but must be unique")
}

want := `# HELP tests_bar bar
# TYPE tests_bar counter
tests_bar{method="issue961",service="spanner"} 1
# HELP tests_baz baz
# TYPE tests_baz counter
tests_baz{method="issue961",service="spanner"} 1
# HELP tests_foo foo
# TYPE tests_foo counter
tests_foo{method="issue961",service="spanner"} 1
`
if output != want {
t.Fatal("output differed from expected")
if strings.Contains(output, "error(s) occurred") {
t.Fatal("error reported by prometheus registry")
}
if diff := cmp.Diff(testCase.want, output); diff != "" {
t.Errorf("Unexpected prometheus output for test {%s} (-want +got):\n%s", testCase.name, diff)
}
meter.Unregister(vc...)
}
}

Expand Down

0 comments on commit 6bcf6f8

Please sign in to comment.