vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/txlogz.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 tabletserver
    18  
    19  import (
    20  	"fmt"
    21  	"html/template"
    22  	"io"
    23  	"net/http"
    24  	"time"
    25  
    26  	"vitess.io/vitess/go/acl"
    27  	"vitess.io/vitess/go/streamlog"
    28  	"vitess.io/vitess/go/vt/callerid"
    29  	"vitess.io/vitess/go/vt/log"
    30  	"vitess.io/vitess/go/vt/logz"
    31  	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"
    32  
    33  	querypb "vitess.io/vitess/go/vt/proto/query"
    34  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    35  )
    36  
    37  var (
    38  	txlogzHeader = []byte(`
    39  		<thead>
    40  			<tr>
    41  				<th>Transaction id</th>
    42  				<th>Effective caller</th>
    43  				<th>Immediate caller</th>
    44  				<th>Start</th>
    45  				<th>End</th>
    46  				<th>Duration</th>
    47  				<th>Decision</th>
    48  				<th>Statements</th>
    49  			</tr>
    50  		</thead>
    51  	`)
    52  	txlogzFuncMap = template.FuncMap{
    53  		"stampMicro":         func(t time.Time) string { return t.Format(time.StampMicro) },
    54  		"getEffectiveCaller": func(e *vtrpcpb.CallerID) string { return callerid.GetPrincipal(e) },
    55  		"getImmediateCaller": func(i *querypb.VTGateCallerID) string { return callerid.GetUsername(i) },
    56  	}
    57  	txlogzTmpl = template.Must(template.New("example").Funcs(txlogzFuncMap).Parse(`
    58  		<tr class="{{.ColorLevel}}">
    59  			<td>{{.TransactionID}}</td>
    60  			<td>{{.EffectiveCallerID | getEffectiveCaller}}</td>
    61  			<td>{{.ImmediateCallerID | getImmediateCaller}}</td>
    62  			<td>{{.StartTime | stampMicro}}</td>
    63  			<td>{{.EndTime | stampMicro}}</td>
    64  			<td>{{.Duration}}</td>
    65  			<td>{{.Conclusion}}</td>
    66  			<td>
    67  				{{ range .Queries }}
    68  					{{.}}<br>
    69  				{{ end}}
    70  			</td>
    71  		</tr>`))
    72  )
    73  
    74  func init() {
    75  	http.HandleFunc("/txlogz", txlogzHandler)
    76  }
    77  
    78  // txlogzHandler serves a human readable snapshot of the
    79  // current transaction log.
    80  // Endpoint: /txlogz?timeout=%d&limit=%d
    81  // timeout: the txlogz will keep dumping transactions until timeout
    82  // limit: txlogz will keep dumping transactions until it hits the limit
    83  func txlogzHandler(w http.ResponseWriter, req *http.Request) {
    84  	if err := acl.CheckAccessHTTP(req, acl.DEBUGGING); err != nil {
    85  		acl.SendError(w, err)
    86  		return
    87  	}
    88  
    89  	if streamlog.GetRedactDebugUIQueries() {
    90  		io.WriteString(w, `
    91  <!DOCTYPE html>
    92  <html>
    93  <body>
    94  <h1>Redacted</h1>
    95  <p>/txlogz has been redacted for your protection</p>
    96  </body>
    97  </html>
    98  `)
    99  		return
   100  	}
   101  
   102  	timeout, limit := parseTimeoutLimitParams(req)
   103  	ch := tabletenv.TxLogger.Subscribe("txlogz")
   104  	defer tabletenv.TxLogger.Unsubscribe(ch)
   105  	logz.StartHTMLTable(w)
   106  	defer logz.EndHTMLTable(w)
   107  	w.Write(txlogzHeader)
   108  
   109  	tmr := time.NewTimer(timeout)
   110  	defer tmr.Stop()
   111  	for i := 0; i < limit; i++ {
   112  		select {
   113  		case out := <-ch:
   114  			txc, ok := out.(*StatefulConnection)
   115  			if !ok {
   116  				err := fmt.Errorf("unexpected value in %s: %#v (expecting value of type %T)", tabletenv.TxLogger.Name(), out, &StatefulConnection{})
   117  				io.WriteString(w, `<tr class="error">`)
   118  				io.WriteString(w, err.Error())
   119  				io.WriteString(w, "</tr>")
   120  				log.Error(err)
   121  				continue
   122  			}
   123  			// not all StatefulConnections contain transactions
   124  			if txc.txProps != nil {
   125  				writeTransactionData(w, txc)
   126  			}
   127  		case <-tmr.C:
   128  			return
   129  		}
   130  	}
   131  }
   132  
   133  func writeTransactionData(w http.ResponseWriter, txc *StatefulConnection) {
   134  	props := txc.txProps
   135  	var level string
   136  	duration := props.EndTime.Sub(props.StartTime).Seconds()
   137  	if duration < 0.1 {
   138  		level = "low"
   139  	} else if duration < 1.0 {
   140  		level = "medium"
   141  	} else {
   142  		level = "high"
   143  	}
   144  	tmplData := struct {
   145  		*StatefulConnection
   146  		Duration   float64
   147  		ColorLevel string
   148  	}{txc, duration, level}
   149  	if err := txlogzTmpl.Execute(w, tmplData); err != nil {
   150  		log.Errorf("txlogz: couldn't execute template: %v", err)
   151  	}
   152  }