plugin_main/plugin_http.go (169 lines of code) (raw):

// Copyright 2021 iLogtail Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "io" "net/http" _ "net/http/pprof" //nolint "os" "runtime" "runtime/debug" "runtime/pprof" "runtime/trace" "sync" "time" "github.com/alibaba/ilogtail/pkg/config" "github.com/alibaba/ilogtail/pkg/flags" "github.com/alibaba/ilogtail/pkg/logger" "github.com/alibaba/ilogtail/pluginmanager" ) var ( handlers = make(map[string]*handler) // contains the whole export http.HandlerFunc. controlLock sync.Mutex // the http logtail plugin control handlers should be invoked in order. once sync.Once ) // handler wraps the http.HandlerFunc with description. type handler struct { handlerFunc http.HandlerFunc description string } // HandleMem dump the memory info at once. func HandleMem(w http.ResponseWriter, req *http.Request) { DumpMemInfo(0) } // HandleTrace dump the trace info at once. func HandleTrace(w http.ResponseWriter, req *http.Request) { DumpTraceInfo() } // HandleCPU dump the CPU info in 30 seconds. func HandleCPU(w http.ResponseWriter, req *http.Request) { DumpCPUInfo(30) } // HandleCPU10 dump the CPU info in 10 seconds. func HandleCPU10(w http.ResponseWriter, req *http.Request) { DumpCPUInfo(10) } // HandleCPU180 dump the CPU info in 180 seconds. func HandleCPU180(w http.ResponseWriter, req *http.Request) { DumpCPUInfo(180) } // HandleForceGC trigger force GC. func HandleForceGC(w http.ResponseWriter, req *http.Request) { logger.Info(context.Background(), "GC begin") runtime.GC() logger.Info(context.Background(), "GC done") debug.FreeOSMemory() logger.Info(context.Background(), "Free os memory done") } // HelpServer show the help messages of the whole exported handlers. func HelpServer(w http.ResponseWriter, req *http.Request) { _, _ = io.WriteString(w, ` /mem to dump mem info /cpu to dump cpu info, default 30 seconds /cpu10 to dump cpu info, 10 seconds /cpu180 to dump cpu info, 180 seconds /debug/pprof/goroutine?debug=1 /debug/pprof/heap?debug=1 /debug/pprof/threadcreate?debug=1 /forcegc `) } // HandleLoadConfig load the new ilogtail config. func HandleLoadConfig(w http.ResponseWriter, r *http.Request) { controlLock.Lock() defer controlLock.Unlock() bytes, err := io.ReadAll(r.Body) if err != nil { logger.Error(context.Background(), "LOAD_CONFIG_ALARM", "stage", "read", "err", err) w.WriteHeader(500) _, _ = w.Write([]byte("read body error")) return } logger.Infof(context.Background(), "%s", string(bytes)) loadConfigs, err := config.DeserializeLoadedConfig(bytes) if err != nil { logger.Error(context.Background(), "LOAD_CONFIG_ALARM", "stage", "parse", "err", err) w.WriteHeader(500) _, _ = w.Write([]byte("parse body error")) return } for _, cfg := range loadConfigs { Stop(cfg.ConfigName, 0) LoadPipeline(cfg.Project, cfg.Logstore, cfg.ConfigName, cfg.LogstoreKey, cfg.JSONStr) Start(cfg.ConfigName) } } // HandleHoldOn hold on the ilogtail process. func HandleHoldOn(w http.ResponseWriter, r *http.Request) { controlLock.Lock() defer controlLock.Unlock() StopAllPipelines(1) StopAllPipelines(0) w.WriteHeader(http.StatusOK) } // DumpCPUInfo dump the cpu profile info with the given seconds. func DumpCPUInfo(seconds int) { f, err := os.Create(*flags.Cpuprofile) if err != nil { return } if err := pprof.StartCPUProfile(f); err != nil { return } time.Sleep((time.Duration)(seconds) * time.Second) pprof.StopCPUProfile() } // DumpTraceInfo dump the logtail plugin process trace info. func DumpTraceInfo() { f, err := os.Create("trace.out") if err != nil { return } defer func(f *os.File) { _ = f.Close() }(f) err = trace.Start(f) if err != nil { return } defer trace.Stop() } // DumpMemInfo dump the mem profile info after the given seconds. func DumpMemInfo(seconds int) { time.Sleep((time.Duration)(seconds) * time.Second) f, err := os.Create(*flags.Memprofile) if err != nil { return } _ = pprof.WriteHeapProfile(f) _ = f.Close() } // InitHTTPServer chooses the exported handlers according to the flags and starts an HTTP server. // When the HTTP prof flag is opening, the debug handlers will also work, which are exported by // `net/http/pprof` package. func InitHTTPServer() { once.Do(func() { if *flags.HTTPLoadFlag { handlers["/loadconfig"] = &handler{handlerFunc: HandleLoadConfig, description: "load new logtail plugin configuration"} handlers["/holdon"] = &handler{handlerFunc: HandleHoldOn, description: "hold on logtail plugin process"} } if *flags.HTTPProfFlag { handlers["/mem"] = &handler{handlerFunc: HandleMem, description: "dump mem info"} handlers["/cpu"] = &handler{handlerFunc: HandleCPU, description: "dump cpu info, default 30 seconds"} handlers["/cpu10"] = &handler{handlerFunc: HandleCPU10, description: "dump cpu info, 10 seconds"} handlers["/cpu180"] = &handler{handlerFunc: HandleCPU180, description: "dump cpu info, 180 seconds"} handlers["/forcegc"] = &handler{handlerFunc: HandleForceGC, description: "force gc"} handlers["/trace"] = &handler{handlerFunc: HandleTrace, description: "dump trace info"} runtime.SetBlockProfileRate(1) if *flags.AutoProfile { go DumpCPUInfo(100) go DumpMemInfo(100) } } if *flags.StatefulSetFlag { handlers["/export/port"] = &handler{handlerFunc: pluginmanager.FindPort, description: "export ilogtail's LISTEN ports"} } if len(handlers) != 0 { handlers["/"] = &handler{handlerFunc: HelpServer, description: "handlers help description"} handlers["/help"] = &handler{handlerFunc: HelpServer, description: "handlers help description"} go func() { var mux *http.ServeMux if *flags.HTTPProfFlag { // bound the handlers exported by `net/http/pprof` mux = http.DefaultServeMux } else { mux = http.NewServeMux() } for path, handler := range handlers { mux.HandleFunc(path, handler.handlerFunc) } logger.Info(context.Background(), "#####################################") logger.Info(context.Background(), "start http server for logtail plugin profile or control") logger.Info(context.Background(), "#####################################") logger.Error(context.Background(), "INIT_HTTP_SERVER_ALARM", "err", http.ListenAndServe(*flags.HTTPAddr, mux)) //nolint }() } }) }