Skip to content

Commit

Permalink
Add kube-client metrics to operatorpkg (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-innis committed Jun 28, 2024
1 parent 8746151 commit 2457d6a
Showing 1 changed file with 129 additions and 0 deletions.
129 changes: 129 additions & 0 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package metrics

import (
"context"
"net/url"
"strings"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/samber/lo"
clientmetrics "k8s.io/client-go/tools/metrics"
)

// This package adds client-go metrics that can be surfaced through the Prometheus metrics server
// This is based on the reference implementation that was pulled out in controller-runtime in https://github.com/kubernetes-sigs/controller-runtime/pull/2298

var (
requestResult = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "client_go_request_total",
Help: "Number of HTTP requests, partitioned by status code and method.",
},
[]string{"code", "method"},
)
requestLatency = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "client_go_request_duration_seconds",
Help: "Request latency in seconds. Broken down by verb, group, version, kind, and subresource.",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
}, []string{"verb", "group", "version", "kind", "subresource"})
)

// RegisterClientMetrics sets up the client latency and result metrics from client-go.
func RegisterClientMetrics(r prometheus.Registerer) {
// register the metrics with our registry
r.MustRegister(requestResult, requestLatency)

clientmetrics.RequestLatency = &latencyAdapter{metric: requestLatency}
clientmetrics.RequestResult = &resultAdapter{metric: requestResult}
}

type resultAdapter struct {
metric *prometheus.CounterVec
}

func (r *resultAdapter) Increment(_ context.Context, code, method, _ string) {
r.metric.WithLabelValues(code, method).Inc()
}

// latencyAdapter implements LatencyMetric.
type latencyAdapter struct {
metric *prometheus.HistogramVec
}

// Observe increments the request latency metric for the given verb/group/version/kind/subresource.
func (l *latencyAdapter) Observe(_ context.Context, verb string, u url.URL, latency time.Duration) {
if data := parsePath(u.Path); data != nil {
// We update the "verb" to better reflect the action being taken by client-go
switch verb {
case "POST":
verb = "CREATE"
case "GET":
if !strings.Contains(u.Path, "{name}") {
verb = "LIST"
}
case "PUT":
if !strings.Contains(u.Path, "{name}") {
verb = "CREATE"
} else {
verb = "UPDATE"
}
}
l.metric.With(prometheus.Labels{
"verb": verb,
"group": data.group,
"version": data.version,
"kind": data.kind,
"subresource": data.subresource,
}).Observe(latency.Seconds())
}
}

// pathData stores data parsed out from the URL path
type pathData struct {
group string
version string
kind string
subresource string
}

// parsePath parses out the URL called from client-go to return back the group, version, kind, and subresource
// urls are formatted similar to /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases/{name} or /apis/karpenter.sh/v1beta1/nodeclaims/{name}
func parsePath(path string) *pathData {
parts := strings.Split(path, "/")[1:]

var groupIdx, versionIdx, kindIdx int
switch parts[0] {
case "api":
groupIdx = 0
case "apis":
groupIdx = 1
default:
return nil
}
// If the url is too short, then it's not interesting to us
if len(parts) < groupIdx+3 {
return nil
}
// This resource is namespaced
if parts[groupIdx+2] == "namespaces" {
versionIdx = groupIdx + 1
kindIdx = versionIdx + 3
} else {
versionIdx = groupIdx + 1
kindIdx = versionIdx + 1
}

// If we have a subresource, it's going to be two indices after the kind
var subresource string
if len(parts) == kindIdx+3 {
subresource = parts[kindIdx+2]
}
return &pathData{
// If the group index is 0, this is part of the core API, so there's no group
group: lo.Ternary(groupIdx == 0, "", parts[groupIdx]),
version: parts[versionIdx],
kind: parts[kindIdx],
subresource: subresource,
}
}

0 comments on commit 2457d6a

Please sign in to comment.