k8s.io/apiserver@v0.31.1/pkg/endpoints/filterlatency/filterlatency.go (about) 1 /* 2 Copyright 2020 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 filterlatency 18 19 import ( 20 "context" 21 "fmt" 22 "net/http" 23 "time" 24 25 "go.opentelemetry.io/otel/trace" 26 27 "k8s.io/apiserver/pkg/endpoints/metrics" 28 apirequest "k8s.io/apiserver/pkg/endpoints/request" 29 "k8s.io/apiserver/pkg/server/httplog" 30 "k8s.io/klog/v2" 31 "k8s.io/utils/clock" 32 ) 33 34 type requestFilterRecordKeyType int 35 36 // requestFilterRecordKey is the context key for a request filter record struct. 37 const requestFilterRecordKey requestFilterRecordKeyType = iota 38 39 const minFilterLatencyToLog = 100 * time.Millisecond 40 41 type requestFilterRecord struct { 42 name string 43 startedTimestamp time.Time 44 } 45 46 // withRequestFilterRecord attaches the given request filter record to the parent context. 47 func withRequestFilterRecord(parent context.Context, fr *requestFilterRecord) context.Context { 48 return apirequest.WithValue(parent, requestFilterRecordKey, fr) 49 } 50 51 // requestFilterRecordFrom returns the request filter record from the given context. 52 func requestFilterRecordFrom(ctx context.Context) *requestFilterRecord { 53 fr, _ := ctx.Value(requestFilterRecordKey).(*requestFilterRecord) 54 return fr 55 } 56 57 // TrackStarted measures the timestamp the given handler has started execution 58 // by attaching a handler to the chain. 59 func TrackStarted(handler http.Handler, tp trace.TracerProvider, name string) http.Handler { 60 return trackStarted(handler, tp, name, clock.RealClock{}) 61 } 62 63 // TrackCompleted measures the timestamp the given handler has completed execution and then 64 // it updates the corresponding metric with the filter latency duration. 65 func TrackCompleted(handler http.Handler) http.Handler { 66 return trackCompleted(handler, clock.RealClock{}, func(ctx context.Context, fr *requestFilterRecord, completedAt time.Time) { 67 latency := completedAt.Sub(fr.startedTimestamp) 68 metrics.RecordFilterLatency(ctx, fr.name, latency) 69 if klog.V(3).Enabled() && latency > minFilterLatencyToLog { 70 httplog.AddKeyValue(ctx, fmt.Sprintf("fl_%s", fr.name), latency.String()) 71 } 72 }) 73 } 74 75 func trackStarted(handler http.Handler, tp trace.TracerProvider, name string, clock clock.PassiveClock) http.Handler { 76 // This is a noop if the tracing is disabled, since tp will be a NoopTracerProvider 77 tracer := tp.Tracer("k8s.op/apiserver/pkg/endpoints/filterlatency") 78 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 79 ctx := r.Context() 80 if fr := requestFilterRecordFrom(ctx); fr != nil { 81 fr.name = name 82 fr.startedTimestamp = clock.Now() 83 84 handler.ServeHTTP(w, r) 85 return 86 } 87 88 fr := &requestFilterRecord{ 89 name: name, 90 startedTimestamp: clock.Now(), 91 } 92 ctx, _ = tracer.Start(ctx, name) 93 r = r.WithContext(withRequestFilterRecord(ctx, fr)) 94 handler.ServeHTTP(w, r) 95 }) 96 } 97 98 func trackCompleted(handler http.Handler, clock clock.PassiveClock, action func(context.Context, *requestFilterRecord, time.Time)) http.Handler { 99 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 100 // The previous filter has just completed. 101 completedAt := clock.Now() 102 103 defer handler.ServeHTTP(w, r) 104 105 ctx := r.Context() 106 if fr := requestFilterRecordFrom(ctx); fr != nil { 107 action(ctx, fr, completedAt) 108 } 109 trace.SpanFromContext(ctx).End() 110 }) 111 }