Skip to content

Commit 438773a

Browse files
rubenvp8510iblancasa
authored andcommitted
Add crd metrics usage information (open-telemetry#2825)
* Add crd metrics usage information Signed-off-by: Ruben Vargas <[email protected]> * Add mode metric Signed-off-by: Ruben Vargas <[email protected]> * Refactor CR metrics Signed-off-by: Ruben Vargas <[email protected]> * Add annotation to avoid generate Metrics Signed-off-by: Ruben Vargas <[email protected]> * Add unit tests Signed-off-by: Ruben Vargas <[email protected]> * remove space Signed-off-by: Ruben Vargas <[email protected]> * remove global provider Signed-off-by: Ruben Vargas <[email protected]> * Update main.go Co-authored-by: Israel Blancas <[email protected]> * revert kusttomization.yaml Signed-off-by: Ruben Vargas <[email protected]> * rename some constants Signed-off-by: Ruben Vargas <[email protected]> * Add connectors metrics Signed-off-by: Ruben Vargas <[email protected]> * Update chlog Signed-off-by: Ruben Vargas <[email protected]> * merge new with init, rename some functions, improve changelog entry Signed-off-by: Ruben Vargas <[email protected]> * improve todo comment Signed-off-by: Ruben Vargas <[email protected]> * fix tests Signed-off-by: Ruben Vargas <[email protected]> * set flag to default false Signed-off-by: Ruben Vargas <[email protected]> * fix lint issues Signed-off-by: Ruben Vargas <[email protected]> * breaking line Signed-off-by: Ruben Vargas <[email protected]> * Use api reader to avoid cache issues Signed-off-by: Ruben Vargas <[email protected]> * Add info metric to changelog entry Signed-off-by: Ruben Vargas <[email protected]> --------- Signed-off-by: Ruben Vargas <[email protected]> Co-authored-by: Israel Blancas <[email protected]>
1 parent c678e14 commit 438773a

File tree

11 files changed

+1166
-9
lines changed

11 files changed

+1166
-9
lines changed

.chloggen/usage_metrics.yaml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: collector
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Add usage metrics for the collector
9+
10+
# One or more tracking issues related to the change
11+
issues: [2829]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext: |
17+
This change will add metrics to the OpenTelemetry operator about how the collector is used in the cluster,
18+
it will add the following metrics to the opentelemetry-operator metrics endpoint
19+
```
20+
opentelemetry_collector_receivers{collector_name="collector_name", namespace="ns", type="otlp"} 1
21+
opentelemetry_collector_exporters{collector_name="collector_name", namespace="ns", type="otlp"} 1
22+
opentelemetry_collector_processors{collector_name="collector_name", namespace="ns", type="otlp"} 1
23+
opentelemetry_collector_connectors{collector_name="collector_name", namespace="ns", type="myconnector"} 0
24+
opentelemetry_collector_info{collector_name="simplest",namespace="default", type="deployment"} 1
25+
```

apis/v1beta1/collector_webhook.go

+41-5
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ type CollectorWebhook struct {
7676
cfg config.Config
7777
scheme *runtime.Scheme
7878
reviewer *rbac.Reviewer
79+
metrics *Metrics
7980
}
8081

8182
func (c CollectorWebhook) Default(_ context.Context, obj runtime.Object) error {
@@ -166,23 +167,57 @@ func (c CollectorWebhook) ValidateCreate(ctx context.Context, obj runtime.Object
166167
if !ok {
167168
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj)
168169
}
169-
return c.validate(ctx, otelcol)
170+
171+
warnings, err := c.validate(ctx, otelcol)
172+
if err != nil {
173+
return warnings, err
174+
}
175+
if c.metrics != nil {
176+
c.metrics.create(ctx, otelcol)
177+
}
178+
179+
return warnings, nil
170180
}
171181

172-
func (c CollectorWebhook) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (admission.Warnings, error) {
182+
func (c CollectorWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
173183
otelcol, ok := newObj.(*OpenTelemetryCollector)
174184
if !ok {
175185
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", newObj)
176186
}
177-
return c.validate(ctx, otelcol)
187+
188+
otelcolOld, ok := oldObj.(*OpenTelemetryCollector)
189+
if !ok {
190+
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", oldObj)
191+
}
192+
193+
warnings, err := c.validate(ctx, otelcol)
194+
if err != nil {
195+
return warnings, err
196+
}
197+
198+
if c.metrics != nil {
199+
c.metrics.update(ctx, otelcolOld, otelcol)
200+
}
201+
202+
return warnings, nil
178203
}
179204

180205
func (c CollectorWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
181206
otelcol, ok := obj.(*OpenTelemetryCollector)
182207
if !ok || otelcol == nil {
183208
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj)
184209
}
185-
return c.validate(ctx, otelcol)
210+
211+
warnings, err := c.validate(ctx, otelcol)
212+
if err != nil {
213+
return warnings, err
214+
}
215+
216+
if c.metrics != nil {
217+
c.metrics.delete(ctx, otelcol)
218+
}
219+
220+
return warnings, nil
186221
}
187222

188223
func (c CollectorWebhook) validate(ctx context.Context, r *OpenTelemetryCollector) (admission.Warnings, error) {
@@ -419,12 +454,13 @@ func checkAutoscalerSpec(autoscaler *AutoscalerSpec) error {
419454
return nil
420455
}
421456

422-
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer) error {
457+
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer, metrics *Metrics) error {
423458
cvw := &CollectorWebhook{
424459
reviewer: reviewer,
425460
logger: mgr.GetLogger().WithValues("handler", "CollectorWebhook", "version", "v1beta1"),
426461
scheme: mgr.GetScheme(),
427462
cfg: cfg,
463+
metrics: metrics,
428464
}
429465
return ctrl.NewWebhookManagedBy(mgr).
430466
For(&OpenTelemetryCollector{}).

apis/v1beta1/metrics.go

+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// 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+
15+
package v1beta1
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"strings"
21+
22+
"go.opentelemetry.io/otel/attribute"
23+
"go.opentelemetry.io/otel/exporters/prometheus"
24+
"go.opentelemetry.io/otel/metric"
25+
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
26+
"sigs.k8s.io/controller-runtime/pkg/client"
27+
"sigs.k8s.io/controller-runtime/pkg/metrics"
28+
)
29+
30+
const (
31+
meterName = "crd-metrics"
32+
)
33+
34+
// Metric labels
35+
36+
const (
37+
prefix = "opentelemetry_collector_"
38+
receiversMetricName = prefix + "receivers"
39+
exportersMetricName = prefix + "exporters"
40+
processorsMetricName = prefix + "processors"
41+
extensionsMetricName = prefix + "extensions"
42+
connectorsMetricName = prefix + "connectors"
43+
modeMetricName = prefix + "info"
44+
)
45+
46+
// TODO: Refactor this logic, centralize it. See: https://github.com/open-telemetry/opentelemetry-operator/issues/2603
47+
type components struct {
48+
receivers []string
49+
processors []string
50+
exporters []string
51+
extensions []string
52+
connectors []string
53+
}
54+
55+
// Metrics hold all gauges for the different metrics related to the CRs
56+
// +kubebuilder:object:generate=false
57+
type Metrics struct {
58+
modeCounter metric.Int64UpDownCounter
59+
receiversCounter metric.Int64UpDownCounter
60+
exporterCounter metric.Int64UpDownCounter
61+
processorCounter metric.Int64UpDownCounter
62+
extensionsCounter metric.Int64UpDownCounter
63+
connectorsCounter metric.Int64UpDownCounter
64+
}
65+
66+
// BootstrapMetrics configures the OpenTelemetry meter provider with the Prometheus exporter.
67+
func BootstrapMetrics() (metric.MeterProvider, error) {
68+
exporter, err := prometheus.New(prometheus.WithRegisterer(metrics.Registry))
69+
if err != nil {
70+
return nil, err
71+
}
72+
return sdkmetric.NewMeterProvider(sdkmetric.WithReader(exporter)), err
73+
}
74+
75+
func NewMetrics(prv metric.MeterProvider, ctx context.Context, cl client.Reader) (*Metrics, error) {
76+
meter := prv.Meter(meterName)
77+
modeCounter, err := meter.Int64UpDownCounter(modeMetricName)
78+
if err != nil {
79+
return nil, err
80+
}
81+
receiversCounter, err := meter.Int64UpDownCounter(receiversMetricName)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
exporterCounter, err := meter.Int64UpDownCounter(exportersMetricName)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
processorCounter, err := meter.Int64UpDownCounter(processorsMetricName)
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
extensionsCounter, err := meter.Int64UpDownCounter(extensionsMetricName)
97+
if err != nil {
98+
return nil, err
99+
}
100+
101+
connectorsCounter, err := meter.Int64UpDownCounter(connectorsMetricName)
102+
if err != nil {
103+
return nil, err
104+
}
105+
106+
m := &Metrics{
107+
modeCounter: modeCounter,
108+
receiversCounter: receiversCounter,
109+
exporterCounter: exporterCounter,
110+
processorCounter: processorCounter,
111+
extensionsCounter: extensionsCounter,
112+
connectorsCounter: connectorsCounter,
113+
}
114+
115+
err = m.init(ctx, cl)
116+
if err != nil {
117+
return nil, err
118+
}
119+
return m, nil
120+
}
121+
122+
// Init metrics from the first time the operator starts.
123+
func (m *Metrics) init(ctx context.Context, cl client.Reader) error {
124+
opts := []client.ListOption{
125+
client.MatchingLabels(map[string]string{
126+
"app.kubernetes.io/managed-by": "opentelemetry-operator",
127+
}),
128+
}
129+
list := &OpenTelemetryCollectorList{}
130+
if err := cl.List(ctx, list, opts...); err != nil {
131+
return fmt.Errorf("failed to list: %w", err)
132+
}
133+
134+
for i := range list.Items {
135+
m.create(ctx, &list.Items[i])
136+
}
137+
return nil
138+
}
139+
140+
func (m *Metrics) create(ctx context.Context, collector *OpenTelemetryCollector) {
141+
m.updateComponentCounters(ctx, collector, true)
142+
m.updateGeneralCRMetricsComponents(ctx, collector, true)
143+
}
144+
145+
func (m *Metrics) delete(ctx context.Context, collector *OpenTelemetryCollector) {
146+
m.updateComponentCounters(ctx, collector, false)
147+
m.updateGeneralCRMetricsComponents(ctx, collector, false)
148+
}
149+
150+
func (m *Metrics) update(ctx context.Context, oldCollector *OpenTelemetryCollector, newCollector *OpenTelemetryCollector) {
151+
m.delete(ctx, oldCollector)
152+
m.create(ctx, newCollector)
153+
}
154+
155+
func (m *Metrics) updateGeneralCRMetricsComponents(ctx context.Context, collector *OpenTelemetryCollector, up bool) {
156+
157+
inc := 1
158+
if !up {
159+
inc = -1
160+
}
161+
m.modeCounter.Add(ctx, int64(inc), metric.WithAttributes(
162+
attribute.Key("collector_name").String(collector.Name),
163+
attribute.Key("namespace").String(collector.Namespace),
164+
attribute.Key("type").String(string(collector.Spec.Mode)),
165+
))
166+
}
167+
func (m *Metrics) updateComponentCounters(ctx context.Context, collector *OpenTelemetryCollector, up bool) {
168+
components := getComponentsFromConfig(collector.Spec.Config)
169+
moveCounter(ctx, collector, components.receivers, m.receiversCounter, up)
170+
moveCounter(ctx, collector, components.exporters, m.exporterCounter, up)
171+
moveCounter(ctx, collector, components.processors, m.processorCounter, up)
172+
moveCounter(ctx, collector, components.extensions, m.extensionsCounter, up)
173+
moveCounter(ctx, collector, components.connectors, m.connectorsCounter, up)
174+
175+
}
176+
177+
func extractElements(elements map[string]interface{}) []string {
178+
// TODO: we should get rid of this method and centralize the parse logic
179+
// see https://github.com/open-telemetry/opentelemetry-operator/issues/2603
180+
if elements == nil {
181+
return []string{}
182+
}
183+
184+
itemsMap := map[string]struct{}{}
185+
var items []string
186+
for key := range elements {
187+
itemName := strings.SplitN(key, "/", 2)[0]
188+
itemsMap[itemName] = struct{}{}
189+
}
190+
for key := range itemsMap {
191+
items = append(items, key)
192+
}
193+
return items
194+
}
195+
196+
func getComponentsFromConfig(yamlContent Config) *components {
197+
198+
info := &components{
199+
receivers: extractElements(yamlContent.Receivers.Object),
200+
exporters: extractElements(yamlContent.Exporters.Object),
201+
}
202+
203+
if yamlContent.Processors != nil {
204+
info.processors = extractElements(yamlContent.Processors.Object)
205+
}
206+
207+
if yamlContent.Extensions != nil {
208+
info.extensions = extractElements(yamlContent.Extensions.Object)
209+
}
210+
211+
if yamlContent.Connectors != nil {
212+
info.connectors = extractElements(yamlContent.Connectors.Object)
213+
}
214+
215+
return info
216+
}
217+
218+
func moveCounter(
219+
ctx context.Context, collector *OpenTelemetryCollector, types []string, upDown metric.Int64UpDownCounter, up bool) {
220+
for _, exporter := range types {
221+
inc := 1
222+
if !up {
223+
inc = -1
224+
}
225+
upDown.Add(ctx, int64(inc), metric.WithAttributes(
226+
attribute.Key("collector_name").String(collector.Name),
227+
attribute.Key("namespace").String(collector.Namespace),
228+
attribute.Key("type").String(exporter),
229+
))
230+
}
231+
}

0 commit comments

Comments
 (0)