istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/ctrlz/topics/scopes.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package topics 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net/http" 21 "sort" 22 23 "github.com/gorilla/mux" 24 25 "istio.io/istio/pkg/ctrlz/fw" 26 "istio.io/istio/pkg/ctrlz/topics/assets" 27 "istio.io/istio/pkg/log" 28 ) 29 30 type scopeTopic struct{} 31 32 type scopeInfo struct { 33 Name string `json:"name"` 34 Description string `json:"description"` 35 OutputLevel string `json:"output_level"` 36 StackTraceLevel string `json:"stack_trace_level"` 37 LogCallers bool `json:"log_callers"` 38 } 39 40 var levelToString = map[log.Level]string{ 41 log.DebugLevel: "debug", 42 log.InfoLevel: "info", 43 log.WarnLevel: "warn", 44 log.ErrorLevel: "error", 45 log.NoneLevel: "none", 46 } 47 48 var stringToLevel = map[string]log.Level{ 49 "debug": log.DebugLevel, 50 "info": log.InfoLevel, 51 "warn": log.WarnLevel, 52 "error": log.ErrorLevel, 53 "none": log.NoneLevel, 54 } 55 56 // ScopeTopic returns a ControlZ topic that allows visualization of process logging scopes. 57 func ScopeTopic() fw.Topic { 58 return scopeTopic{} 59 } 60 61 func (scopeTopic) Title() string { 62 return "Logging Scopes" 63 } 64 65 func (scopeTopic) Prefix() string { 66 return "scope" 67 } 68 69 func getScopeInfo(s *log.Scope) *scopeInfo { 70 return &scopeInfo{ 71 Name: s.Name(), 72 Description: s.Description(), 73 OutputLevel: levelToString[s.GetOutputLevel()], 74 StackTraceLevel: levelToString[s.GetStackTraceLevel()], 75 LogCallers: s.GetLogCallers(), 76 } 77 } 78 79 func (scopeTopic) Activate(context fw.TopicContext) { 80 tmpl := assets.ParseTemplate(context.Layout(), "templates/scopes.html") 81 82 _ = context.HTMLRouter().NewRoute().HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 83 allScopes := log.Scopes() 84 s := make([]scopeInfo, 0, len(allScopes)) 85 for _, scope := range allScopes { 86 s = append(s, *getScopeInfo(scope)) 87 } 88 sort.Slice(s, func(i, j int) bool { 89 return s[i].Name < s[j].Name 90 }) 91 92 fw.RenderHTML(w, tmpl, s) 93 }) 94 95 _ = context.JSONRouter().StrictSlash(true).NewRoute().Methods("GET").Path("/").HandlerFunc(getAllScopes) 96 _ = context.JSONRouter().NewRoute().Methods("GET").Path("/{scope}").HandlerFunc(getScope) 97 _ = context.JSONRouter().NewRoute().Methods("PUT").Path("/{scope}").HandlerFunc(putScope) 98 } 99 100 func getAllScopes(w http.ResponseWriter, _ *http.Request) { 101 allScopes := log.Scopes() 102 103 scopeInfos := make([]scopeInfo, 0, len(allScopes)) 104 for _, s := range allScopes { 105 scopeInfos = append(scopeInfos, *getScopeInfo(s)) 106 } 107 sort.Slice(scopeInfos, func(i, j int) bool { 108 return scopeInfos[i].Name < scopeInfos[j].Name 109 }) 110 111 fw.RenderJSON(w, http.StatusOK, scopeInfos) 112 } 113 114 func getScope(w http.ResponseWriter, req *http.Request) { 115 vars := mux.Vars(req) 116 name := vars["scope"] 117 118 if s := log.FindScope(name); s != nil { 119 fw.RenderJSON(w, http.StatusOK, getScopeInfo(s)) 120 return 121 } 122 123 fw.RenderError(w, http.StatusBadRequest, fmt.Errorf("unknown scope name: %s", name)) 124 } 125 126 func putScope(w http.ResponseWriter, req *http.Request) { 127 vars := mux.Vars(req) 128 name := vars["scope"] 129 130 var info scopeInfo 131 if err := json.NewDecoder(req.Body).Decode(&info); err != nil { 132 fw.RenderError(w, http.StatusBadRequest, fmt.Errorf("unable to decode request: %v", err)) 133 return 134 } 135 136 if s := log.FindScope(name); s != nil { 137 level, ok := stringToLevel[info.OutputLevel] 138 if ok { 139 s.SetOutputLevel(level) 140 } 141 142 level, ok = stringToLevel[info.StackTraceLevel] 143 if ok { 144 s.SetStackTraceLevel(level) 145 } 146 147 s.SetLogCallers(info.LogCallers) 148 w.WriteHeader(http.StatusAccepted) 149 return 150 } 151 152 fw.RenderError(w, http.StatusBadRequest, fmt.Errorf("unknown scope name: %s", name)) 153 }