blob: ed9eccca3e274ebc9d70af03cc481e155bc47f3d [file] [log] [blame]
Joey Armstronga6af1522023-01-17 16:06:16 -05001// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package registry // import "go.opentelemetry.io/otel/api/metric/registry"
16
17import (
18 "context"
19 "fmt"
20 "sync"
21
22 "go.opentelemetry.io/otel/api/metric"
23 "go.opentelemetry.io/otel/label"
24)
25
26// MeterProvider is a standard MeterProvider for wrapping `MeterImpl`
27type MeterProvider struct {
28 impl metric.MeterImpl
29}
30
31var _ metric.MeterProvider = (*MeterProvider)(nil)
32
33// uniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
34// uniqueness checking for instrument descriptors. Use NewUniqueInstrumentMeter
35// to wrap an implementation with uniqueness checking.
36type uniqueInstrumentMeterImpl struct {
37 lock sync.Mutex
38 impl metric.MeterImpl
39 state map[key]metric.InstrumentImpl
40}
41
42var _ metric.MeterImpl = (*uniqueInstrumentMeterImpl)(nil)
43
44type key struct {
45 instrumentName string
46 instrumentationName string
47 InstrumentationVersion string
48}
49
50// NewMeterProvider returns a new provider that implements instrument
51// name-uniqueness checking.
52func NewMeterProvider(impl metric.MeterImpl) *MeterProvider {
53 return &MeterProvider{
54 impl: NewUniqueInstrumentMeterImpl(impl),
55 }
56}
57
58// Meter implements MeterProvider.
59func (p *MeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
60 return metric.WrapMeterImpl(p.impl, instrumentationName, opts...)
61}
62
63// ErrMetricKindMismatch is the standard error for mismatched metric
64// instrument definitions.
65var ErrMetricKindMismatch = fmt.Errorf(
66 "A metric was already registered by this name with another kind or number type")
67
68// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl with
69// the addition of uniqueness checking.
70func NewUniqueInstrumentMeterImpl(impl metric.MeterImpl) metric.MeterImpl {
71 return &uniqueInstrumentMeterImpl{
72 impl: impl,
73 state: map[key]metric.InstrumentImpl{},
74 }
75}
76
77// RecordBatch implements metric.MeterImpl.
78func (u *uniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []label.KeyValue, ms ...metric.Measurement) {
79 u.impl.RecordBatch(ctx, labels, ms...)
80}
81
82func keyOf(descriptor metric.Descriptor) key {
83 return key{
84 descriptor.Name(),
85 descriptor.InstrumentationName(),
86 descriptor.InstrumentationVersion(),
87 }
88}
89
90// NewMetricKindMismatchError formats an error that describes a
91// mismatched metric instrument definition.
92func NewMetricKindMismatchError(desc metric.Descriptor) error {
93 return fmt.Errorf("Metric was %s (%s %s)registered as a %s %s: %w",
94 desc.Name(),
95 desc.InstrumentationName(),
96 desc.InstrumentationVersion(),
97 desc.NumberKind(),
98 desc.MetricKind(),
99 ErrMetricKindMismatch)
100}
101
102// Compatible determines whether two metric.Descriptors are considered
103// the same for the purpose of uniqueness checking.
104func Compatible(candidate, existing metric.Descriptor) bool {
105 return candidate.MetricKind() == existing.MetricKind() &&
106 candidate.NumberKind() == existing.NumberKind()
107}
108
109// checkUniqueness returns an ErrMetricKindMismatch error if there is
110// a conflict between a descriptor that was already registered and the
111// `descriptor` argument. If there is an existing compatible
112// registration, this returns the already-registered instrument. If
113// there is no conflict and no prior registration, returns (nil, nil).
114func (u *uniqueInstrumentMeterImpl) checkUniqueness(descriptor metric.Descriptor) (metric.InstrumentImpl, error) {
115 impl, ok := u.state[keyOf(descriptor)]
116 if !ok {
117 return nil, nil
118 }
119
120 if !Compatible(descriptor, impl.Descriptor()) {
121 return nil, NewMetricKindMismatchError(impl.Descriptor())
122 }
123
124 return impl, nil
125}
126
127// NewSyncInstrument implements metric.MeterImpl.
128func (u *uniqueInstrumentMeterImpl) NewSyncInstrument(descriptor metric.Descriptor) (metric.SyncImpl, error) {
129 u.lock.Lock()
130 defer u.lock.Unlock()
131
132 impl, err := u.checkUniqueness(descriptor)
133
134 if err != nil {
135 return nil, err
136 } else if impl != nil {
137 return impl.(metric.SyncImpl), nil
138 }
139
140 syncInst, err := u.impl.NewSyncInstrument(descriptor)
141 if err != nil {
142 return nil, err
143 }
144 u.state[keyOf(descriptor)] = syncInst
145 return syncInst, nil
146}
147
148// NewAsyncInstrument implements metric.MeterImpl.
149func (u *uniqueInstrumentMeterImpl) NewAsyncInstrument(
150 descriptor metric.Descriptor,
151 runner metric.AsyncRunner,
152) (metric.AsyncImpl, error) {
153 u.lock.Lock()
154 defer u.lock.Unlock()
155
156 impl, err := u.checkUniqueness(descriptor)
157
158 if err != nil {
159 return nil, err
160 } else if impl != nil {
161 return impl.(metric.AsyncImpl), nil
162 }
163
164 asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
165 if err != nil {
166 return nil, err
167 }
168 u.state[keyOf(descriptor)] = asyncInst
169 return asyncInst, nil
170}