istio

Форк
0
/
monitoring.go 
298 строк · 9.5 Кб
1
// Copyright 2019 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

15
package monitoring
16

17
import (
18
	"net/http"
19
	"sync"
20

21
	"github.com/prometheus/client_golang/prometheus"
22
	"github.com/prometheus/client_golang/prometheus/promhttp"
23
	"go.opentelemetry.io/otel"
24
	"go.opentelemetry.io/otel/attribute"
25
	otelprom "go.opentelemetry.io/otel/exporters/prometheus"
26
	api "go.opentelemetry.io/otel/metric"
27
	"go.opentelemetry.io/otel/sdk/metric"
28

29
	"istio.io/istio/pkg/log"
30
	"istio.io/istio/pkg/maps"
31
	"istio.io/istio/pkg/slices"
32
)
33

34
var (
35
	meter = func() api.Meter {
36
		return otel.GetMeterProvider().Meter("istio")
37
	}
38

39
	monitoringLogger = log.RegisterScope("monitoring", "metrics monitoring")
40
)
41

42
func init() {
43
	otel.SetLogger(log.NewLogrAdapter(monitoringLogger))
44
}
45

46
// RegisterPrometheusExporter sets the global metrics handler to the provided Prometheus registerer and gatherer.
47
// Returned is an HTTP handler that can be used to read metrics from.
48
func RegisterPrometheusExporter(reg prometheus.Registerer, gatherer prometheus.Gatherer) (http.Handler, error) {
49
	if reg == nil {
50
		reg = prometheus.DefaultRegisterer
51
	}
52
	if gatherer == nil {
53
		gatherer = prometheus.DefaultGatherer
54
	}
55
	promOpts := []otelprom.Option{
56
		otelprom.WithoutScopeInfo(),
57
		otelprom.WithoutTargetInfo(),
58
		otelprom.WithoutUnits(),
59
		otelprom.WithRegisterer(reg),
60
		otelprom.WithoutCounterSuffixes(),
61
	}
62

63
	prom, err := otelprom.New(promOpts...)
64
	if err != nil {
65
		return nil, err
66
	}
67

68
	opts := []metric.Option{metric.WithReader(prom)}
69
	opts = append(opts, knownMetrics.toHistogramViews()...)
70
	mp := metric.NewMeterProvider(opts...)
71
	otel.SetMeterProvider(mp)
72
	handler := promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{})
73
	return handler, nil
74
}
75

76
// A Metric collects numerical observations.
77
type Metric interface {
78
	// Increment records a value of 1 for the current measure. For Sums,
79
	// this is equivalent to adding 1 to the current value. For Gauges,
80
	// this is equivalent to setting the value to 1. For Distributions,
81
	// this is equivalent to making an observation of value 1.
82
	Increment()
83

84
	// Decrement records a value of -1 for the current measure. For Sums,
85
	// this is equivalent to subtracting -1 to the current value. For Gauges,
86
	// this is equivalent to setting the value to -1. For Distributions,
87
	// this is equivalent to making an observation of value -1.
88
	Decrement()
89

90
	// Name returns the name value of a Metric.
91
	Name() string
92

93
	// Record makes an observation of the provided value for the given measure.
94
	Record(value float64)
95

96
	// RecordInt makes an observation of the provided value for the measure.
97
	RecordInt(value int64)
98

99
	// With creates a new Metric, with the LabelValues provided. This allows creating
100
	// a set of pre-dimensioned data for recording purposes. This is primarily used
101
	// for documentation and convenience. Metrics created with this method do not need
102
	// to be registered (they share the registration of their parent Metric).
103
	With(labelValues ...LabelValue) Metric
104

105
	// Register configures the Metric for export. It MUST be called before collection
106
	// of values for the Metric. An error will be returned if registration fails.
107
	Register() error
108
}
109

110
// DerivedMetric can be used to supply values that dynamically derive from internal
111
// state, but are not updated based on any specific event. Their value will be calculated
112
// based on a value func that executes when the metrics are exported.
113
//
114
// At the moment, only a Gauge type is supported.
115
type DerivedMetric interface {
116
	// Name returns the name value of a DerivedMetric.
117
	Name() string
118

119
	// Register handles any required setup to ensure metric export.
120
	Register() error
121

122
	// ValueFrom is used to update the derived value with the provided
123
	// function and the associated label values. If the metric is unlabeled,
124
	// ValueFrom may be called without any labelValues. Otherwise, the labelValues
125
	// supplied MUST match the label keys supplied at creation time both in number
126
	// and in order.
127
	ValueFrom(valueFn func() float64, labelValues ...LabelValue) DerivedMetric
128
}
129

130
// CreateLabel will attempt to create a new Label.
131
func CreateLabel(key string) Label {
132
	return Label{attribute.Key(key)}
133
}
134

135
// A Label provides a named dimension for a Metric.
136
type Label struct {
137
	key attribute.Key
138
}
139

140
// Value creates a new LabelValue for the Label.
141
func (l Label) Value(value string) LabelValue {
142
	return LabelValue{l.key.String(value)}
143
}
144

145
// A LabelValue represents a Label with a specific value. It is used to record
146
// values for a Metric.
147
type LabelValue struct {
148
	keyValue attribute.KeyValue
149
}
150

151
func (l LabelValue) Key() Label {
152
	return Label{l.keyValue.Key}
153
}
154

155
func (l LabelValue) Value() string {
156
	return l.keyValue.Value.AsString()
157
}
158

159
// RecordHook has a callback function which a measure is recorded.
160
type RecordHook interface {
161
	OnRecord(name string, tags []LabelValue, value float64)
162
}
163

164
var (
165
	recordHooks     = map[string]RecordHook{}
166
	recordHookMutex sync.RWMutex
167
)
168

169
// RegisterRecordHook adds a RecordHook for a given measure.
170
func RegisterRecordHook(name string, h RecordHook) {
171
	recordHookMutex.Lock()
172
	defer recordHookMutex.Unlock()
173
	recordHooks[name] = h
174
}
175

176
// NewSum creates a new Sum Metric (the values will be cumulative).
177
// That means that data collected by the new Metric will be summed before export.
178
func NewSum(name, description string, opts ...Options) Metric {
179
	knownMetrics.register(MetricDefinition{
180
		Name:        name,
181
		Type:        "Sum",
182
		Description: description,
183
	})
184
	o, dm := createOptions(name, description, opts...)
185
	if dm != nil {
186
		return dm
187
	}
188
	return newCounter(o)
189
}
190

191
// NewGauge creates a new Gauge Metric. That means that data collected by the new
192
// Metric will export only the last recorded value.
193
func NewGauge(name, description string, opts ...Options) Metric {
194
	knownMetrics.register(MetricDefinition{
195
		Name:        name,
196
		Type:        "LastValue",
197
		Description: description,
198
	})
199
	o, dm := createOptions(name, description, opts...)
200
	if dm != nil {
201
		return dm
202
	}
203
	return newGauge(o)
204
}
205

206
// NewDerivedGauge creates a new Gauge Metric. That means that data collected by the new
207
// Metric will export only the last recorded value.
208
// Unlike NewGauge, the DerivedGauge accepts functions which are called to get the current value.
209
func NewDerivedGauge(name, description string) DerivedMetric {
210
	knownMetrics.register(MetricDefinition{
211
		Name:        name,
212
		Type:        "LastValue",
213
		Description: description,
214
	})
215
	return newDerivedGauge(name, description)
216
}
217

218
// NewDistribution creates a new Metric with an aggregation type of Distribution. This means that the
219
// data collected by the Metric will be collected and exported as a histogram, with the specified bounds.
220
func NewDistribution(name, description string, bounds []float64, opts ...Options) Metric {
221
	knownMetrics.register(MetricDefinition{
222
		Name:        name,
223
		Type:        "Distribution",
224
		Description: description,
225
		Bounds:      bounds,
226
	})
227
	o, dm := createOptions(name, description, opts...)
228
	if dm != nil {
229
		return dm
230
	}
231
	return newDistribution(o)
232
}
233

234
// MetricDefinition records a metric's metadata.
235
// This is used to work around two limitations of OpenTelemetry:
236
//   - (https://github.com/open-telemetry/opentelemetry-go/issues/4003) Histogram buckets cannot be defined per instrument.
237
//     instead, we record all metric definitions and add them as Views at registration time.
238
//   - Support pkg/collateral, which wants to query all metrics. This cannot use a simple Collect() call, as this ignores any unused metrics.
239
type MetricDefinition struct {
240
	Name        string
241
	Type        string
242
	Description string
243
	Bounds      []float64
244
}
245

246
// metrics stores known metrics
247
type metrics struct {
248
	started bool
249
	mu      sync.Mutex
250
	known   map[string]MetricDefinition
251
}
252

253
// knownMetrics is a global that stores all registered metrics
254
var knownMetrics = metrics{
255
	known: map[string]MetricDefinition{},
256
}
257

258
// ExportMetricDefinitions reports all currently registered metric definitions.
259
func ExportMetricDefinitions() []MetricDefinition {
260
	knownMetrics.mu.Lock()
261
	defer knownMetrics.mu.Unlock()
262
	return slices.SortBy(maps.Values(knownMetrics.known), func(a MetricDefinition) string {
263
		return a.Name
264
	})
265
}
266

267
// register records a newly defined metric. Only valid before an exporter is set.
268
func (d *metrics) register(def MetricDefinition) {
269
	d.mu.Lock()
270
	defer d.mu.Unlock()
271
	if d.started {
272
		log.Fatalf("Attempting to initialize metric %q after metrics have started", def.Name)
273
	}
274
	d.known[def.Name] = def
275
}
276

277
// toHistogramViews works around https://github.com/open-telemetry/opentelemetry-go/issues/4003; in the future we can define
278
// this when we create the histogram.
279
func (d *metrics) toHistogramViews() []metric.Option {
280
	d.mu.Lock()
281
	defer d.mu.Unlock()
282
	d.started = true
283
	opts := []metric.Option{}
284
	for name, def := range d.known {
285
		if def.Bounds == nil {
286
			continue
287
		}
288
		// for each histogram metric (i.e. those with bounds), set up a view explicitly defining those buckets.
289
		v := metric.WithView(metric.NewView(
290
			metric.Instrument{Name: name},
291
			metric.Stream{Aggregation: metric.AggregationExplicitBucketHistogram{
292
				Boundaries: def.Bounds,
293
			}},
294
		))
295
		opts = append(opts, v)
296
	}
297
	return opts
298
}
299

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.