Skip to content

Commit

Permalink
Add crd metrics usage information
Browse files Browse the repository at this point in the history
Signed-off-by: Ruben Vargas <[email protected]>
  • Loading branch information
rubenvp8510 committed May 7, 2024
1 parent 9944af6 commit 37e2e95
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 4 deletions.
22 changes: 22 additions & 0 deletions .chloggen/usage_metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
component: collector

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add usage metrics for the collector

# One or more tracking issues related to the change
issues: [2829]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
This change will add metrics to the OpenTelemetry operator about how the collector is used in the cluster,
it will add the following metrics to the opentelemetry-operator metrics endpoint
opentelemetry_collector_receivers{collector_name="collector_name", namespace="ns", type="otlp"} 1
opentelemetry_collector_exporters{collector_name="collector_name", namespace="ns", type="otlp"} 1
opentelemetry_collector_processors{collector_name="collector_name", namespace="ns", type="otlp"} 1
opentelemetry_collector_processors{collector_name="collector_name", namespace="ns", type="otlp"} 0
51 changes: 47 additions & 4 deletions apis/v1beta1/collector_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,66 @@ func (c CollectorWebhook) ValidateCreate(ctx context.Context, obj runtime.Object
if !ok {
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj)
}
return c.validate(ctx, otelcol)

warnings, err := c.validate(ctx, otelcol)
if err != nil {
return warnings, err
}

err = IncCounters(ctx, otelcol)
if err != nil {
return warnings, err
}

return warnings, nil
}

func (c CollectorWebhook) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (admission.Warnings, error) {
func (c CollectorWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
otelcol, ok := newObj.(*OpenTelemetryCollector)
if !ok {
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", newObj)
}
return c.validate(ctx, otelcol)

otelcolOld, ok := oldObj.(*OpenTelemetryCollector)
if !ok {
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", oldObj)
}

warnings, err := c.validate(ctx, otelcol)
if err != nil {
return warnings, err
}

// Decrease all metrics related to old CR
err = DecCounters(ctx, otelcolOld)
if err != nil {
return warnings, err
}

// Increase all metrics related to new CR
err = IncCounters(ctx, otelcolOld)
if err != nil {
return warnings, err
}
return warnings, nil
}

func (c CollectorWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
otelcol, ok := obj.(*OpenTelemetryCollector)
if !ok || otelcol == nil {
return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj)
}
return c.validate(ctx, otelcol)

warnings, err := c.validate(ctx, otelcol)
if err != nil {
return warnings, err
}
err = DecCounters(ctx, otelcol)
if err != nil {
return warnings, err
}

return warnings, nil
}

func (c CollectorWebhook) validate(ctx context.Context, r *OpenTelemetryCollector) (admission.Warnings, error) {
Expand Down
149 changes: 149 additions & 0 deletions apis/v1beta1/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1beta1

import (
"context"
"strings"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"sigs.k8s.io/controller-runtime/pkg/metrics"
)

const (
meterName = "crd-metrics"
)

// Metric labels

const (
prefix = "opentelemetry_collector_"
receivers = prefix + "receivers"
exporters = prefix + "exporters"
processors = prefix + "processors"
extensions = prefix + "extensions"
mode = prefix + "info"
)

type components struct {
receivers []string
processors []string
exporters []string
extensions []string
}

// BootstrapMetrics configures the OpenTelemetry meter provider with the Prometheus exporter.
func BootstrapMetrics() error {

exporter, err := prometheus.New(prometheus.WithRegisterer(metrics.Registry))
if err != nil {
return err
}
provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(exporter))
otel.SetMeterProvider(provider)
return err
}

func extractElements(elements map[string]interface{}) []string {
if elements == nil {
return []string{}
}

itemsMap := map[string]struct{}{}
var items []string
for key := range elements {
itemName := strings.SplitN(key, "/", 2)[0]
itemsMap[itemName] = struct{}{}
}
for key := range itemsMap {
items = append(items, key)
}
return items
}

func getComponentsFromConfigV1Beta1(yamlContent Config) *components {

info := &components{
receivers: extractElements(yamlContent.Receivers.Object),
exporters: extractElements(yamlContent.Exporters.Object),
}

if yamlContent.Processors != nil {
info.processors = extractElements(yamlContent.Processors.Object)
}

if yamlContent.Extensions != nil {
info.extensions = extractElements(yamlContent.Extensions.Object)
}
return info
}

func IncCounters(ctx context.Context, collector *OpenTelemetryCollector) error {
return updateCounter(ctx, collector, true)
}

func DecCounters(ctx context.Context, collector *OpenTelemetryCollector) error {
return updateCounter(ctx, collector, false)
}

func updateCounter(ctx context.Context, collector *OpenTelemetryCollector, up bool) error {
meter := otel.Meter(meterName)
receiversCounter, err := meter.Int64UpDownCounter(receivers)
if err != nil {
return err
}

exporterCounter, err := meter.Int64UpDownCounter(exporters)
if err != nil {
return err
}

processorCounter, err := meter.Int64UpDownCounter(processors)
if err != nil {
return err
}

extensionsCounter, err := meter.Int64UpDownCounter(extensions)
if err != nil {
return err
}

components := getComponentsFromConfigV1Beta1(collector.Spec.Config)
moveCounter(ctx, collector, components.receivers, receiversCounter, up)
moveCounter(ctx, collector, components.exporters, exporterCounter, up)
moveCounter(ctx, collector, components.processors, processorCounter, up)
moveCounter(ctx, collector, components.extensions, extensionsCounter, up)

return nil
}

func moveCounter(
ctx context.Context, collector *OpenTelemetryCollector, types []string, upDown metric.Int64UpDownCounter, up bool) {
for _, exporter := range types {
inc := 1
if !up {
inc = -1
}
upDown.Add(ctx, int64(inc), metric.WithAttributes(
attribute.Key("collector_name").String(collector.Name),
attribute.Key("namespace").String(collector.Namespace),
attribute.Key("type").String(exporter),
))
}
}
6 changes: 6 additions & 0 deletions config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
resources:
- manager.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: quay.io/rvargasp/opentelemetry-operator
newTag: 1714976402.0.0
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ require (
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.48.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/exporters/prometheus v0.48.0 h1:sBQe3VNGUjY9IKWQC6z2lNqa5iGbDSxhs60ABwK4y0s=
go.opentelemetry.io/otel/exporters/prometheus v0.48.0/go.mod h1:DtrbMzoZWwQHyrQmCfLam5DZbnmorsGbOtTbYHycU5o=
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8=
Expand Down
10 changes: 10 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func main() {
enableNginxInstrumentation bool
enableNodeJSInstrumentation bool
enableJavaInstrumentation bool
enableCRMetrics bool
collectorImage string
targetAllocatorImage string
operatorOpAMPBridgeImage string
Expand Down Expand Up @@ -149,6 +150,8 @@ func main() {
pflag.BoolVar(&enableNginxInstrumentation, constants.FlagNginx, false, "Controls whether the operator supports nginx auto-instrumentation")
pflag.BoolVar(&enableNodeJSInstrumentation, constants.FlagNodeJS, true, "Controls whether the operator supports nodejs auto-instrumentation")
pflag.BoolVar(&enableJavaInstrumentation, constants.FlagJava, true, "Controls whether the operator supports java auto-instrumentation")
pflag.BoolVar(&enableCRMetrics, constants.FlagCRMetrics, false, "Controls whether the CR metrics is enabled")

stringFlagOrEnv(&collectorImage, "collector-image", "RELATED_IMAGE_COLLECTOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:%s", v.OpenTelemetryCollector), "The default OpenTelemetry collector image. This image is used when no image is specified in the CustomResource.")
stringFlagOrEnv(&targetAllocatorImage, "target-allocator-image", "RELATED_IMAGE_TARGET_ALLOCATOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/target-allocator:%s", v.TargetAllocator), "The default OpenTelemetry target allocator image. This image is used when no image is specified in the CustomResource.")
stringFlagOrEnv(&operatorOpAMPBridgeImage, "operator-opamp-bridge-image", "RELATED_IMAGE_OPERATOR_OPAMP_BRIDGE", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:%s", v.OperatorOpAMPBridge), "The default OpenTelemetry Operator OpAMP Bridge image. This image is used when no image is specified in the CustomResource.")
Expand Down Expand Up @@ -313,6 +316,13 @@ func main() {
}
}
}

if enableCRMetrics {
if metricsErr := otelv1beta1.BootstrapMetrics(); metricsErr != nil {
setupLog.Error(metricsErr, "Error bootstrapping CRD metrics")
}
}

if cfg.LabelsFilter() != nil {
for _, basePattern := range cfg.LabelsFilter() {
_, compileErr := regexp.Compile(basePattern)
Expand Down
2 changes: 2 additions & 0 deletions pkg/constants/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const (
EnvNodeName = "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME"
EnvNodeIP = "OTEL_NODE_IP"

FlagCRMetrics = "enable-cr-metrics"

FlagApacheHttpd = "enable-apache-httpd-instrumentation"
FlagDotNet = "enable-dotnet-instrumentation"
FlagGo = "enable-go-instrumentation"
Expand Down

0 comments on commit 37e2e95

Please sign in to comment.