Skip to content

Commit 8e01753

Browse files
committed
UPSTREAM: <carry>: add http logging for kubelet metrics endpoint
1 parent 9c2642e commit 8e01753

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

pkg/kubelet/server/server.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -474,15 +474,17 @@ func (s *Server) InstallAuthNotRequiredHandlers() {
474474
r.RawMustRegister(metrics.NewPrometheusCollector(prometheusHostAdapter{s.host}, containerPrometheusLabelsFunc(s.host), includedMetrics, clock.RealClock{}, cadvisorOpts))
475475
}
476476
s.restfulCont.Handle(cadvisorMetricsPath,
477-
compbasemetrics.HandlerFor(r, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
478-
)
477+
WithHTTPLogging(
478+
compbasemetrics.HandlerFor(r, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
479+
))
479480

480481
s.addMetricsBucketMatcher("metrics/resource")
481482
resourceRegistry := compbasemetrics.NewKubeRegistry()
482483
resourceRegistry.CustomMustRegister(collectors.NewResourceMetricsCollector(s.resourceAnalyzer))
483484
s.restfulCont.Handle(resourceMetricsPath,
484-
compbasemetrics.HandlerFor(resourceRegistry, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
485-
)
485+
WithHTTPLogging(
486+
compbasemetrics.HandlerFor(resourceRegistry, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
487+
))
486488

487489
// prober metrics are exposed under a different endpoint
488490

pkg/kubelet/server/server_patch.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package server
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"time"
7+
8+
"k8s.io/apiserver/pkg/audit"
9+
"k8s.io/apiserver/pkg/endpoints/responsewriter"
10+
"k8s.io/klog/v2"
11+
12+
"github.com/google/uuid"
13+
)
14+
15+
func WithHTTPLogging(handler http.Handler) http.Handler {
16+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
17+
logger := newHTTPLogger(w, req)
18+
defer logger.log()
19+
20+
w = responsewriter.WrapForHTTP1Or2(logger)
21+
handler.ServeHTTP(w, req)
22+
})
23+
}
24+
25+
func newHTTPLogger(w http.ResponseWriter, req *http.Request) *httpLogger {
26+
auditID := audit.GetAuditIDTruncated(req.Context())
27+
if len(auditID) == 0 {
28+
auditID = uuid.New().String()
29+
}
30+
return &httpLogger{
31+
w: w,
32+
startedAt: time.Now(),
33+
method: req.Method,
34+
requestURI: req.RequestURI,
35+
auditID: auditID,
36+
userAgent: req.UserAgent(),
37+
srcIP: req.RemoteAddr,
38+
}
39+
}
40+
41+
type httpLogger struct {
42+
w http.ResponseWriter
43+
44+
method string
45+
requestURI string
46+
auditID string
47+
userAgent string
48+
srcIP string
49+
50+
startedAt time.Time
51+
writeLatency time.Duration
52+
flushLatency time.Duration
53+
writeBytes int
54+
statusRecorded bool
55+
statusCode int
56+
}
57+
58+
var _ http.ResponseWriter = &httpLogger{}
59+
var _ responsewriter.UserProvidedDecorator = &httpLogger{}
60+
61+
func (l *httpLogger) Unwrap() http.ResponseWriter {
62+
return l.w
63+
}
64+
65+
// Header implements http.ResponseWriter.
66+
func (l *httpLogger) Header() http.Header {
67+
return l.w.Header()
68+
}
69+
70+
// Write implements http.ResponseWriter.
71+
func (l *httpLogger) Write(b []byte) (int, error) {
72+
if !l.statusRecorded {
73+
l.record(http.StatusOK) // Default if WriteHeader hasn't been called
74+
}
75+
now := time.Now()
76+
var written int
77+
defer func() {
78+
l.writeLatency += time.Since(now)
79+
l.writeBytes += written
80+
}()
81+
written, err := l.w.Write(b)
82+
return written, err
83+
}
84+
85+
func (l *httpLogger) Flush() {
86+
now := time.Now()
87+
defer func() {
88+
l.flushLatency += time.Since(now)
89+
}()
90+
l.w.(http.Flusher).Flush()
91+
}
92+
93+
// WriteHeader implements http.ResponseWriter.
94+
func (l *httpLogger) WriteHeader(status int) {
95+
l.record(status)
96+
l.w.WriteHeader(status)
97+
}
98+
99+
func (l *httpLogger) record(status int) {
100+
l.statusCode = status
101+
l.statusRecorded = true
102+
}
103+
104+
func (l *httpLogger) log() {
105+
latency := time.Since(l.startedAt)
106+
kvs := []interface{}{
107+
"method", l.method,
108+
"URI", l.requestURI,
109+
"latency", latency,
110+
"userAgent", l.userAgent,
111+
"audit-ID", l.auditID,
112+
"srcIP", l.srcIP,
113+
"status", l.statusCode,
114+
"writeLatency", l.writeLatency,
115+
"writtenBytes", fmt.Sprintf("%dK", l.writeBytes/1024),
116+
"flushLatency", l.flushLatency,
117+
}
118+
klog.V(1).InfoSDepth(1, "HTTP", kvs...)
119+
}

0 commit comments

Comments
 (0)