Add description field to LabelKey (#1114)

* Add description field to LabelKey

* keep description as "" when not specified.
diff --git a/exporter/prometheus/prometheus.go b/exporter/prometheus/prometheus.go
index 53ff6ba..9fbffb3 100644
--- a/exporter/prometheus/prometheus.go
+++ b/exporter/prometheus/prometheus.go
@@ -192,9 +192,9 @@
 	return nil
 }
 
-func toPromLabels(mls []string) (labels []string) {
+func toPromLabels(mls []metricdata.LabelKey) (labels []string) {
 	for _, ml := range mls {
-		labels = append(labels, internal.Sanitize(ml))
+		labels = append(labels, internal.Sanitize(ml.Key))
 	}
 	return labels
 }
diff --git a/metric/common.go b/metric/common.go
index c370f7b..f5716c9 100644
--- a/metric/common.go
+++ b/metric/common.go
@@ -33,7 +33,7 @@
 	vals   sync.Map
 	desc   metricdata.Descriptor
 	start  time.Time
-	keys   []string
+	keys   []metricdata.LabelKey
 	bmType baseMetricType
 }
 
diff --git a/metric/cumulative_test.go b/metric/cumulative_test.go
index 2e0d3c2..538320c 100644
--- a/metric/cumulative_test.go
+++ b/metric/cumulative_test.go
@@ -39,9 +39,12 @@
 	want := []*metricdata.Metric{
 		{
 			Descriptor: metricdata.Descriptor{
-				Name:      "TestCumulative",
-				LabelKeys: []string{"k1", "k2"},
-				Type:      metricdata.TypeCumulativeFloat64,
+				Name: "TestCumulative",
+				LabelKeys: []metricdata.LabelKey{
+					{Key: "k1"},
+					{Key: "k2"},
+				},
+				Type: metricdata.TypeCumulativeFloat64,
 			},
 			TimeSeries: []*metricdata.TimeSeries{
 				{
diff --git a/metric/gauge_test.go b/metric/gauge_test.go
index b8c41d1..a352d2d 100644
--- a/metric/gauge_test.go
+++ b/metric/gauge_test.go
@@ -41,9 +41,12 @@
 	want := []*metricdata.Metric{
 		{
 			Descriptor: metricdata.Descriptor{
-				Name:      "TestGauge",
-				LabelKeys: []string{"k1", "k2"},
-				Type:      metricdata.TypeGaugeFloat64,
+				Name: "TestGauge",
+				LabelKeys: []metricdata.LabelKey{
+					{Key: "k1"},
+					{Key: "k2"},
+				},
+				Type: metricdata.TypeGaugeFloat64,
 			},
 			TimeSeries: []*metricdata.TimeSeries{
 				{
@@ -136,9 +139,33 @@
 	name := "testOptUnit"
 	gf, _ := r.AddFloat64Gauge(name, WithLabelKeys("k1", "k3"))
 	want := metricdata.Descriptor{
-		Name:      name,
-		LabelKeys: []string{"k1", "k3"},
-		Type:      metricdata.TypeGaugeFloat64,
+		Name: name,
+		LabelKeys: []metricdata.LabelKey{
+			{Key: "k1"},
+			{Key: "k3"},
+		},
+		Type: metricdata.TypeGaugeFloat64,
+	}
+	got := gf.bm.desc
+	if !cmp.Equal(got, want) {
+		t.Errorf("metric descriptor: got %v, want %v\n", got, want)
+	}
+}
+
+func TestGaugeMetricOptionLabelKeysAndDesc(t *testing.T) {
+	r := NewRegistry()
+	name := "testOptUnit"
+	lks := []metricdata.LabelKey{}
+	lks = append(lks, metricdata.LabelKey{Key: "k1", Description: "desc k1"},
+		metricdata.LabelKey{Key: "k3", Description: "desc k3"})
+	gf, _ := r.AddFloat64Gauge(name, WithLabelKeysAndDescription(lks...))
+	want := metricdata.Descriptor{
+		Name: name,
+		LabelKeys: []metricdata.LabelKey{
+			{Key: "k1", Description: "desc k1"},
+			{Key: "k3", Description: "desc k3"},
+		},
+		Type: metricdata.TypeGaugeFloat64,
 	}
 	got := gf.bm.desc
 	if !cmp.Equal(got, want) {
@@ -263,7 +290,7 @@
 	for i, tc := range cases {
 		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
 			g := &baseMetric{
-				keys: make([]string, len(tc)),
+				keys: make([]metricdata.LabelKey, len(tc)),
 			}
 			mk := g.encodeLabelVals(tc)
 			vals := g.decodeLabelVals(mk)
diff --git a/metric/metricdata/label.go b/metric/metricdata/label.go
index 87c55b9..aadae41 100644
--- a/metric/metricdata/label.go
+++ b/metric/metricdata/label.go
@@ -14,6 +14,13 @@
 
 package metricdata
 
+// LabelKey represents key of a label. It has optional
+// description attribute.
+type LabelKey struct {
+	Key         string
+	Description string
+}
+
 // LabelValue represents the value of a label.
 // The zero value represents a missing label value, which may be treated
 // differently to an empty string value by some back ends.
diff --git a/metric/metricdata/metric.go b/metric/metricdata/metric.go
index 6ccdec5..8293712 100644
--- a/metric/metricdata/metric.go
+++ b/metric/metricdata/metric.go
@@ -22,11 +22,11 @@
 
 // Descriptor holds metadata about a metric.
 type Descriptor struct {
-	Name        string   // full name of the metric
-	Description string   // human-readable description
-	Unit        Unit     // units for the measure
-	Type        Type     // type of measure
-	LabelKeys   []string // label keys
+	Name        string     // full name of the metric
+	Description string     // human-readable description
+	Unit        Unit       // units for the measure
+	Type        Type       // type of measure
+	LabelKeys   []LabelKey // label keys
 }
 
 // Metric represents a quantity measured against a resource with different
diff --git a/metric/registry.go b/metric/registry.go
index 5dc4163..ceea7e9 100644
--- a/metric/registry.go
+++ b/metric/registry.go
@@ -31,7 +31,7 @@
 //TODO: [rghetia] add constant labels.
 type metricOptions struct {
 	unit      metricdata.Unit
-	labelkeys []string
+	labelkeys []metricdata.LabelKey
 	desc      string
 }
 
@@ -53,7 +53,18 @@
 }
 
 // WithLabelKeys applies provided label.
-func WithLabelKeys(labelKeys ...string) Options {
+func WithLabelKeys(keys ...string) Options {
+	return func(mo *metricOptions) {
+		labelKeys := make([]metricdata.LabelKey, 0)
+		for _, key := range keys {
+			labelKeys = append(labelKeys, metricdata.LabelKey{Key: key})
+		}
+		mo.labelkeys = labelKeys
+	}
+}
+
+// WithLabelKeysAndDescription applies provided label.
+func WithLabelKeysAndDescription(labelKeys ...metricdata.LabelKey) Options {
 	return func(mo *metricOptions) {
 		mo.labelkeys = labelKeys
 	}
diff --git a/stats/view/view_to_metric.go b/stats/view/view_to_metric.go
index 557c190..010f81b 100644
--- a/stats/view/view_to_metric.go
+++ b/stats/view/view_to_metric.go
@@ -73,10 +73,10 @@
 	}
 }
 
-func getLableKeys(v *View) []string {
-	labelKeys := []string{}
+func getLableKeys(v *View) []metricdata.LabelKey {
+	labelKeys := []metricdata.LabelKey{}
 	for _, k := range v.TagKeys {
-		labelKeys = append(labelKeys, k.Name())
+		labelKeys = append(labelKeys, metricdata.LabelKey{Key: k.Name()})
 	}
 	return labelKeys
 }
@@ -91,7 +91,7 @@
 	}
 }
 
-func toLabelValues(row *Row, expectedKeys []string) []metricdata.LabelValue {
+func toLabelValues(row *Row, expectedKeys []metricdata.LabelKey) []metricdata.LabelValue {
 	labelValues := []metricdata.LabelValue{}
 	tagMap := make(map[string]string)
 	for _, tag := range row.Tags {
@@ -99,7 +99,7 @@
 	}
 
 	for _, key := range expectedKeys {
-		if val, ok := tagMap[key]; ok {
+		if val, ok := tagMap[key.Key]; ok {
 			labelValues = append(labelValues, metricdata.NewLabelValue(val))
 		} else {
 			labelValues = append(labelValues, metricdata.LabelValue{})
diff --git a/stats/view/view_to_metric_test.go b/stats/view/view_to_metric_test.go
index 2f2c723..c73d9a2 100644
--- a/stats/view/view_to_metric_test.go
+++ b/stats/view/view_to_metric_test.go
@@ -50,7 +50,7 @@
 	labelValues      []metricdata.LabelValue
 	emptyLabelValues []metricdata.LabelValue
 
-	labelKeys []string
+	labelKeys []metricdata.LabelKey
 
 	recordsInt64        []recordValWithTag
 	recordsFloat64      []recordValWithTag
@@ -125,7 +125,10 @@
 		{Value: "", Present: false},
 		{Value: "", Present: false},
 	}
-	labelKeys = []string{tk1.Name(), tk2.Name()}
+	labelKeys = []metricdata.LabelKey{
+		{Key: tk1.Name()},
+		{Key: tk2.Name()},
+	}
 
 	recordsInt64 = []recordValWithTag{
 		{tags: tags, value: int64(2)},