vitess.io/vitess@v0.16.2/go/vt/vtgate/querylogz.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 vtgate 18 19 import ( 20 "fmt" 21 "io" 22 "net/http" 23 "strconv" 24 "strings" 25 "text/template" 26 "time" 27 28 "vitess.io/vitess/go/vt/vtgate/logstats" 29 30 "vitess.io/vitess/go/acl" 31 "vitess.io/vitess/go/vt/log" 32 "vitess.io/vitess/go/vt/logz" 33 "vitess.io/vitess/go/vt/sqlparser" 34 ) 35 36 var ( 37 querylogzHeader = []byte(` 38 <thead> 39 <tr> 40 <th>Method</th> 41 <th>Context</th> 42 <th>Effective Caller</th> 43 <th>Immediate Caller</th> 44 <th>SessionUUID</th> 45 <th>Start</th> 46 <th>End</th> 47 <th>Duration</th> 48 <th>Plan Time</th> 49 <th>Execute Time</th> 50 <th>Commit Time</th> 51 <th>Stmt Type</th> 52 <th>SQL</th> 53 <th>ShardQueries</th> 54 <th>RowsAffected</th> 55 <th>Error</th> 56 </tr> 57 </thead> 58 `) 59 querylogzFuncMap = template.FuncMap{ 60 "stampMicro": func(t time.Time) string { return t.Format(time.StampMicro) }, 61 "cssWrappable": logz.Wrappable, 62 "truncateQuery": sqlparser.TruncateForUI, 63 "unquote": func(s string) string { return strings.Trim(s, "\"") }, 64 } 65 querylogzTmpl = template.Must(template.New("example").Funcs(querylogzFuncMap).Parse(` 66 <tr class="{{.ColorLevel}}"> 67 <td>{{.Method}}</td> 68 <td>{{.ContextHTML}}</td> 69 <td>{{.EffectiveCaller}}</td> 70 <td>{{.ImmediateCaller}}</td> 71 <td>{{.SessionUUID}}</td> 72 <td>{{.StartTime | stampMicro}}</td> 73 <td>{{.EndTime | stampMicro}}</td> 74 <td>{{.TotalTime.Seconds}}</td> 75 <td>{{.PlanTime.Seconds}}</td> 76 <td>{{.ExecuteTime.Seconds}}</td> 77 <td>{{.CommitTime.Seconds}}</td> 78 <td>{{.StmtType}}</td> 79 <td>{{.SQL | truncateQuery | unquote | cssWrappable}}</td> 80 <td>{{.ShardQueries}}</td> 81 <td>{{.RowsAffected}}</td> 82 <td>{{.ErrorStr}}</td> 83 </tr> 84 `)) 85 ) 86 87 // querylogzHandler serves a human readable snapshot of the 88 // current query log. 89 func querylogzHandler(ch chan any, w http.ResponseWriter, r *http.Request) { 90 if err := acl.CheckAccessHTTP(r, acl.DEBUGGING); err != nil { 91 acl.SendError(w, err) 92 return 93 } 94 timeout, limit := parseTimeoutLimitParams(r) 95 logz.StartHTMLTable(w) 96 defer logz.EndHTMLTable(w) 97 w.Write(querylogzHeader) 98 99 tmr := time.NewTimer(timeout) 100 defer tmr.Stop() 101 for i := 0; i < limit; i++ { 102 select { 103 case out := <-ch: 104 select { 105 case <-tmr.C: 106 return 107 default: 108 } 109 stats, ok := out.(*logstats.LogStats) 110 if !ok { 111 err := fmt.Errorf("unexpected value in %s: %#v (expecting value of type %T)", QueryLogger.Name(), out, &logstats.LogStats{}) 112 _, _ = io.WriteString(w, `<tr class="error">`) 113 _, _ = io.WriteString(w, err.Error()) 114 _, _ = io.WriteString(w, "</tr>") 115 log.Error(err) 116 continue 117 } 118 var level string 119 if stats.TotalTime().Seconds() < 0.01 { 120 level = "low" 121 } else if stats.TotalTime().Seconds() < 0.1 { 122 level = "medium" 123 } else { 124 level = "high" 125 } 126 tmplData := struct { 127 *logstats.LogStats 128 ColorLevel string 129 }{stats, level} 130 if err := querylogzTmpl.Execute(w, tmplData); err != nil { 131 log.Errorf("querylogz: couldn't execute template: %v", err) 132 } 133 case <-tmr.C: 134 return 135 } 136 } 137 } 138 139 func parseTimeoutLimitParams(req *http.Request) (time.Duration, int) { 140 timeout := 10 141 limit := 300 142 if ts, ok := req.URL.Query()["timeout"]; ok { 143 if t, err := strconv.Atoi(ts[0]); err == nil { 144 timeout = adjustValue(t, 0, 60) 145 } 146 } 147 if l, ok := req.URL.Query()["limit"]; ok { 148 if lim, err := strconv.Atoi(l[0]); err == nil { 149 limit = adjustValue(lim, 1, 200000) 150 } 151 } 152 return time.Duration(timeout) * time.Second, limit 153 } 154 155 func adjustValue(val int, lower int, upper int) int { 156 if val < lower { 157 return lower 158 } else if val > upper { 159 return upper 160 } 161 return val 162 }