knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/observability/metrics/k8s/workqueue.go (about)

     1  /*
     2  Copyright 2025 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package k8s
    18  
    19  import (
    20  	"go.opentelemetry.io/otel"
    21  	"go.opentelemetry.io/otel/attribute"
    22  	"go.opentelemetry.io/otel/metric"
    23  
    24  	"k8s.io/client-go/util/workqueue"
    25  
    26  	"knative.dev/pkg/observability/attributekey"
    27  )
    28  
    29  var nameKey = attributekey.String("name")
    30  
    31  type WorkqueueMetricsProvider struct {
    32  	depth          metric.Int64UpDownCounter
    33  	adds           metric.Int64Counter
    34  	latency        metric.Float64Histogram
    35  	workDuration   metric.Float64Histogram
    36  	unfinishedWork metric.Float64Gauge
    37  	longestRunning metric.Float64Gauge
    38  	retries        metric.Int64Counter
    39  }
    40  
    41  type options struct {
    42  	meterProvider metric.MeterProvider
    43  }
    44  
    45  type Option func(*options)
    46  
    47  func WithMeterProvider(p metric.MeterProvider) Option {
    48  	return func(o *options) {
    49  		if p != nil {
    50  			o.meterProvider = p
    51  		}
    52  	}
    53  }
    54  
    55  func NewWorkqueueMetricsProvider(opts ...Option) (*WorkqueueMetricsProvider, error) {
    56  	options := options{
    57  		meterProvider: otel.GetMeterProvider(),
    58  	}
    59  	var err error
    60  
    61  	for _, opt := range opts {
    62  		opt(&options)
    63  	}
    64  
    65  	meter := options.meterProvider.Meter(scopeName)
    66  
    67  	w := &WorkqueueMetricsProvider{}
    68  
    69  	w.depth, err = meter.Int64UpDownCounter(
    70  		"kn.workqueue.depth",
    71  		metric.WithDescription("Number of current items in the queue"),
    72  		metric.WithUnit("{item}"),
    73  	)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	w.adds, err = meter.Int64Counter(
    79  		"kn.workqueue.adds",
    80  		metric.WithDescription("Number of items added to the queue"),
    81  		metric.WithUnit("{item}"),
    82  	)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	w.latency, err = meter.Float64Histogram(
    88  		"kn.workqueue.queue.duration",
    89  		metric.WithDescription("How long an item stays in workqueue"),
    90  		metric.WithUnit("s"),
    91  		metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
    92  	)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	w.workDuration, err = meter.Float64Histogram(
    98  		"kn.workqueue.process.duration",
    99  		metric.WithUnit("How long in seconds processing an item from workqueue takes"),
   100  		metric.WithUnit("s"),
   101  		metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
   102  	)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	w.unfinishedWork, err = meter.Float64Gauge(
   108  		"kn.workqueue.unfinished_work",
   109  		metric.WithUnit("s"),
   110  	)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	w.longestRunning, err = meter.Float64Gauge(
   116  		"kn.workqueue.longest_running_processor",
   117  		metric.WithUnit("s"),
   118  	)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	w.retries, err = meter.Int64Counter(
   124  		"kn.workqueue.retries",
   125  		metric.WithDescription("Number of items re-added to the queue"),
   126  		metric.WithUnit("{item}"),
   127  	)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	return w, nil
   133  }
   134  
   135  func (p *WorkqueueMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric {
   136  	return &gauge{m: p.depth, attrs: attrs(name)}
   137  }
   138  
   139  func (p *WorkqueueMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric {
   140  	return &counter{m: p.adds, attrs: attrs(name)}
   141  }
   142  
   143  func (p *WorkqueueMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric {
   144  	return &histogram{m: p.latency, attrs: attrs(name)}
   145  }
   146  
   147  func (p *WorkqueueMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric {
   148  	return &histogram{m: p.workDuration, attrs: attrs(name)}
   149  }
   150  
   151  func (p *WorkqueueMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric {
   152  	return &settableGauge{m: p.unfinishedWork, attrs: attrs(name)}
   153  }
   154  
   155  func (p *WorkqueueMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric {
   156  	return &settableGauge{m: p.longestRunning, attrs: attrs(name)}
   157  }
   158  
   159  func (p *WorkqueueMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric {
   160  	return &counter{m: p.retries, attrs: attrs(name)}
   161  }
   162  
   163  var _ workqueue.MetricsProvider = (*WorkqueueMetricsProvider)(nil)
   164  
   165  func attrs(name string) attribute.Set {
   166  	return attribute.NewSet(nameKey.With(name))
   167  }