istio
104 строки · 2.9 Кб
1// Copyright Istio 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 monitoring
16
17import (
18"context"
19"sync"
20
21"go.opentelemetry.io/otel/attribute"
22api "go.opentelemetry.io/otel/metric"
23
24"istio.io/istio/pkg/log"
25)
26
27type gauge struct {
28baseMetric
29g api.Float64ObservableGauge
30
31// attributeSets stores a map of attributes -> values, for gauges.
32attributeSetsMutex *sync.RWMutex
33attributeSets map[attribute.Set]*gaugeValues
34currentGaugeSet *gaugeValues
35}
36
37var _ Metric = &gauge{}
38
39func newGauge(o options) *gauge {
40r := &gauge{
41attributeSetsMutex: &sync.RWMutex{},
42}
43r.attributeSets = map[attribute.Set]*gaugeValues{}
44g, err := meter().Float64ObservableGauge(o.name,
45api.WithFloat64Callback(func(ctx context.Context, observer api.Float64Observer) error {
46r.attributeSetsMutex.Lock()
47defer r.attributeSetsMutex.Unlock()
48for _, gv := range r.attributeSets {
49observer.Observe(gv.val, gv.opt...)
50}
51return nil
52}),
53api.WithDescription(o.description),
54api.WithUnit(string(o.unit)))
55if err != nil {
56log.Fatalf("failed to create gauge: %v", err)
57}
58r.g = g
59r.baseMetric = baseMetric{
60name: o.name,
61rest: r,
62}
63return r
64}
65
66func (f *gauge) Record(value float64) {
67f.runRecordHook(value)
68// TODO: https://github.com/open-telemetry/opentelemetry-specification/issues/2318 use synchronous gauge so we don't need to deal with this
69f.attributeSetsMutex.Lock()
70// Special case: we lazy-load the non-labeled value. This ensures that metrics which should always have labels do not end up with a un-labeled zero-value
71// If a metric really requires `metric{} 0`, they can explicitly call .Record(0).
72if f.currentGaugeSet == nil {
73f.currentGaugeSet = &gaugeValues{}
74f.attributeSets[attribute.NewSet()] = f.currentGaugeSet
75}
76f.currentGaugeSet.val = value
77f.attributeSetsMutex.Unlock()
78}
79
80func (f *gauge) With(labelValues ...LabelValue) Metric {
81attrs, set := rebuildAttributes(f.baseMetric, labelValues)
82nm := &gauge{
83g: f.g,
84attributeSetsMutex: f.attributeSetsMutex,
85attributeSets: f.attributeSets,
86}
87if _, f := nm.attributeSets[set]; !f {
88nm.attributeSets[set] = &gaugeValues{
89opt: []api.ObserveOption{api.WithAttributeSet(set)},
90}
91}
92nm.currentGaugeSet = nm.attributeSets[set]
93nm.baseMetric = baseMetric{
94name: f.name,
95attrs: attrs,
96rest: nm,
97}
98return nm
99}
100
101type gaugeValues struct {
102val float64
103opt []api.ObserveOption
104}
105