vitess.io/vitess@v0.16.2/go/vt/throttler/throttlerlogz.go (about) 1 /* 2 Copyright 2019 The Vitess 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 throttler 18 19 import ( 20 "fmt" 21 "html/template" 22 "io" 23 "net/http" 24 "strings" 25 "time" 26 27 "vitess.io/vitess/go/vt/logz" 28 ) 29 30 const logHeaderHTML = ` 31 <style> 32 table.gridtable th { 33 /* Override the nowrap default to avoid that the table overflows. */ 34 white-space: normal; 35 } 36 </style> 37 <thead> 38 <tr> 39 <th>Now</th> 40 <th>Rate Change</th> 41 <th>Old Rate</th> 42 <th>New Rate</th> 43 <th>Tablet</th> 44 <th>Lag</th> 45 <th>Last Change</th> 46 <th>Actual Rate</th> 47 <th>Good/​Bad?</th> 48 <th>If Skipped</th> 49 <th>Highest Good</th> 50 <th>Lowest Bad</th> 51 <th>Old State</th> 52 <th>Tested State</th> 53 <th>New State</th> 54 <th>Lag Before</th> 55 <th>Recorded Ago</th> 56 <th>Primary Rate</th> 57 <th>Replica Rate</th> 58 <th>Old Backlog</th> 59 <th>New Backlog</th> 60 <th>Reason</th> 61 </tr> 62 </thead> 63 ` 64 65 const logEntryHTML = ` 66 <tr class="{{.ColorLevel}}"> 67 <td>{{.Now.Format "15:04:05"}}</td> 68 <td>{{.RateChange}}</td> 69 <td>{{.OldRate}}</td> 70 <td>{{.NewRate}}</td> 71 <td>{{.Alias}}</td> 72 <td>{{.LagRecordNow.Stats.ReplicationLagSeconds}}s</td> 73 <td>{{.TimeSinceLastRateChange}}</td> 74 <td>{{.CurrentRate}}</td> 75 <td>{{.GoodOrBad}}</td> 76 <td>{{.MemorySkipReason}}</td> 77 <td>{{.HighestGood}}</td> 78 <td>{{.LowestBad}}</td> 79 <td>{{.OldState}}</td> 80 <td>{{.TestedState}}</td> 81 <td>{{.NewState}}</td> 82 <td>{{.LagBefore}}</td> 83 <td>{{.AgeOfBeforeLag}}</td> 84 <td>{{.PrimaryRate}}</td> 85 <td>{{.GuessedReplicationRate}}</td> 86 <td>{{.GuessedReplicationBacklogOld}}</td> 87 <td>{{.GuessedReplicationBacklogNew}}</td> 88 <td>{{.Reason}}</td> 89 </tr> 90 ` 91 92 const logFooterHTML = ` 93 {{.Count}} lag records spanning the last {{.TimeSpan}} minutes are displayed. 94 ` 95 96 var ( 97 logEntryTemplate = template.Must(template.New("logEntry").Parse(logEntryHTML)) 98 logFooterTemplate = template.Must(template.New("logFooter").Parse(logFooterHTML)) 99 ) 100 101 func init() { 102 http.HandleFunc("/throttlerlogz/", func(w http.ResponseWriter, r *http.Request) { 103 throttlerlogzHandler(w, r, GlobalManager) 104 }) 105 } 106 107 func throttlerlogzHandler(w http.ResponseWriter, r *http.Request, m *managerImpl) { 108 // Longest supported URL: /throttlerlogz/<name> 109 parts := strings.SplitN(r.URL.Path, "/", 3) 110 111 if len(parts) != 3 { 112 errMsg := fmt.Sprintf("invalid /throttlerlogz path: %q expected paths: /throttlerlogz/ or /throttlerlogz/<throttler name>", r.URL.Path) 113 http.Error(w, errMsg, http.StatusInternalServerError) 114 return 115 } 116 117 name := parts[2] 118 if name == "" { 119 // If no name is given, redirect to the list of throttlers at /throttlerz. 120 http.Redirect(w, r, "/throttlerz", http.StatusTemporaryRedirect) 121 return 122 } 123 124 showThrottlerLog(w, m, name) 125 } 126 127 func showThrottlerLog(w http.ResponseWriter, m *managerImpl, name string) { 128 results, err := m.log(name) 129 if err != nil { 130 http.Error(w, err.Error(), http.StatusInternalServerError) 131 return 132 } 133 134 logz.StartHTMLTable(w) 135 136 if _, err := io.WriteString(w, logHeaderHTML); err != nil { 137 panic(fmt.Sprintf("failed to execute logHeader template: %v", err)) 138 } 139 for _, r := range results { 140 // Color based on max(tested state, new state). 141 state := r.TestedState 142 if stateGreater(r.NewState, state) { 143 state = r.NewState 144 } 145 var colorLevel string 146 switch state { 147 case stateIncreaseRate: 148 colorLevel = "low" 149 case stateDecreaseAndGuessRate: 150 colorLevel = "medium" 151 case stateEmergency: 152 colorLevel = "high" 153 } 154 data := struct { 155 result 156 ColorLevel string 157 }{r, colorLevel} 158 159 if err := logEntryTemplate.Execute(w, data); err != nil { 160 panic(fmt.Sprintf("failed to execute logEntry template: %v", err)) 161 } 162 } 163 164 logz.EndHTMLTable(w) 165 166 // Print footer. 167 count := len(results) 168 var d time.Duration 169 if count > 0 { 170 d = results[0].Now.Sub(results[count-1].Now) 171 } 172 if err := logFooterTemplate.Execute(w, map[string]any{ 173 "Count": count, 174 "TimeSpan": fmt.Sprintf("%.1f", d.Minutes()), 175 }); err != nil { 176 panic(fmt.Sprintf("failed to execute logFooter template: %v", err)) 177 } 178 }