-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathinstrument.go
364 lines (312 loc) · 11.4 KB
/
instrument.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//go:generate stringer -type=InstrumentKind -trimprefix=InstrumentKind
package metric // import "go.opentelemetry.io/otel/sdk/metric"
import (
"context"
"errors"
"fmt"
"strings"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/embedded"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
"go.opentelemetry.io/otel/sdk/metric/internal/x"
)
var zeroScope instrumentation.Scope
// InstrumentKind is the identifier of a group of instruments that all
// performing the same function.
type InstrumentKind uint8
const (
// instrumentKindUndefined is an undefined instrument kind, it should not
// be used by any initialized type.
instrumentKindUndefined InstrumentKind = 0 // nolint:deadcode,varcheck,unused
// InstrumentKindCounter identifies a group of instruments that record
// increasing values synchronously with the code path they are measuring.
InstrumentKindCounter InstrumentKind = 1
// InstrumentKindUpDownCounter identifies a group of instruments that
// record increasing and decreasing values synchronously with the code path
// they are measuring.
InstrumentKindUpDownCounter InstrumentKind = 2
// InstrumentKindHistogram identifies a group of instruments that record a
// distribution of values synchronously with the code path they are
// measuring.
InstrumentKindHistogram InstrumentKind = 3
// InstrumentKindObservableCounter identifies a group of instruments that
// record increasing values in an asynchronous callback.
InstrumentKindObservableCounter InstrumentKind = 4
// InstrumentKindObservableUpDownCounter identifies a group of instruments
// that record increasing and decreasing values in an asynchronous
// callback.
InstrumentKindObservableUpDownCounter InstrumentKind = 5
// InstrumentKindObservableGauge identifies a group of instruments that
// record current values in an asynchronous callback.
InstrumentKindObservableGauge InstrumentKind = 6
// InstrumentKindGauge identifies a group of instruments that record
// instantaneous values synchronously with the code path they are
// measuring.
InstrumentKindGauge InstrumentKind = 7
)
type nonComparable [0]func() // nolint: unused // This is indeed used.
// Instrument describes properties an instrument is created with.
type Instrument struct {
// Name is the human-readable identifier of the instrument.
Name string
// Description describes the purpose of the instrument.
Description string
// Kind defines the functional group of the instrument.
Kind InstrumentKind
// Unit is the unit of measurement recorded by the instrument.
Unit string
// Scope identifies the instrumentation that created the instrument.
Scope instrumentation.Scope
// Ensure forward compatibility if non-comparable fields need to be added.
nonComparable // nolint: unused
}
// IsEmpty returns if all Instrument fields are their zero-value.
func (i Instrument) IsEmpty() bool {
return i.Name == "" &&
i.Description == "" &&
i.Kind == instrumentKindUndefined &&
i.Unit == "" &&
i.Scope == zeroScope
}
// matches returns whether all the non-zero-value fields of i match the
// corresponding fields of other. If i is empty it will match all other, and
// true will always be returned.
func (i Instrument) matches(other Instrument) bool {
return i.matchesName(other) &&
i.matchesDescription(other) &&
i.matchesKind(other) &&
i.matchesUnit(other) &&
i.matchesScope(other)
}
// matchesName returns true if the Name of i is "" or it equals the Name of
// other, otherwise false.
func (i Instrument) matchesName(other Instrument) bool {
return i.Name == "" || i.Name == other.Name
}
// matchesDescription returns true if the Description of i is "" or it equals
// the Description of other, otherwise false.
func (i Instrument) matchesDescription(other Instrument) bool {
return i.Description == "" || i.Description == other.Description
}
// matchesKind returns true if the Kind of i is its zero-value or it equals the
// Kind of other, otherwise false.
func (i Instrument) matchesKind(other Instrument) bool {
return i.Kind == instrumentKindUndefined || i.Kind == other.Kind
}
// matchesUnit returns true if the Unit of i is its zero-value or it equals the
// Unit of other, otherwise false.
func (i Instrument) matchesUnit(other Instrument) bool {
return i.Unit == "" || i.Unit == other.Unit
}
// matchesScope returns true if the Scope of i is its zero-value or it equals
// the Scope of other, otherwise false.
func (i Instrument) matchesScope(other Instrument) bool {
return (i.Scope.Name == "" || i.Scope.Name == other.Scope.Name) &&
(i.Scope.Version == "" || i.Scope.Version == other.Scope.Version) &&
(i.Scope.SchemaURL == "" || i.Scope.SchemaURL == other.Scope.SchemaURL)
}
// Stream describes the stream of data an instrument produces.
type Stream struct {
// Name is the human-readable identifier of the stream.
Name string
// Description describes the purpose of the data.
Description string
// Unit is the unit of measurement recorded.
Unit string
// Aggregation the stream uses for an instrument.
Aggregation Aggregation
// AttributeFilter is an attribute Filter applied to the attributes
// recorded for an instrument's measurement. If the filter returns false
// the attribute will not be recorded, otherwise, if it returns true, it
// will record the attribute.
//
// Use NewAllowKeysFilter from "go.opentelemetry.io/otel/attribute" to
// provide an allow-list of attribute keys here.
AttributeFilter attribute.Filter
// ExemplarReservoirProvider selects the
// [go.opentelemetry.io/otel/sdk/metric/exemplar.ReservoirProvider] based
// on the [Aggregation].
//
// If unspecified, [DefaultExemplarReservoirProviderSelector] is used.
ExemplarReservoirProviderSelector ExemplarReservoirProviderSelector
}
// instID are the identifying properties of a instrument.
type instID struct {
// Name is the name of the stream.
Name string
// Description is the description of the stream.
Description string
// Kind defines the functional group of the instrument.
Kind InstrumentKind
// Unit is the unit of the stream.
Unit string
// Number is the number type of the stream.
Number string
}
// Returns a normalized copy of the instID i.
//
// Instrument names are considered case-insensitive. Standardize the instrument
// name to always be lowercase for the returned instID so it can be compared
// without the name casing affecting the comparison.
func (i instID) normalize() instID {
i.Name = strings.ToLower(i.Name)
return i
}
type int64Inst struct {
measures []aggregate.Measure[int64]
embedded.Int64Counter
embedded.Int64UpDownCounter
embedded.Int64Histogram
embedded.Int64Gauge
}
var (
_ metric.Int64Counter = (*int64Inst)(nil)
_ metric.Int64UpDownCounter = (*int64Inst)(nil)
_ metric.Int64Histogram = (*int64Inst)(nil)
_ metric.Int64Gauge = (*int64Inst)(nil)
_ x.EnabledInstrument = (*int64Inst)(nil)
)
func (i *int64Inst) Add(ctx context.Context, val int64, opts ...metric.AddOption) {
c := metric.NewAddConfig(opts)
i.aggregate(ctx, val, c.Attributes())
}
func (i *int64Inst) Record(ctx context.Context, val int64, opts ...metric.RecordOption) {
c := metric.NewRecordConfig(opts)
i.aggregate(ctx, val, c.Attributes())
}
func (i *int64Inst) Enabled(_ context.Context) bool {
return len(i.measures) != 0
}
func (i *int64Inst) aggregate(ctx context.Context, val int64, s attribute.Set) { // nolint:revive // okay to shadow pkg with method.
for _, in := range i.measures {
in(ctx, val, s)
}
}
type float64Inst struct {
measures []aggregate.Measure[float64]
embedded.Float64Counter
embedded.Float64UpDownCounter
embedded.Float64Histogram
embedded.Float64Gauge
}
var (
_ metric.Float64Counter = (*float64Inst)(nil)
_ metric.Float64UpDownCounter = (*float64Inst)(nil)
_ metric.Float64Histogram = (*float64Inst)(nil)
_ metric.Float64Gauge = (*float64Inst)(nil)
_ x.EnabledInstrument = (*float64Inst)(nil)
)
func (i *float64Inst) Add(ctx context.Context, val float64, opts ...metric.AddOption) {
c := metric.NewAddConfig(opts)
i.aggregate(ctx, val, c.Attributes())
}
func (i *float64Inst) Record(ctx context.Context, val float64, opts ...metric.RecordOption) {
c := metric.NewRecordConfig(opts)
i.aggregate(ctx, val, c.Attributes())
}
func (i *float64Inst) Enabled(_ context.Context) bool {
return len(i.measures) != 0
}
func (i *float64Inst) aggregate(ctx context.Context, val float64, s attribute.Set) {
for _, in := range i.measures {
in(ctx, val, s)
}
}
// observableID is a comparable unique identifier of an observable.
type observableID[N int64 | float64] struct {
name string
description string
kind InstrumentKind
unit string
scope instrumentation.Scope
}
type float64Observable struct {
metric.Float64Observable
*observable[float64]
embedded.Float64ObservableCounter
embedded.Float64ObservableUpDownCounter
embedded.Float64ObservableGauge
}
var (
_ metric.Float64ObservableCounter = float64Observable{}
_ metric.Float64ObservableUpDownCounter = float64Observable{}
_ metric.Float64ObservableGauge = float64Observable{}
)
func newFloat64Observable(m *meter, kind InstrumentKind, name, desc, u string) float64Observable {
return float64Observable{
observable: newObservable[float64](m, kind, name, desc, u),
}
}
type int64Observable struct {
metric.Int64Observable
*observable[int64]
embedded.Int64ObservableCounter
embedded.Int64ObservableUpDownCounter
embedded.Int64ObservableGauge
}
var (
_ metric.Int64ObservableCounter = int64Observable{}
_ metric.Int64ObservableUpDownCounter = int64Observable{}
_ metric.Int64ObservableGauge = int64Observable{}
)
func newInt64Observable(m *meter, kind InstrumentKind, name, desc, u string) int64Observable {
return int64Observable{
observable: newObservable[int64](m, kind, name, desc, u),
}
}
type observable[N int64 | float64] struct {
metric.Observable
observableID[N]
meter *meter
measures measures[N]
dropAggregation bool
}
func newObservable[N int64 | float64](m *meter, kind InstrumentKind, name, desc, u string) *observable[N] {
return &observable[N]{
observableID: observableID[N]{
name: name,
description: desc,
kind: kind,
unit: u,
scope: m.scope,
},
meter: m,
}
}
// observe records the val for the set of attrs.
func (o *observable[N]) observe(val N, s attribute.Set) {
o.measures.observe(val, s)
}
func (o *observable[N]) appendMeasures(meas []aggregate.Measure[N]) {
o.measures = append(o.measures, meas...)
}
type measures[N int64 | float64] []aggregate.Measure[N]
// observe records the val for the set of attrs.
func (m measures[N]) observe(val N, s attribute.Set) {
for _, in := range m {
in(context.Background(), val, s)
}
}
var errEmptyAgg = errors.New("no aggregators for observable instrument")
// registerable returns an error if the observable o should not be registered,
// and nil if it should. An errEmptyAgg error is returned if o is effectively a
// no-op because it does not have any aggregators. Also, an error is returned
// if scope defines a Meter other than the one o was created by.
func (o *observable[N]) registerable(m *meter) error {
if len(o.measures) == 0 {
return errEmptyAgg
}
if m != o.meter {
return fmt.Errorf(
"invalid registration: observable %q from Meter %q, registered with Meter %q",
o.name,
o.scope.Name,
m.scope.Name,
)
}
return nil
}