knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/profiling/server.go (about) 1 /* 2 Copyright 2019 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 profiling 18 19 import ( 20 "fmt" 21 "net/http" 22 "net/http/pprof" 23 "os" 24 "strconv" 25 "sync/atomic" 26 "time" 27 28 "go.uber.org/zap" 29 corev1 "k8s.io/api/core/v1" 30 ) 31 32 const ( 33 // ProfilingPortKey specified the name of an environment variable that 34 // may be used to override the default profiling port. 35 ProfilingPortKey = "PROFILING_PORT" 36 37 // ProfilingPort specifies the default port where profiling data is available when profiling is enabled 38 ProfilingPort = 8008 39 40 // profilingKey is the name of the key in config-observability config map 41 // that indicates whether profiling is enabled 42 profilingKey = "profiling.enable" 43 ) 44 45 // Handler holds the main HTTP handler and a flag indicating 46 // whether the handler is active 47 type Handler struct { 48 enabled *atomic.Bool 49 handler http.Handler 50 log *zap.SugaredLogger 51 } 52 53 // NewHandler create a new ProfilingHandler which serves runtime profiling data 54 // according to the given context path 55 func NewHandler(logger *zap.SugaredLogger, enableProfiling bool) *Handler { 56 const pprofPrefix = "/debug/pprof/" 57 58 mux := http.NewServeMux() 59 mux.HandleFunc(pprofPrefix, pprof.Index) 60 mux.HandleFunc(pprofPrefix+"cmdline", pprof.Cmdline) 61 mux.HandleFunc(pprofPrefix+"profile", pprof.Profile) 62 mux.HandleFunc(pprofPrefix+"symbol", pprof.Symbol) 63 mux.HandleFunc(pprofPrefix+"trace", pprof.Trace) 64 65 logger.Info("Profiling enabled: ", enableProfiling) 66 var enabled atomic.Bool 67 enabled.Store(enableProfiling) 68 69 return &Handler{ 70 enabled: &enabled, 71 handler: mux, 72 log: logger, 73 } 74 } 75 76 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 77 if h.enabled.Load() { 78 h.handler.ServeHTTP(w, r) 79 } else { 80 http.NotFoundHandler().ServeHTTP(w, r) 81 } 82 } 83 84 func ReadProfilingFlag(config map[string]string) (bool, error) { 85 profiling, ok := config[profilingKey] 86 if !ok { 87 return false, nil 88 } 89 enabled, err := strconv.ParseBool(profiling) 90 if err != nil { 91 return false, fmt.Errorf("failed to parse the profiling flag: %w", err) 92 } 93 return enabled, nil 94 } 95 96 // UpdateFromConfigMap modifies the Enabled flag in the Handler 97 // according to the value in the given ConfigMap 98 func (h *Handler) UpdateFromConfigMap(configMap *corev1.ConfigMap) { 99 enabled, err := ReadProfilingFlag(configMap.Data) 100 if err != nil { 101 h.log.Errorw("Failed to update the profiling flag", zap.Error(err)) 102 return 103 } 104 105 if h.enabled.Swap(enabled) != enabled { 106 h.log.Info("Profiling enabled: ", enabled) 107 } 108 } 109 110 // NewServer creates a new http server that exposes profiling data on the default profiling port 111 func NewServer(handler http.Handler) *http.Server { 112 port := os.Getenv(ProfilingPortKey) 113 if port == "" { 114 port = strconv.Itoa(ProfilingPort) 115 } 116 117 return &http.Server{ 118 Addr: ":" + port, 119 Handler: handler, 120 ReadHeaderTimeout: time.Minute, // https://medium.com/a-journey-with-go/go-understand-and-mitigate-slowloris-attack-711c1b1403f6 121 } 122 }