github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/trace/events.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package trace
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"html/template"
    11  	"io"
    12  	"log"
    13  	"net/http"
    14  	"runtime"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"sync/atomic"
    20  	"text/tabwriter"
    21  	"time"
    22  )
    23  
    24  const maxEventsPerLog = 100
    25  
    26  type bucket struct {
    27  	MaxErrAge time.Duration
    28  	String    string
    29  }
    30  
    31  var buckets = []bucket{
    32  	{0, "total"},
    33  	{10 * time.Second, "errs<10s"},
    34  	{1 * time.Minute, "errs<1m"},
    35  	{10 * time.Minute, "errs<10m"},
    36  	{1 * time.Hour, "errs<1h"},
    37  	{10 * time.Hour, "errs<10h"},
    38  	{24000 * time.Hour, "errors"},
    39  }
    40  
    41  // RenderEvents renders the HTML page typically served at /debug/events.
    42  // It does not do any auth checking. The request may be nil.
    43  //
    44  // Most users will use the Events handler.
    45  func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
    46  	now := time.Now()
    47  	data := &struct {
    48  		Families []string // family names
    49  		Buckets  []bucket
    50  		Counts   [][]int // eventLog count per family/bucket
    51  
    52  		// Set when a bucket has been selected.
    53  		Family    string
    54  		Bucket    int
    55  		EventLogs eventLogs
    56  		Expanded  bool
    57  	}{
    58  		Buckets: buckets,
    59  	}
    60  
    61  	data.Families = make([]string, 0, len(families))
    62  	famMu.RLock()
    63  	for name := range families {
    64  		data.Families = append(data.Families, name)
    65  	}
    66  	famMu.RUnlock()
    67  	sort.Strings(data.Families)
    68  
    69  	// Count the number of eventLogs in each family for each error age.
    70  	data.Counts = make([][]int, len(data.Families))
    71  	for i, name := range data.Families {
    72  		// TODO(sameer): move this loop under the family lock.
    73  		f := getEventFamily(name)
    74  		data.Counts[i] = make([]int, len(data.Buckets))
    75  		for j, b := range data.Buckets {
    76  			data.Counts[i][j] = f.Count(now, b.MaxErrAge)
    77  		}
    78  	}
    79  
    80  	if req != nil {
    81  		var ok bool
    82  		data.Family, data.Bucket, ok = parseEventsArgs(req)
    83  		if !ok {
    84  			// No-op
    85  		} else {
    86  			data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
    87  		}
    88  		if data.EventLogs != nil {
    89  			defer data.EventLogs.Free()
    90  			sort.Sort(data.EventLogs)
    91  		}
    92  		if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
    93  			data.Expanded = exp
    94  		}
    95  	}
    96  
    97  	famMu.RLock()
    98  	defer famMu.RUnlock()
    99  	if err := eventsTmpl().Execute(w, data); err != nil {
   100  		log.Printf("net/trace: Failed executing template: %v", err)
   101  	}
   102  }
   103  
   104  func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
   105  	fam, bStr := req.FormValue("fam"), req.FormValue("b")
   106  	if fam == "" || bStr == "" {
   107  		return "", 0, false
   108  	}
   109  	b, err := strconv.Atoi(bStr)
   110  	if err != nil || b < 0 || b >= len(buckets) {
   111  		return "", 0, false
   112  	}
   113  	return fam, b, true
   114  }
   115  
   116  // An EventLog provides a log of events associated with a specific object.
   117  type EventLog interface {
   118  	// Printf formats its arguments with fmt.Sprintf and adds the
   119  	// result to the event log.
   120  	Printf(format string, a ...interface{})
   121  
   122  	// Errorf is like Printf, but it marks this event as an error.
   123  	Errorf(format string, a ...interface{})
   124  
   125  	// Finish declares that this event log is complete.
   126  	// The event log should not be used after calling this method.
   127  	Finish()
   128  }
   129  
   130  // NewEventLog returns a new EventLog with the specified family name
   131  // and title.
   132  func NewEventLog(family, title string) EventLog {
   133  	el := newEventLog()
   134  	el.ref()
   135  	el.Family, el.Title = family, title
   136  	el.Start = time.Now()
   137  	el.events = make([]logEntry, 0, maxEventsPerLog)
   138  	el.stack = make([]uintptr, 32)
   139  	n := runtime.Callers(2, el.stack)
   140  	el.stack = el.stack[:n]
   141  
   142  	getEventFamily(family).add(el)
   143  	return el
   144  }
   145  
   146  func (el *eventLog) Finish() {
   147  	getEventFamily(el.Family).remove(el)
   148  	el.unref() // matches ref in New
   149  }
   150  
   151  var (
   152  	famMu    sync.RWMutex
   153  	families = make(map[string]*eventFamily) // family name => family
   154  )
   155  
   156  func getEventFamily(fam string) *eventFamily {
   157  	famMu.Lock()
   158  	defer famMu.Unlock()
   159  	f := families[fam]
   160  	if f == nil {
   161  		f = &eventFamily{}
   162  		families[fam] = f
   163  	}
   164  	return f
   165  }
   166  
   167  type eventFamily struct {
   168  	mu        sync.RWMutex
   169  	eventLogs eventLogs
   170  }
   171  
   172  func (f *eventFamily) add(el *eventLog) {
   173  	f.mu.Lock()
   174  	f.eventLogs = append(f.eventLogs, el)
   175  	f.mu.Unlock()
   176  }
   177  
   178  func (f *eventFamily) remove(el *eventLog) {
   179  	f.mu.Lock()
   180  	defer f.mu.Unlock()
   181  	for i, el0 := range f.eventLogs {
   182  		if el == el0 {
   183  			copy(f.eventLogs[i:], f.eventLogs[i+1:])
   184  			f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
   185  			return
   186  		}
   187  	}
   188  }
   189  
   190  func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
   191  	f.mu.RLock()
   192  	defer f.mu.RUnlock()
   193  	for _, el := range f.eventLogs {
   194  		if el.hasRecentError(now, maxErrAge) {
   195  			n++
   196  		}
   197  	}
   198  	return
   199  }
   200  
   201  func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
   202  	f.mu.RLock()
   203  	defer f.mu.RUnlock()
   204  	els = make(eventLogs, 0, len(f.eventLogs))
   205  	for _, el := range f.eventLogs {
   206  		if el.hasRecentError(now, maxErrAge) {
   207  			el.ref()
   208  			els = append(els, el)
   209  		}
   210  	}
   211  	return
   212  }
   213  
   214  type eventLogs []*eventLog
   215  
   216  // Free calls unref on each element of the list.
   217  func (els eventLogs) Free() {
   218  	for _, el := range els {
   219  		el.unref()
   220  	}
   221  }
   222  
   223  // eventLogs may be sorted in reverse chronological order.
   224  func (els eventLogs) Len() int           { return len(els) }
   225  func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
   226  func (els eventLogs) Swap(i, j int)      { els[i], els[j] = els[j], els[i] }
   227  
   228  // A logEntry is a timestamped log entry in an event log.
   229  type logEntry struct {
   230  	When    time.Time
   231  	Elapsed time.Duration // since previous event in log
   232  	NewDay  bool          // whether this event is on a different day to the previous event
   233  	What    string
   234  	IsErr   bool
   235  }
   236  
   237  // WhenString returns a string representation of the elapsed time of the event.
   238  // It will include the date if midnight was crossed.
   239  func (e logEntry) WhenString() string {
   240  	if e.NewDay {
   241  		return e.When.Format("2006/01/02 15:04:05.000000")
   242  	}
   243  	return e.When.Format("15:04:05.000000")
   244  }
   245  
   246  // An eventLog represents an active event log.
   247  type eventLog struct {
   248  	// Family is the top-level grouping of event logs to which this belongs.
   249  	Family string
   250  
   251  	// Title is the title of this event log.
   252  	Title string
   253  
   254  	// Timing information.
   255  	Start time.Time
   256  
   257  	// Call stack where this event log was created.
   258  	stack []uintptr
   259  
   260  	// Append-only sequence of events.
   261  	//
   262  	// TODO(sameer): change this to a ring buffer to avoid the array copy
   263  	// when we hit maxEventsPerLog.
   264  	mu            sync.RWMutex
   265  	events        []logEntry
   266  	LastErrorTime time.Time
   267  	discarded     int
   268  
   269  	refs int32 // how many buckets this is in
   270  }
   271  
   272  func (el *eventLog) reset() {
   273  	// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
   274  	el.Family = ""
   275  	el.Title = ""
   276  	el.Start = time.Time{}
   277  	el.stack = nil
   278  	el.events = nil
   279  	el.LastErrorTime = time.Time{}
   280  	el.discarded = 0
   281  	el.refs = 0
   282  }
   283  
   284  func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
   285  	if maxErrAge == 0 {
   286  		return true
   287  	}
   288  	el.mu.RLock()
   289  	defer el.mu.RUnlock()
   290  	return now.Sub(el.LastErrorTime) < maxErrAge
   291  }
   292  
   293  // delta returns the elapsed time since the last event or the log start,
   294  // and whether it spans midnight.
   295  // L >= el.mu
   296  func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
   297  	if len(el.events) == 0 {
   298  		return t.Sub(el.Start), false
   299  	}
   300  	prev := el.events[len(el.events)-1].When
   301  	return t.Sub(prev), prev.Day() != t.Day()
   302  
   303  }
   304  
   305  func (el *eventLog) Printf(format string, a ...interface{}) {
   306  	el.printf(false, format, a...)
   307  }
   308  
   309  func (el *eventLog) Errorf(format string, a ...interface{}) {
   310  	el.printf(true, format, a...)
   311  }
   312  
   313  func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
   314  	e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
   315  	el.mu.Lock()
   316  	e.Elapsed, e.NewDay = el.delta(e.When)
   317  	if len(el.events) < maxEventsPerLog {
   318  		el.events = append(el.events, e)
   319  	} else {
   320  		// Discard the oldest event.
   321  		if el.discarded == 0 {
   322  			// el.discarded starts at two to count for the event it
   323  			// is replacing, plus the next one that we are about to
   324  			// drop.
   325  			el.discarded = 2
   326  		} else {
   327  			el.discarded++
   328  		}
   329  		// TODO(sameer): if this causes allocations on a critical path,
   330  		// change eventLog.What to be a fmt.Stringer, as in trace.go.
   331  		el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
   332  		// The timestamp of the discarded meta-event should be
   333  		// the time of the last event it is representing.
   334  		el.events[0].When = el.events[1].When
   335  		copy(el.events[1:], el.events[2:])
   336  		el.events[maxEventsPerLog-1] = e
   337  	}
   338  	if e.IsErr {
   339  		el.LastErrorTime = e.When
   340  	}
   341  	el.mu.Unlock()
   342  }
   343  
   344  func (el *eventLog) ref() {
   345  	atomic.AddInt32(&el.refs, 1)
   346  }
   347  
   348  func (el *eventLog) unref() {
   349  	if atomic.AddInt32(&el.refs, -1) == 0 {
   350  		freeEventLog(el)
   351  	}
   352  }
   353  
   354  func (el *eventLog) When() string {
   355  	return el.Start.Format("2006/01/02 15:04:05.000000")
   356  }
   357  
   358  func (el *eventLog) ElapsedTime() string {
   359  	elapsed := time.Since(el.Start)
   360  	return fmt.Sprintf("%.6f", elapsed.Seconds())
   361  }
   362  
   363  func (el *eventLog) Stack() string {
   364  	buf := new(bytes.Buffer)
   365  	tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
   366  	printStackRecord(tw, el.stack)
   367  	tw.Flush()
   368  	return buf.String()
   369  }
   370  
   371  // printStackRecord prints the function + source line information
   372  // for a single stack trace.
   373  // Adapted from runtime/pprof/pprof.go.
   374  func printStackRecord(w io.Writer, stk []uintptr) {
   375  	for _, pc := range stk {
   376  		f := runtime.FuncForPC(pc)
   377  		if f == nil {
   378  			continue
   379  		}
   380  		file, line := f.FileLine(pc)
   381  		name := f.Name()
   382  		// Hide runtime.goexit and any runtime functions at the beginning.
   383  		if strings.HasPrefix(name, "runtime.") {
   384  			continue
   385  		}
   386  		fmt.Fprintf(w, "#   %s\t%s:%d\n", name, file, line)
   387  	}
   388  }
   389  
   390  func (el *eventLog) Events() []logEntry {
   391  	el.mu.RLock()
   392  	defer el.mu.RUnlock()
   393  	return el.events
   394  }
   395  
   396  // freeEventLogs is a freelist of *eventLog
   397  var freeEventLogs = make(chan *eventLog, 1000)
   398  
   399  // newEventLog returns a event log ready to use.
   400  func newEventLog() *eventLog {
   401  	select {
   402  	case el := <-freeEventLogs:
   403  		return el
   404  	default:
   405  		return new(eventLog)
   406  	}
   407  }
   408  
   409  // freeEventLog adds el to freeEventLogs if there's room.
   410  // This is non-blocking.
   411  func freeEventLog(el *eventLog) {
   412  	el.reset()
   413  	select {
   414  	case freeEventLogs <- el:
   415  	default:
   416  	}
   417  }
   418  
   419  var eventsTmplCache *template.Template
   420  var eventsTmplOnce sync.Once
   421  
   422  func eventsTmpl() *template.Template {
   423  	eventsTmplOnce.Do(func() {
   424  		eventsTmplCache = template.Must(template.New("events").Funcs(template.FuncMap{
   425  			"elapsed":   elapsed,
   426  			"trimSpace": strings.TrimSpace,
   427  		}).Parse(eventsHTML))
   428  	})
   429  	return eventsTmplCache
   430  }
   431  
   432  const eventsHTML = `
   433  <html>
   434  	<head>
   435  		<title>events</title>
   436  	</head>
   437  	<style type="text/css">
   438  		body {
   439  			font-family: sans-serif;
   440  		}
   441  		table#req-status td.family {
   442  			padding-right: 2em;
   443  		}
   444  		table#req-status td.active {
   445  			padding-right: 1em;
   446  		}
   447  		table#req-status td.empty {
   448  			color: #aaa;
   449  		}
   450  		table#reqs {
   451  			margin-top: 1em;
   452  		}
   453  		table#reqs tr.first {
   454  			{{if $.Expanded}}font-weight: bold;{{end}}
   455  		}
   456  		table#reqs td {
   457  			font-family: monospace;
   458  		}
   459  		table#reqs td.when {
   460  			text-align: right;
   461  			white-space: nowrap;
   462  		}
   463  		table#reqs td.elapsed {
   464  			padding: 0 0.5em;
   465  			text-align: right;
   466  			white-space: pre;
   467  			width: 10em;
   468  		}
   469  		address {
   470  			font-size: smaller;
   471  			margin-top: 5em;
   472  		}
   473  	</style>
   474  	<body>
   475  
   476  <h1>/debug/events</h1>
   477  
   478  <table id="req-status">
   479  	{{range $i, $fam := .Families}}
   480  	<tr>
   481  		<td class="family">{{$fam}}</td>
   482  
   483  	        {{range $j, $bucket := $.Buckets}}
   484  	        {{$n := index $.Counts $i $j}}
   485  		<td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
   486  	                {{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
   487  		        [{{$n}} {{$bucket.String}}]
   488  			{{if $n}}</a>{{end}}
   489  		</td>
   490                  {{end}}
   491  
   492  	</tr>{{end}}
   493  </table>
   494  
   495  {{if $.EventLogs}}
   496  <hr />
   497  <h3>Family: {{$.Family}}</h3>
   498  
   499  {{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
   500  [Summary]{{if $.Expanded}}</a>{{end}}
   501  
   502  {{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
   503  [Expanded]{{if not $.Expanded}}</a>{{end}}
   504  
   505  <table id="reqs">
   506  	<tr><th>When</th><th>Elapsed</th></tr>
   507  	{{range $el := $.EventLogs}}
   508  	<tr class="first">
   509  		<td class="when">{{$el.When}}</td>
   510  		<td class="elapsed">{{$el.ElapsedTime}}</td>
   511  		<td>{{$el.Title}}
   512  	</tr>
   513  	{{if $.Expanded}}
   514  	<tr>
   515  		<td class="when"></td>
   516  		<td class="elapsed"></td>
   517  		<td><pre>{{$el.Stack|trimSpace}}</pre></td>
   518  	</tr>
   519  	{{range $el.Events}}
   520  	<tr>
   521  		<td class="when">{{.WhenString}}</td>
   522  		<td class="elapsed">{{elapsed .Elapsed}}</td>
   523  		<td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
   524  	</tr>
   525  	{{end}}
   526  	{{end}}
   527  	{{end}}
   528  </table>
   529  {{end}}
   530  	</body>
   531  </html>
   532  `