Skip to content

Commit 7809ffb

Browse files
authored
Merge pull request #6675 from chrischdi/pr-runtime-sdk-experiments-poc-runtime-client-metrics
🌱 Add metrics to Runtime SDK hook client
2 parents 28d146b + e36b78f commit 7809ffb

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ require (
114114
github.com/pelletier/go-toml v1.9.5 // indirect
115115
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
116116
github.com/pmezard/go-difflib v1.0.0 // indirect
117-
github.com/prometheus/client_golang v1.12.1 // indirect
117+
github.com/prometheus/client_golang v1.12.1
118118
github.com/prometheus/client_model v0.2.0 // indirect
119119
github.com/prometheus/common v0.32.1 // indirect
120120
github.com/prometheus/procfs v0.7.3 // indirect

internal/runtime/client/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
4646
runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
4747
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
48+
runtimemetrics "sigs.k8s.io/cluster-api/internal/runtime/metrics"
4849
runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry"
4950
"sigs.k8s.io/cluster-api/util"
5051
)
@@ -377,6 +378,11 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
377378
return errors.Wrap(err, "http call failed")
378379
}
379380

381+
// Observe request duration metric.
382+
start := time.Now()
383+
defer func() {
384+
runtimemetrics.RequestDuration.Observe(opts.hookGVH, *extensionURL, time.Since(start))
385+
}()
380386
requireConversion := opts.registrationGVH.Version != opts.hookGVH.Version
381387

382388
requestLocal := request
@@ -450,6 +456,8 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
450456
})
451457

452458
resp, err := client.Do(httpRequest)
459+
// Create http request metric.
460+
runtimemetrics.RequestsTotal.Observe(httpRequest, resp, opts.hookGVH, err)
453461
if err != nil {
454462
return errCallingExtensionHandler(
455463
errors.Wrapf(err, "http call failed"),

internal/runtime/metrics/metrics.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Copyright 2022 The Kubernetes 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 metrics provides functions for creating Runtime SDK related metrics.
18+
package metrics
19+
20+
import (
21+
"net/http"
22+
"net/url"
23+
"strconv"
24+
"time"
25+
26+
"github.com/prometheus/client_golang/prometheus"
27+
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
28+
29+
runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
30+
)
31+
32+
func init() {
33+
// Register the metrics at the controller-runtime metrics registry.
34+
ctrlmetrics.Registry.MustRegister(RequestsTotal.metric)
35+
ctrlmetrics.Registry.MustRegister(RequestDuration.metric)
36+
}
37+
38+
// Metrics subsystem and all of the keys used by the Runtime SDK.
39+
const (
40+
runtimeSDKSubsystem = "capi_runtime_sdk"
41+
)
42+
43+
var (
44+
// RequestsTotal reports request results.
45+
RequestsTotal = requestsTotalObserver{
46+
prometheus.NewCounterVec(prometheus.CounterOpts{
47+
Subsystem: runtimeSDKSubsystem,
48+
Name: "requests_total",
49+
Help: "Number of HTTP requests, partitioned by status code, host and hook.",
50+
}, []string{"code", "host", "group", "version", "hook"}),
51+
}
52+
// RequestDuration reports the request latency in seconds.
53+
RequestDuration = requestDurationObserver{
54+
prometheus.NewHistogramVec(prometheus.HistogramOpts{
55+
Subsystem: runtimeSDKSubsystem,
56+
Name: "request_duration_seconds",
57+
Help: "Request duration in seconds, broken down by hook and host.",
58+
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
59+
}, []string{"host", "group", "version", "hook"}),
60+
}
61+
)
62+
63+
type requestsTotalObserver struct {
64+
metric *prometheus.CounterVec
65+
}
66+
67+
// Observe observes a http request result and increments the metric for the given
68+
// error status code, host and gvh.
69+
func (m *requestsTotalObserver) Observe(req *http.Request, resp *http.Response, gvh runtimecatalog.GroupVersionHook, err error) {
70+
host := req.URL.Host
71+
72+
// Errors can be arbitrary strings. Unbound label cardinality is not suitable for a metric
73+
// system so they are reported as `<error>`.
74+
code := "<error>"
75+
if err == nil {
76+
code = strconv.Itoa(resp.StatusCode)
77+
}
78+
m.metric.WithLabelValues(code, host, gvh.Group, gvh.Version, gvh.Hook).Inc()
79+
}
80+
81+
type requestDurationObserver struct {
82+
metric *prometheus.HistogramVec
83+
}
84+
85+
// Observe increments the request latency metric for the given host and gvh.
86+
func (m *requestDurationObserver) Observe(gvh runtimecatalog.GroupVersionHook, u url.URL, latency time.Duration) {
87+
m.metric.WithLabelValues(u.Host, gvh.Group, gvh.Version, gvh.Hook).Observe(latency.Seconds())
88+
}

0 commit comments

Comments
 (0)