@@ -28,6 +28,7 @@ import (
28
28
"go.opentelemetry.io/otel"
29
29
"go.opentelemetry.io/otel/attribute"
30
30
"go.opentelemetry.io/otel/metric/unit"
31
+ "go.opentelemetry.io/otel/sdk/instrumentation"
31
32
"go.opentelemetry.io/otel/sdk/metric"
32
33
"go.opentelemetry.io/otel/sdk/metric/metricdata"
33
34
"go.opentelemetry.io/otel/sdk/resource"
@@ -36,8 +37,13 @@ import (
36
37
const (
37
38
targetInfoMetricName = "target_info"
38
39
targetInfoDescription = "Target metadata"
40
+
41
+ scopeInfoMetricName = "otel_scope_info"
42
+ scopeInfoDescription = "Instrumentation Scope metadata"
39
43
)
40
44
45
+ var scopeInfoKeys = [2 ]string {"otel_scope_name" , "otel_scope_version" }
46
+
41
47
// Exporter is a Prometheus Exporter that embeds the OTel metric.Reader
42
48
// interface for easy instantiation with a MeterProvider.
43
49
type Exporter struct {
@@ -53,7 +59,9 @@ type collector struct {
53
59
disableTargetInfo bool
54
60
withoutUnits bool
55
61
targetInfo prometheus.Metric
62
+ disableScopeInfo bool
56
63
createTargetInfoOnce sync.Once
64
+ scopeInfos map [instrumentation.Scope ]prometheus.Metric
57
65
}
58
66
59
67
// prometheus counters MUST have a _total suffix:
@@ -73,6 +81,8 @@ func New(opts ...Option) (*Exporter, error) {
73
81
reader : reader ,
74
82
disableTargetInfo : cfg .disableTargetInfo ,
75
83
withoutUnits : cfg .withoutUnits ,
84
+ disableScopeInfo : cfg .disableScopeInfo ,
85
+ scopeInfos : make (map [instrumentation.Scope ]prometheus.Metric ),
76
86
}
77
87
78
88
if err := cfg .registerer .Register (collector ); err != nil {
@@ -118,28 +128,46 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
118
128
if ! c .disableTargetInfo {
119
129
ch <- c .targetInfo
120
130
}
131
+
121
132
for _ , scopeMetrics := range metrics .ScopeMetrics {
133
+ var keys , values [2 ]string
134
+
135
+ if ! c .disableScopeInfo {
136
+ scopeInfo , ok := c .scopeInfos [scopeMetrics .Scope ]
137
+ if ! ok {
138
+ scopeInfo , err = createScopeInfoMetric (scopeMetrics .Scope )
139
+ if err != nil {
140
+ otel .Handle (err )
141
+ }
142
+ c .scopeInfos [scopeMetrics .Scope ] = scopeInfo
143
+ }
144
+ ch <- scopeInfo
145
+ keys = scopeInfoKeys
146
+ values = [2 ]string {scopeMetrics .Scope .Name , scopeMetrics .Scope .Version }
147
+ }
148
+
122
149
for _ , m := range scopeMetrics .Metrics {
123
150
switch v := m .Data .(type ) {
124
151
case metricdata.Histogram :
125
- addHistogramMetric (ch , v , m , c .getName (m ))
152
+ addHistogramMetric (ch , v , m , keys , values , c .getName (m ))
126
153
case metricdata.Sum [int64 ]:
127
- addSumMetric (ch , v , m , c .getName (m ))
154
+ addSumMetric (ch , v , m , keys , values , c .getName (m ))
128
155
case metricdata.Sum [float64 ]:
129
- addSumMetric (ch , v , m , c .getName (m ))
156
+ addSumMetric (ch , v , m , keys , values , c .getName (m ))
130
157
case metricdata.Gauge [int64 ]:
131
- addGaugeMetric (ch , v , m , c .getName (m ))
158
+ addGaugeMetric (ch , v , m , keys , values , c .getName (m ))
132
159
case metricdata.Gauge [float64 ]:
133
- addGaugeMetric (ch , v , m , c .getName (m ))
160
+ addGaugeMetric (ch , v , m , keys , values , c .getName (m ))
134
161
}
135
162
}
136
163
}
137
164
}
138
165
139
- func addHistogramMetric (ch chan <- prometheus.Metric , histogram metricdata.Histogram , m metricdata.Metrics , name string ) {
166
+ func addHistogramMetric (ch chan <- prometheus.Metric , histogram metricdata.Histogram , m metricdata.Metrics , ks , vs [ 2 ] string , name string ) {
140
167
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/3163): support exemplars
141
168
for _ , dp := range histogram .DataPoints {
142
- keys , values := getAttrs (dp .Attributes )
169
+ keys , values := getAttrs (dp .Attributes , ks , vs )
170
+
143
171
desc := prometheus .NewDesc (name , m .Description , keys , nil )
144
172
buckets := make (map [float64 ]uint64 , len (dp .Bounds ))
145
173
@@ -157,7 +185,7 @@ func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histog
157
185
}
158
186
}
159
187
160
- func addSumMetric [N int64 | float64 ](ch chan <- prometheus.Metric , sum metricdata.Sum [N ], m metricdata.Metrics , name string ) {
188
+ func addSumMetric [N int64 | float64 ](ch chan <- prometheus.Metric , sum metricdata.Sum [N ], m metricdata.Metrics , ks , vs [ 2 ] string , name string ) {
161
189
valueType := prometheus .CounterValue
162
190
if ! sum .IsMonotonic {
163
191
valueType = prometheus .GaugeValue
@@ -167,7 +195,8 @@ func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata
167
195
name += counterSuffix
168
196
}
169
197
for _ , dp := range sum .DataPoints {
170
- keys , values := getAttrs (dp .Attributes )
198
+ keys , values := getAttrs (dp .Attributes , ks , vs )
199
+
171
200
desc := prometheus .NewDesc (name , m .Description , keys , nil )
172
201
m , err := prometheus .NewConstMetric (desc , valueType , float64 (dp .Value ), values ... )
173
202
if err != nil {
@@ -178,9 +207,10 @@ func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata
178
207
}
179
208
}
180
209
181
- func addGaugeMetric [N int64 | float64 ](ch chan <- prometheus.Metric , gauge metricdata.Gauge [N ], m metricdata.Metrics , name string ) {
210
+ func addGaugeMetric [N int64 | float64 ](ch chan <- prometheus.Metric , gauge metricdata.Gauge [N ], m metricdata.Metrics , ks , vs [ 2 ] string , name string ) {
182
211
for _ , dp := range gauge .DataPoints {
183
- keys , values := getAttrs (dp .Attributes )
212
+ keys , values := getAttrs (dp .Attributes , ks , vs )
213
+
184
214
desc := prometheus .NewDesc (name , m .Description , keys , nil )
185
215
m , err := prometheus .NewConstMetric (desc , prometheus .GaugeValue , float64 (dp .Value ), values ... )
186
216
if err != nil {
@@ -194,7 +224,7 @@ func addGaugeMetric[N int64 | float64](ch chan<- prometheus.Metric, gauge metric
194
224
// getAttrs parses the attribute.Set to two lists of matching Prometheus-style
195
225
// keys and values. It sanitizes invalid characters and handles duplicate keys
196
226
// (due to sanitization) by sorting and concatenating the values following the spec.
197
- func getAttrs (attrs attribute.Set ) ([]string , []string ) {
227
+ func getAttrs (attrs attribute.Set , ks , vs [ 2 ] string ) ([]string , []string ) {
198
228
keysMap := make (map [string ][]string )
199
229
itr := attrs .Iter ()
200
230
for itr .Next () {
@@ -217,15 +247,26 @@ func getAttrs(attrs attribute.Set) ([]string, []string) {
217
247
})
218
248
values = append (values , strings .Join (vals , ";" ))
219
249
}
250
+
251
+ if ks [0 ] != "" {
252
+ keys = append (keys , ks [:]... )
253
+ values = append (values , vs [:]... )
254
+ }
220
255
return keys , values
221
256
}
222
257
223
258
func (c * collector ) createInfoMetric (name , description string , res * resource.Resource ) (prometheus.Metric , error ) {
224
- keys , values := getAttrs (* res .Set ())
259
+ keys , values := getAttrs (* res .Set (), [ 2 ] string {}, [ 2 ] string {} )
225
260
desc := prometheus .NewDesc (name , description , keys , nil )
226
261
return prometheus .NewConstMetric (desc , prometheus .GaugeValue , float64 (1 ), values ... )
227
262
}
228
263
264
+ func createScopeInfoMetric (scope instrumentation.Scope ) (prometheus.Metric , error ) {
265
+ keys := scopeInfoKeys [:]
266
+ desc := prometheus .NewDesc (scopeInfoMetricName , scopeInfoDescription , keys , nil )
267
+ return prometheus .NewConstMetric (desc , prometheus .GaugeValue , float64 (1 ), scope .Name , scope .Version )
268
+ }
269
+
229
270
func sanitizeRune (r rune ) rune {
230
271
if unicode .IsLetter (r ) || unicode .IsDigit (r ) || r == ':' || r == '_' {
231
272
return r
0 commit comments