Skip to content

Commit 372bc18

Browse files
committed
Add crd metrics usage information
Signed-off-by: Ruben Vargas <[email protected]>
1 parent 5bfb867 commit 372bc18

File tree

8 files changed

+380
-0
lines changed

8 files changed

+380
-0
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ require (
3232
go.opentelemetry.io/collector/featuregate v1.0.1
3333
go.opentelemetry.io/otel v1.24.0
3434
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.24.0
35+
go.opentelemetry.io/otel/exporters/prometheus v0.46.0
3536
go.opentelemetry.io/otel/metric v1.24.0
3637
go.opentelemetry.io/otel/sdk v1.24.0
3738
go.opentelemetry.io/otel/sdk/metric v1.24.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYa
678678
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
679679
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q=
680680
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY=
681+
go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ=
682+
go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs=
681683
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
682684
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
683685
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=

main.go

+5
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import (
5555
"github.com/open-telemetry/opentelemetry-operator/internal/webhook/podmutation"
5656
collectorupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade"
5757
"github.com/open-telemetry/opentelemetry-operator/pkg/constants"
58+
"github.com/open-telemetry/opentelemetry-operator/pkg/crdmetrics"
5859
"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
5960
"github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation"
6061
instrumentationupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation/upgrade"
@@ -281,6 +282,10 @@ func main() {
281282
}
282283
reviewer := rbac.NewReviewer(clientset)
283284

285+
if metricsErr := crdmetrics.Bootstrap(mgr.GetClient()); metricsErr != nil {
286+
setupLog.Error(metricsErr, "Error bootstrapping CRD metrics")
287+
}
288+
284289
if err = controllers.NewReconciler(controllers.Params{
285290
Client: mgr.GetClient(),
286291
Log: ctrl.Log.WithName("controllers").WithName("OpenTelemetryCollector"),

pkg/crdmetrics/bootstrap.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 crdmetrics
16+
17+
import (
18+
"go.opentelemetry.io/otel"
19+
"go.opentelemetry.io/otel/exporters/prometheus"
20+
"go.opentelemetry.io/otel/sdk/metric"
21+
"sigs.k8s.io/controller-runtime/pkg/client"
22+
"sigs.k8s.io/controller-runtime/pkg/metrics"
23+
)
24+
25+
// Bootstrap configures the OpenTelemetry meter provider with the Prometheus exporter.
26+
func Bootstrap(client client.Client) error {
27+
28+
exporter, err := prometheus.New(prometheus.WithRegisterer(metrics.Registry))
29+
if err != nil {
30+
return err
31+
}
32+
provider := metric.NewMeterProvider(metric.WithReader(exporter))
33+
otel.SetMeterProvider(provider)
34+
// Create metrics
35+
openTelemetryCollectorMetrics := newOpenTelemetryCollectorMetrics(client)
36+
err = openTelemetryCollectorMetrics.Setup()
37+
return err
38+
}

pkg/crdmetrics/collector.go

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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 crdmetrics
16+
17+
import (
18+
"context"
19+
20+
"go.opentelemetry.io/otel"
21+
"go.opentelemetry.io/otel/metric"
22+
"sigs.k8s.io/controller-runtime/pkg/client"
23+
24+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
25+
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
26+
)
27+
28+
type OpenTelemetryCollectorMetrics struct {
29+
client client.Client
30+
observations []instancesView
31+
}
32+
33+
func newOpenTelemetryCollectorMetrics(client client.Client) *OpenTelemetryCollectorMetrics {
34+
return &OpenTelemetryCollectorMetrics{
35+
client: client,
36+
}
37+
}
38+
39+
func (i *OpenTelemetryCollectorMetrics) Setup() error {
40+
meter := otel.Meter(meterName)
41+
42+
obs, err := newObservation(meter,
43+
receivers,
44+
"Number of instances using certain receiver",
45+
"type",
46+
func(instance *collectorInfo) ([]string, bool) {
47+
return instance.components.receivers, true
48+
})
49+
if err != nil {
50+
return err
51+
}
52+
i.observations = append(i.observations, obs)
53+
54+
obs, err = newObservation(meter,
55+
exporters,
56+
"Number of instances using certain exporter",
57+
"type",
58+
func(instance *collectorInfo) ([]string, bool) {
59+
return instance.components.exporters, true
60+
})
61+
if err != nil {
62+
return err
63+
}
64+
i.observations = append(i.observations, obs)
65+
66+
obs, err = newObservation(meter,
67+
processors,
68+
"Number of instances using certain processor",
69+
"type",
70+
func(instance *collectorInfo) ([]string, bool) {
71+
return instance.components.processors, true
72+
73+
})
74+
if err != nil {
75+
return err
76+
}
77+
i.observations = append(i.observations, obs)
78+
79+
obs, err = newObservation(meter,
80+
extensions,
81+
"Number of instances using certain extension",
82+
"type",
83+
func(instance *collectorInfo) ([]string, bool) {
84+
return instance.components.extensions, true
85+
86+
})
87+
if err != nil {
88+
return err
89+
}
90+
i.observations = append(i.observations, obs)
91+
92+
obs, err = newObservation(meter,
93+
mode,
94+
"Collector deployment mode",
95+
"mode",
96+
func(instance *collectorInfo) ([]string, bool) {
97+
return []string{instance.mode}, true
98+
99+
})
100+
if err != nil {
101+
return err
102+
}
103+
i.observations = append(i.observations, obs)
104+
instruments := make([]metric.Observable, 0, len(i.observations))
105+
for _, o := range i.observations {
106+
instruments = append(instruments, o.Gauge)
107+
}
108+
_, err = meter.RegisterCallback(i.callback, instruments...)
109+
return err
110+
}
111+
112+
func (i *OpenTelemetryCollectorMetrics) callback(ctx context.Context, observer metric.Observer) error {
113+
v1alpha1collectors := &v1alpha1.OpenTelemetryCollectorList{}
114+
if err := i.client.List(ctx, v1alpha1collectors); err == nil {
115+
116+
// Reset observations
117+
for _, o := range i.observations {
118+
o.reset()
119+
}
120+
121+
for k := range v1alpha1collectors.Items {
122+
collector := v1alpha1collectors.Items[k]
123+
configInfo, err := getComponentsFromConfigV1Alpha1(collector.Spec.Config)
124+
if err != nil {
125+
return err
126+
}
127+
collectorInfo := &collectorInfo{
128+
mode: string(collector.Spec.Mode),
129+
version: "v1alpha1",
130+
components: configInfo,
131+
}
132+
for _, o := range i.observations {
133+
o.Record(collectorInfo)
134+
}
135+
}
136+
137+
v1beta1collectors := &v1beta1.OpenTelemetryCollectorList{}
138+
139+
for k := range v1beta1collectors.Items {
140+
collector := v1beta1collectors.Items[k]
141+
142+
collectorInfo := &collectorInfo{
143+
mode: string(collector.Spec.Mode),
144+
version: "v1beta1",
145+
components: getComponentsFromConfigV1Beta1(collector.Spec.Config),
146+
}
147+
for _, o := range i.observations {
148+
o.Record(collectorInfo)
149+
}
150+
}
151+
}
152+
153+
// Report metrics
154+
for _, o := range i.observations {
155+
o.Report(observer)
156+
}
157+
158+
return nil
159+
}

pkg/crdmetrics/consts.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 crdmetrics
16+
17+
const (
18+
meterName = "crd-metrics"
19+
)
20+
21+
// Metric labels
22+
23+
const (
24+
prefix = "opentelemetry_collector_"
25+
receivers = prefix + "receivers"
26+
exporters = prefix + "exporters"
27+
processors = prefix + "processors"
28+
extensions = prefix + "extensions"
29+
mode = prefix + "mode"
30+
)

pkg/crdmetrics/instance_view.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 crdmetrics
16+
17+
import (
18+
"go.opentelemetry.io/otel/attribute"
19+
"go.opentelemetry.io/otel/metric"
20+
)
21+
22+
type collectorInfo struct {
23+
components *components
24+
version string
25+
mode string
26+
}
27+
28+
type countFn func(instance *collectorInfo) ([]string, bool)
29+
30+
// This structure contains the labels associated with the instances and a counter of the number of instances.
31+
type instancesView struct {
32+
Name string
33+
Label string
34+
Count map[string]int
35+
Gauge metric.Int64ObservableGauge
36+
KeyFn countFn
37+
}
38+
39+
func (i *instancesView) reset() {
40+
for k := range i.Count {
41+
i.Count[k] = 0
42+
}
43+
}
44+
45+
func (i *instancesView) Record(instance *collectorInfo) {
46+
label, counted := i.KeyFn(instance)
47+
if counted {
48+
for _, v := range label {
49+
i.Count[v]++
50+
}
51+
}
52+
}
53+
54+
func (i *instancesView) Report(observer metric.Observer) {
55+
for key, count := range i.Count {
56+
opt := metric.WithAttributes(
57+
attribute.Key(i.Label).String(key),
58+
)
59+
observer.ObserveInt64(i.Gauge, int64(count), opt)
60+
}
61+
}
62+
63+
func newObservation(meter metric.Meter, name string, desc string, label string, keyFn countFn) (instancesView, error) {
64+
observation := instancesView{
65+
Name: name,
66+
Count: make(map[string]int),
67+
KeyFn: keyFn,
68+
Label: label,
69+
}
70+
71+
g, err := meter.Int64ObservableGauge(name, metric.WithDescription(desc))
72+
if err != nil {
73+
return instancesView{}, err
74+
}
75+
76+
observation.Gauge = g
77+
return observation, nil
78+
}

0 commit comments

Comments
 (0)