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 /* 6 Package trace implements tracing of requests and long-lived objects. 7 It exports HTTP interfaces on /debug/requests and /debug/events. 8 9 A trace.Trace provides tracing for short-lived objects, usually requests. 10 A request handler might be implemented like this: 11 12 func fooHandler(w http.ResponseWriter, req *http.Request) { 13 tr := trace.New("mypkg.Foo", req.URL.Path) 14 defer tr.Finish() 15 ... 16 tr.LazyPrintf("some event %q happened", str) 17 ... 18 if err := somethingImportant(); err != nil { 19 tr.LazyPrintf("somethingImportant failed: %v", err) 20 tr.SetError() 21 } 22 } 23 24 The /debug/requests HTTP endpoint organizes the traces by family, 25 errors, and duration. It also provides histogram of request duration 26 for each family. 27 28 A trace.EventLog provides tracing for long-lived objects, such as RPC 29 connections. 30 31 // A Fetcher fetches URL paths for a single domain. 32 type Fetcher struct { 33 domain string 34 events trace.EventLog 35 } 36 37 func NewFetcher(domain string) *Fetcher { 38 return &Fetcher{ 39 domain, 40 trace.NewEventLog("mypkg.Fetcher", domain), 41 } 42 } 43 44 func (f *Fetcher) Fetch(path string) (string, error) { 45 resp, err := http.Get("http://" + f.domain + "/" + path) 46 if err != nil { 47 f.events.Errorf("Get(%q) = %v", path, err) 48 return "", err 49 } 50 f.events.Printf("Get(%q) = %s", path, resp.Status) 51 ... 52 } 53 54 func (f *Fetcher) Close() error { 55 f.events.Finish() 56 return nil 57 } 58 59 The /debug/events HTTP endpoint organizes the event logs by family and 60 by time since the last error. The expanded view displays recent log 61 entries and the log's call stack. 62 */ 63 package trace 64 65 import ( 66 "bytes" 67 "fmt" 68 "html/template" 69 "io" 70 "log" 71 "net" 72 "net/http" 73 "runtime" 74 "sort" 75 "strconv" 76 "sync" 77 "sync/atomic" 78 "time" 79 80 "golang.org/x/net/internal/timeseries" 81 ) 82 83 // DebugUseAfterFinish controls whether to debug uses of Trace values after finishing. 84 // FOR DEBUGGING ONLY. This will slow down the program. 85 var DebugUseAfterFinish = false 86 87 // AuthRequest determines whether a specific request is permitted to load the 88 // /debug/requests or /debug/events pages. 89 // 90 // It returns two bools; the first indicates whether the page may be viewed at all, 91 // and the second indicates whether sensitive events will be shown. 92 // 93 // AuthRequest may be replaced by a program to customize its authorization requirements. 94 // 95 // The default AuthRequest function returns (true, true) if and only if the request 96 // comes from localhost/127.0.0.1/[::1]. 97 var AuthRequest = func(req *http.Request) (any, sensitive bool) { 98 // RemoteAddr is commonly in the form "IP" or "IP:port". 99 // If it is in the form "IP:port", split off the port. 100 host, _, err := net.SplitHostPort(req.RemoteAddr) 101 if err != nil { 102 host = req.RemoteAddr 103 } 104 switch host { 105 case "localhost", "127.0.0.1", "::1": 106 return true, true 107 default: 108 return false, false 109 } 110 } 111 112 func init() { 113 // TODO(jbd): Serve Traces from /debug/traces in the future? 114 // There is no requirement for a request to be present to have traces. 115 http.HandleFunc("/debug/requests", Traces) 116 http.HandleFunc("/debug/events", Events) 117 } 118 119 // Traces responds with traces from the program. 120 // The package initialization registers it in http.DefaultServeMux 121 // at /debug/requests. 122 // 123 // It performs authorization by running AuthRequest. 124 func Traces(w http.ResponseWriter, req *http.Request) { 125 any, sensitive := AuthRequest(req) 126 if !any { 127 http.Error(w, "not allowed", http.StatusUnauthorized) 128 return 129 } 130 w.Header().Set("Content-Type", "text/html; charset=utf-8") 131 Render(w, req, sensitive) 132 } 133 134 // Events responds with a page of events collected by EventLogs. 135 // The package initialization registers it in http.DefaultServeMux 136 // at /debug/events. 137 // 138 // It performs authorization by running AuthRequest. 139 func Events(w http.ResponseWriter, req *http.Request) { 140 any, sensitive := AuthRequest(req) 141 if !any { 142 http.Error(w, "not allowed", http.StatusUnauthorized) 143 return 144 } 145 w.Header().Set("Content-Type", "text/html; charset=utf-8") 146 RenderEvents(w, req, sensitive) 147 } 148 149 // Render renders the HTML page typically served at /debug/requests. 150 // It does not do any auth checking. The request may be nil. 151 // 152 // Most users will use the Traces handler. 153 func Render(w io.Writer, req *http.Request, sensitive bool) { 154 data := &struct { 155 Families []string 156 ActiveTraceCount map[string]int 157 CompletedTraces map[string]*family 158 159 // Set when a bucket has been selected. 160 Traces traceList 161 Family string 162 Bucket int 163 Expanded bool 164 Traced bool 165 Active bool 166 ShowSensitive bool // whether to show sensitive events 167 168 Histogram template.HTML 169 HistogramWindow string // e.g. "last minute", "last hour", "all time" 170 171 // If non-zero, the set of traces is a partial set, 172 // and this is the total number. 173 Total int 174 }{ 175 CompletedTraces: completedTraces, 176 } 177 178 data.ShowSensitive = sensitive 179 if req != nil { 180 // Allow show_sensitive=0 to force hiding of sensitive data for testing. 181 // This only goes one way; you can't use show_sensitive=1 to see things. 182 if req.FormValue("show_sensitive") == "0" { 183 data.ShowSensitive = false 184 } 185 186 if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil { 187 data.Expanded = exp 188 } 189 if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil { 190 data.Traced = exp 191 } 192 } 193 194 completedMu.RLock() 195 data.Families = make([]string, 0, len(completedTraces)) 196 for fam := range completedTraces { 197 data.Families = append(data.Families, fam) 198 } 199 completedMu.RUnlock() 200 sort.Strings(data.Families) 201 202 // We are careful here to minimize the time spent locking activeMu, 203 // since that lock is required every time an RPC starts and finishes. 204 data.ActiveTraceCount = make(map[string]int, len(data.Families)) 205 activeMu.RLock() 206 for fam, s := range activeTraces { 207 data.ActiveTraceCount[fam] = s.Len() 208 } 209 activeMu.RUnlock() 210 211 var ok bool 212 data.Family, data.Bucket, ok = parseArgs(req) 213 switch { 214 case !ok: 215 // No-op 216 case data.Bucket == -1: 217 data.Active = true 218 n := data.ActiveTraceCount[data.Family] 219 data.Traces = getActiveTraces(data.Family) 220 if len(data.Traces) < n { 221 data.Total = n 222 } 223 case data.Bucket < bucketsPerFamily: 224 if b := lookupBucket(data.Family, data.Bucket); b != nil { 225 data.Traces = b.Copy(data.Traced) 226 } 227 default: 228 if f := getFamily(data.Family, false); f != nil { 229 var obs timeseries.Observable 230 f.LatencyMu.RLock() 231 switch o := data.Bucket - bucketsPerFamily; o { 232 case 0: 233 obs = f.Latency.Minute() 234 data.HistogramWindow = "last minute" 235 case 1: 236 obs = f.Latency.Hour() 237 data.HistogramWindow = "last hour" 238 case 2: 239 obs = f.Latency.Total() 240 data.HistogramWindow = "all time" 241 } 242 f.LatencyMu.RUnlock() 243 if obs != nil { 244 data.Histogram = obs.(*histogram).html() 245 } 246 } 247 } 248 249 if data.Traces != nil { 250 defer data.Traces.Free() 251 sort.Sort(data.Traces) 252 } 253 254 completedMu.RLock() 255 defer completedMu.RUnlock() 256 if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil { 257 log.Printf("net/trace: Failed executing template: %v", err) 258 } 259 } 260 261 func parseArgs(req *http.Request) (fam string, b int, ok bool) { 262 if req == nil { 263 return "", 0, false 264 } 265 fam, bStr := req.FormValue("fam"), req.FormValue("b") 266 if fam == "" || bStr == "" { 267 return "", 0, false 268 } 269 b, err := strconv.Atoi(bStr) 270 if err != nil || b < -1 { 271 return "", 0, false 272 } 273 274 return fam, b, true 275 } 276 277 func lookupBucket(fam string, b int) *traceBucket { 278 f := getFamily(fam, false) 279 if f == nil || b < 0 || b >= len(f.Buckets) { 280 return nil 281 } 282 return f.Buckets[b] 283 } 284 285 type contextKeyT string 286 287 var contextKey = contextKeyT("golang.org/x/net/trace.Trace") 288 289 // Trace represents an active request. 290 type Trace interface { 291 // LazyLog adds x to the event log. It will be evaluated each time the 292 // /debug/requests page is rendered. Any memory referenced by x will be 293 // pinned until the trace is finished and later discarded. 294 LazyLog(x fmt.Stringer, sensitive bool) 295 296 // LazyPrintf evaluates its arguments with fmt.Sprintf each time the 297 // /debug/requests page is rendered. Any memory referenced by a will be 298 // pinned until the trace is finished and later discarded. 299 LazyPrintf(format string, a ...interface{}) 300 301 // SetError declares that this trace resulted in an error. 302 SetError() 303 304 // SetRecycler sets a recycler for the trace. 305 // f will be called for each event passed to LazyLog at a time when 306 // it is no longer required, whether while the trace is still active 307 // and the event is discarded, or when a completed trace is discarded. 308 SetRecycler(f func(interface{})) 309 310 // SetTraceInfo sets the trace info for the trace. 311 // This is currently unused. 312 SetTraceInfo(traceID, spanID uint64) 313 314 // SetMaxEvents sets the maximum number of events that will be stored 315 // in the trace. This has no effect if any events have already been 316 // added to the trace. 317 SetMaxEvents(m int) 318 319 // Finish declares that this trace is complete. 320 // The trace should not be used after calling this method. 321 Finish() 322 } 323 324 type lazySprintf struct { 325 format string 326 a []interface{} 327 } 328 329 func (l *lazySprintf) String() string { 330 return fmt.Sprintf(l.format, l.a...) 331 } 332 333 // New returns a new Trace with the specified family and title. 334 func New(family, title string) Trace { 335 tr := newTrace() 336 tr.ref() 337 tr.Family, tr.Title = family, title 338 tr.Start = time.Now() 339 tr.maxEvents = maxEventsPerTrace 340 tr.events = tr.eventsBuf[:0] 341 342 activeMu.RLock() 343 s := activeTraces[tr.Family] 344 activeMu.RUnlock() 345 if s == nil { 346 activeMu.Lock() 347 s = activeTraces[tr.Family] // check again 348 if s == nil { 349 s = new(traceSet) 350 activeTraces[tr.Family] = s 351 } 352 activeMu.Unlock() 353 } 354 s.Add(tr) 355 356 // Trigger allocation of the completed trace structure for this family. 357 // This will cause the family to be present in the request page during 358 // the first trace of this family. We don't care about the return value, 359 // nor is there any need for this to run inline, so we execute it in its 360 // own goroutine, but only if the family isn't allocated yet. 361 completedMu.RLock() 362 if _, ok := completedTraces[tr.Family]; !ok { 363 go allocFamily(tr.Family) 364 } 365 completedMu.RUnlock() 366 367 return tr 368 } 369 370 func (tr *trace) Finish() { 371 elapsed := time.Now().Sub(tr.Start) 372 tr.mu.Lock() 373 tr.Elapsed = elapsed 374 tr.mu.Unlock() 375 376 if DebugUseAfterFinish { 377 buf := make([]byte, 4<<10) // 4 KB should be enough 378 n := runtime.Stack(buf, false) 379 tr.finishStack = buf[:n] 380 } 381 382 activeMu.RLock() 383 m := activeTraces[tr.Family] 384 activeMu.RUnlock() 385 m.Remove(tr) 386 387 f := getFamily(tr.Family, true) 388 tr.mu.RLock() // protects tr fields in Cond.match calls 389 for _, b := range f.Buckets { 390 if b.Cond.match(tr) { 391 b.Add(tr) 392 } 393 } 394 tr.mu.RUnlock() 395 396 // Add a sample of elapsed time as microseconds to the family's timeseries 397 h := new(histogram) 398 h.addMeasurement(elapsed.Nanoseconds() / 1e3) 399 f.LatencyMu.Lock() 400 f.Latency.Add(h) 401 f.LatencyMu.Unlock() 402 403 tr.unref() // matches ref in New 404 } 405 406 const ( 407 bucketsPerFamily = 9 408 tracesPerBucket = 10 409 maxActiveTraces = 20 // Maximum number of active traces to show. 410 maxEventsPerTrace = 10 411 numHistogramBuckets = 38 412 ) 413 414 var ( 415 // The active traces. 416 activeMu sync.RWMutex 417 activeTraces = make(map[string]*traceSet) // family -> traces 418 419 // Families of completed traces. 420 completedMu sync.RWMutex 421 completedTraces = make(map[string]*family) // family -> traces 422 ) 423 424 type traceSet struct { 425 mu sync.RWMutex 426 m map[*trace]bool 427 428 // We could avoid the entire map scan in FirstN by having a slice of all the traces 429 // ordered by start time, and an index into that from the trace struct, with a periodic 430 // repack of the slice after enough traces finish; we could also use a skip list or similar. 431 // However, that would shift some of the expense from /debug/requests time to RPC time, 432 // which is probably the wrong trade-off. 433 } 434 435 func (ts *traceSet) Len() int { 436 ts.mu.RLock() 437 defer ts.mu.RUnlock() 438 return len(ts.m) 439 } 440 441 func (ts *traceSet) Add(tr *trace) { 442 ts.mu.Lock() 443 if ts.m == nil { 444 ts.m = make(map[*trace]bool) 445 } 446 ts.m[tr] = true 447 ts.mu.Unlock() 448 } 449 450 func (ts *traceSet) Remove(tr *trace) { 451 ts.mu.Lock() 452 delete(ts.m, tr) 453 ts.mu.Unlock() 454 } 455 456 // FirstN returns the first n traces ordered by time. 457 func (ts *traceSet) FirstN(n int) traceList { 458 ts.mu.RLock() 459 defer ts.mu.RUnlock() 460 461 if n > len(ts.m) { 462 n = len(ts.m) 463 } 464 trl := make(traceList, 0, n) 465 466 // Fast path for when no selectivity is needed. 467 if n == len(ts.m) { 468 for tr := range ts.m { 469 tr.ref() 470 trl = append(trl, tr) 471 } 472 sort.Sort(trl) 473 return trl 474 } 475 476 // Pick the oldest n traces. 477 // This is inefficient. See the comment in the traceSet struct. 478 for tr := range ts.m { 479 // Put the first n traces into trl in the order they occur. 480 // When we have n, sort trl, and thereafter maintain its order. 481 if len(trl) < n { 482 tr.ref() 483 trl = append(trl, tr) 484 if len(trl) == n { 485 // This is guaranteed to happen exactly once during this loop. 486 sort.Sort(trl) 487 } 488 continue 489 } 490 if tr.Start.After(trl[n-1].Start) { 491 continue 492 } 493 494 // Find where to insert this one. 495 tr.ref() 496 i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) }) 497 trl[n-1].unref() 498 copy(trl[i+1:], trl[i:]) 499 trl[i] = tr 500 } 501 502 return trl 503 } 504 505 func getActiveTraces(fam string) traceList { 506 activeMu.RLock() 507 s := activeTraces[fam] 508 activeMu.RUnlock() 509 if s == nil { 510 return nil 511 } 512 return s.FirstN(maxActiveTraces) 513 } 514 515 func getFamily(fam string, allocNew bool) *family { 516 completedMu.RLock() 517 f := completedTraces[fam] 518 completedMu.RUnlock() 519 if f == nil && allocNew { 520 f = allocFamily(fam) 521 } 522 return f 523 } 524 525 func allocFamily(fam string) *family { 526 completedMu.Lock() 527 defer completedMu.Unlock() 528 f := completedTraces[fam] 529 if f == nil { 530 f = newFamily() 531 completedTraces[fam] = f 532 } 533 return f 534 } 535 536 // family represents a set of trace buckets and associated latency information. 537 type family struct { 538 // traces may occur in multiple buckets. 539 Buckets [bucketsPerFamily]*traceBucket 540 541 // latency time series 542 LatencyMu sync.RWMutex 543 Latency *timeseries.MinuteHourSeries 544 } 545 546 func newFamily() *family { 547 return &family{ 548 Buckets: [bucketsPerFamily]*traceBucket{ 549 {Cond: minCond(0)}, 550 {Cond: minCond(50 * time.Millisecond)}, 551 {Cond: minCond(100 * time.Millisecond)}, 552 {Cond: minCond(200 * time.Millisecond)}, 553 {Cond: minCond(500 * time.Millisecond)}, 554 {Cond: minCond(1 * time.Second)}, 555 {Cond: minCond(10 * time.Second)}, 556 {Cond: minCond(100 * time.Second)}, 557 {Cond: errorCond{}}, 558 }, 559 Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }), 560 } 561 } 562 563 // traceBucket represents a size-capped bucket of historic traces, 564 // along with a condition for a trace to belong to the bucket. 565 type traceBucket struct { 566 Cond cond 567 568 // Ring buffer implementation of a fixed-size FIFO queue. 569 mu sync.RWMutex 570 buf [tracesPerBucket]*trace 571 start int // < tracesPerBucket 572 length int // <= tracesPerBucket 573 } 574 575 func (b *traceBucket) Add(tr *trace) { 576 b.mu.Lock() 577 defer b.mu.Unlock() 578 579 i := b.start + b.length 580 if i >= tracesPerBucket { 581 i -= tracesPerBucket 582 } 583 if b.length == tracesPerBucket { 584 // "Remove" an element from the bucket. 585 b.buf[i].unref() 586 b.start++ 587 if b.start == tracesPerBucket { 588 b.start = 0 589 } 590 } 591 b.buf[i] = tr 592 if b.length < tracesPerBucket { 593 b.length++ 594 } 595 tr.ref() 596 } 597 598 // Copy returns a copy of the traces in the bucket. 599 // If tracedOnly is true, only the traces with trace information will be returned. 600 // The logs will be ref'd before returning; the caller should call 601 // the Free method when it is done with them. 602 // TODO(dsymonds): keep track of traced requests in separate buckets. 603 func (b *traceBucket) Copy(tracedOnly bool) traceList { 604 b.mu.RLock() 605 defer b.mu.RUnlock() 606 607 trl := make(traceList, 0, b.length) 608 for i, x := 0, b.start; i < b.length; i++ { 609 tr := b.buf[x] 610 if !tracedOnly || tr.spanID != 0 { 611 tr.ref() 612 trl = append(trl, tr) 613 } 614 x++ 615 if x == b.length { 616 x = 0 617 } 618 } 619 return trl 620 } 621 622 func (b *traceBucket) Empty() bool { 623 b.mu.RLock() 624 defer b.mu.RUnlock() 625 return b.length == 0 626 } 627 628 // cond represents a condition on a trace. 629 type cond interface { 630 match(t *trace) bool 631 String() string 632 } 633 634 type minCond time.Duration 635 636 func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) } 637 func (m minCond) String() string { return fmt.Sprintf("%gs", time.Duration(m).Seconds()) } 638 639 type errorCond struct{} 640 641 func (e errorCond) match(t *trace) bool { return t.IsError } 642 func (e errorCond) String() string { return "errors" } 643 644 type traceList []*trace 645 646 // Free calls unref on each element of the list. 647 func (trl traceList) Free() { 648 for _, t := range trl { 649 t.unref() 650 } 651 } 652 653 // traceList may be sorted in reverse chronological order. 654 func (trl traceList) Len() int { return len(trl) } 655 func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) } 656 func (trl traceList) Swap(i, j int) { trl[i], trl[j] = trl[j], trl[i] } 657 658 // An event is a timestamped log entry in a trace. 659 type event struct { 660 When time.Time 661 Elapsed time.Duration // since previous event in trace 662 NewDay bool // whether this event is on a different day to the previous event 663 Recyclable bool // whether this event was passed via LazyLog 664 Sensitive bool // whether this event contains sensitive information 665 What interface{} // string or fmt.Stringer 666 } 667 668 // WhenString returns a string representation of the elapsed time of the event. 669 // It will include the date if midnight was crossed. 670 func (e event) WhenString() string { 671 if e.NewDay { 672 return e.When.Format("2006/01/02 15:04:05.000000") 673 } 674 return e.When.Format("15:04:05.000000") 675 } 676 677 // discarded represents a number of discarded events. 678 // It is stored as *discarded to make it easier to update in-place. 679 type discarded int 680 681 func (d *discarded) String() string { 682 return fmt.Sprintf("(%d events discarded)", int(*d)) 683 } 684 685 // trace represents an active or complete request, 686 // either sent or received by this program. 687 type trace struct { 688 // Family is the top-level grouping of traces to which this belongs. 689 Family string 690 691 // Title is the title of this trace. 692 Title string 693 694 // Start time of the this trace. 695 Start time.Time 696 697 mu sync.RWMutex 698 events []event // Append-only sequence of events (modulo discards). 699 maxEvents int 700 recycler func(interface{}) 701 IsError bool // Whether this trace resulted in an error. 702 Elapsed time.Duration // Elapsed time for this trace, zero while active. 703 traceID uint64 // Trace information if non-zero. 704 spanID uint64 705 706 refs int32 // how many buckets this is in 707 disc discarded // scratch space to avoid allocation 708 709 finishStack []byte // where finish was called, if DebugUseAfterFinish is set 710 711 eventsBuf [4]event // preallocated buffer in case we only log a few events 712 } 713 714 func (tr *trace) reset() { 715 // Clear all but the mutex. Mutexes may not be copied, even when unlocked. 716 tr.Family = "" 717 tr.Title = "" 718 tr.Start = time.Time{} 719 720 tr.mu.Lock() 721 tr.Elapsed = 0 722 tr.traceID = 0 723 tr.spanID = 0 724 tr.IsError = false 725 tr.maxEvents = 0 726 tr.events = nil 727 tr.recycler = nil 728 tr.mu.Unlock() 729 730 tr.refs = 0 731 tr.disc = 0 732 tr.finishStack = nil 733 for i := range tr.eventsBuf { 734 tr.eventsBuf[i] = event{} 735 } 736 } 737 738 // delta returns the elapsed time since the last event or the trace start, 739 // and whether it spans midnight. 740 // L >= tr.mu 741 func (tr *trace) delta(t time.Time) (time.Duration, bool) { 742 if len(tr.events) == 0 { 743 return t.Sub(tr.Start), false 744 } 745 prev := tr.events[len(tr.events)-1].When 746 return t.Sub(prev), prev.Day() != t.Day() 747 } 748 749 func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) { 750 if DebugUseAfterFinish && tr.finishStack != nil { 751 buf := make([]byte, 4<<10) // 4 KB should be enough 752 n := runtime.Stack(buf, false) 753 log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n]) 754 } 755 756 /* 757 NOTE TO DEBUGGERS 758 759 If you are here because your program panicked in this code, 760 it is almost definitely the fault of code using this package, 761 and very unlikely to be the fault of this code. 762 763 The most likely scenario is that some code elsewhere is using 764 a trace.Trace after its Finish method is called. 765 You can temporarily set the DebugUseAfterFinish var 766 to help discover where that is; do not leave that var set, 767 since it makes this package much less efficient. 768 */ 769 770 e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive} 771 tr.mu.Lock() 772 e.Elapsed, e.NewDay = tr.delta(e.When) 773 if len(tr.events) < tr.maxEvents { 774 tr.events = append(tr.events, e) 775 } else { 776 // Discard the middle events. 777 di := int((tr.maxEvents - 1) / 2) 778 if d, ok := tr.events[di].What.(*discarded); ok { 779 (*d)++ 780 } else { 781 // disc starts at two to count for the event it is replacing, 782 // plus the next one that we are about to drop. 783 tr.disc = 2 784 if tr.recycler != nil && tr.events[di].Recyclable { 785 go tr.recycler(tr.events[di].What) 786 } 787 tr.events[di].What = &tr.disc 788 } 789 // The timestamp of the discarded meta-event should be 790 // the time of the last event it is representing. 791 tr.events[di].When = tr.events[di+1].When 792 793 if tr.recycler != nil && tr.events[di+1].Recyclable { 794 go tr.recycler(tr.events[di+1].What) 795 } 796 copy(tr.events[di+1:], tr.events[di+2:]) 797 tr.events[tr.maxEvents-1] = e 798 } 799 tr.mu.Unlock() 800 } 801 802 func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) { 803 tr.addEvent(x, true, sensitive) 804 } 805 806 func (tr *trace) LazyPrintf(format string, a ...interface{}) { 807 tr.addEvent(&lazySprintf{format, a}, false, false) 808 } 809 810 func (tr *trace) SetError() { 811 tr.mu.Lock() 812 tr.IsError = true 813 tr.mu.Unlock() 814 } 815 816 func (tr *trace) SetRecycler(f func(interface{})) { 817 tr.mu.Lock() 818 tr.recycler = f 819 tr.mu.Unlock() 820 } 821 822 func (tr *trace) SetTraceInfo(traceID, spanID uint64) { 823 tr.mu.Lock() 824 tr.traceID, tr.spanID = traceID, spanID 825 tr.mu.Unlock() 826 } 827 828 func (tr *trace) SetMaxEvents(m int) { 829 tr.mu.Lock() 830 // Always keep at least three events: first, discarded count, last. 831 if len(tr.events) == 0 && m > 3 { 832 tr.maxEvents = m 833 } 834 tr.mu.Unlock() 835 } 836 837 func (tr *trace) ref() { 838 atomic.AddInt32(&tr.refs, 1) 839 } 840 841 func (tr *trace) unref() { 842 if atomic.AddInt32(&tr.refs, -1) == 0 { 843 tr.mu.RLock() 844 if tr.recycler != nil { 845 // freeTrace clears tr, so we hold tr.recycler and tr.events here. 846 go func(f func(interface{}), es []event) { 847 for _, e := range es { 848 if e.Recyclable { 849 f(e.What) 850 } 851 } 852 }(tr.recycler, tr.events) 853 } 854 tr.mu.RUnlock() 855 856 freeTrace(tr) 857 } 858 } 859 860 func (tr *trace) When() string { 861 return tr.Start.Format("2006/01/02 15:04:05.000000") 862 } 863 864 func (tr *trace) ElapsedTime() string { 865 tr.mu.RLock() 866 t := tr.Elapsed 867 tr.mu.RUnlock() 868 869 if t == 0 { 870 // Active trace. 871 t = time.Since(tr.Start) 872 } 873 return fmt.Sprintf("%.6f", t.Seconds()) 874 } 875 876 func (tr *trace) Events() []event { 877 tr.mu.RLock() 878 defer tr.mu.RUnlock() 879 return tr.events 880 } 881 882 var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool? 883 884 // newTrace returns a trace ready to use. 885 func newTrace() *trace { 886 select { 887 case tr := <-traceFreeList: 888 return tr 889 default: 890 return new(trace) 891 } 892 } 893 894 // freeTrace adds tr to traceFreeList if there's room. 895 // This is non-blocking. 896 func freeTrace(tr *trace) { 897 if DebugUseAfterFinish { 898 return // never reuse 899 } 900 tr.reset() 901 select { 902 case traceFreeList <- tr: 903 default: 904 } 905 } 906 907 func elapsed(d time.Duration) string { 908 b := []byte(fmt.Sprintf("%.6f", d.Seconds())) 909 910 // For subsecond durations, blank all zeros before decimal point, 911 // and all zeros between the decimal point and the first non-zero digit. 912 if d < time.Second { 913 dot := bytes.IndexByte(b, '.') 914 for i := 0; i < dot; i++ { 915 b[i] = ' ' 916 } 917 for i := dot + 1; i < len(b); i++ { 918 if b[i] == '0' { 919 b[i] = ' ' 920 } else { 921 break 922 } 923 } 924 } 925 926 return string(b) 927 } 928 929 var pageTmplCache *template.Template 930 var pageTmplOnce sync.Once 931 932 func pageTmpl() *template.Template { 933 pageTmplOnce.Do(func() { 934 pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{ 935 "elapsed": elapsed, 936 "add": func(a, b int) int { return a + b }, 937 }).Parse(pageHTML)) 938 }) 939 return pageTmplCache 940 } 941 942 const pageHTML = ` 943 {{template "Prolog" .}} 944 {{template "StatusTable" .}} 945 {{template "Epilog" .}} 946 947 {{define "Prolog"}} 948 <html> 949 <head> 950 <title>/debug/requests</title> 951 <style type="text/css"> 952 body { 953 font-family: sans-serif; 954 } 955 table#tr-status td.family { 956 padding-right: 2em; 957 } 958 table#tr-status td.active { 959 padding-right: 1em; 960 } 961 table#tr-status td.latency-first { 962 padding-left: 1em; 963 } 964 table#tr-status td.empty { 965 color: #aaa; 966 } 967 table#reqs { 968 margin-top: 1em; 969 } 970 table#reqs tr.first { 971 {{if $.Expanded}}font-weight: bold;{{end}} 972 } 973 table#reqs td { 974 font-family: monospace; 975 } 976 table#reqs td.when { 977 text-align: right; 978 white-space: nowrap; 979 } 980 table#reqs td.elapsed { 981 padding: 0 0.5em; 982 text-align: right; 983 white-space: pre; 984 width: 10em; 985 } 986 address { 987 font-size: smaller; 988 margin-top: 5em; 989 } 990 </style> 991 </head> 992 <body> 993 994 <h1>/debug/requests</h1> 995 {{end}} {{/* end of Prolog */}} 996 997 {{define "StatusTable"}} 998 <table id="tr-status"> 999 {{range $fam := .Families}} 1000 <tr> 1001 <td class="family">{{$fam}}</td> 1002 1003 {{$n := index $.ActiveTraceCount $fam}} 1004 <td class="active {{if not $n}}empty{{end}}"> 1005 {{if $n}}<a href="?fam={{$fam}}&b=-1{{if $.Expanded}}&exp=1{{end}}">{{end}} 1006 [{{$n}} active] 1007 {{if $n}}</a>{{end}} 1008 </td> 1009 1010 {{$f := index $.CompletedTraces $fam}} 1011 {{range $i, $b := $f.Buckets}} 1012 {{$empty := $b.Empty}} 1013 <td {{if $empty}}class="empty"{{end}}> 1014 {{if not $empty}}<a href="?fam={{$fam}}&b={{$i}}{{if $.Expanded}}&exp=1{{end}}">{{end}} 1015 [{{.Cond}}] 1016 {{if not $empty}}</a>{{end}} 1017 </td> 1018 {{end}} 1019 1020 {{$nb := len $f.Buckets}} 1021 <td class="latency-first"> 1022 <a href="?fam={{$fam}}&b={{$nb}}">[minute]</a> 1023 </td> 1024 <td> 1025 <a href="?fam={{$fam}}&b={{add $nb 1}}">[hour]</a> 1026 </td> 1027 <td> 1028 <a href="?fam={{$fam}}&b={{add $nb 2}}">[total]</a> 1029 </td> 1030 1031 </tr> 1032 {{end}} 1033 </table> 1034 {{end}} {{/* end of StatusTable */}} 1035 1036 {{define "Epilog"}} 1037 {{if $.Traces}} 1038 <hr /> 1039 <h3>Family: {{$.Family}}</h3> 1040 1041 {{if or $.Expanded $.Traced}} 1042 <a href="?fam={{$.Family}}&b={{$.Bucket}}">[Normal/Summary]</a> 1043 {{else}} 1044 [Normal/Summary] 1045 {{end}} 1046 1047 {{if or (not $.Expanded) $.Traced}} 1048 <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">[Normal/Expanded]</a> 1049 {{else}} 1050 [Normal/Expanded] 1051 {{end}} 1052 1053 {{if not $.Active}} 1054 {{if or $.Expanded (not $.Traced)}} 1055 <a href="?fam={{$.Family}}&b={{$.Bucket}}&rtraced=1">[Traced/Summary]</a> 1056 {{else}} 1057 [Traced/Summary] 1058 {{end}} 1059 {{if or (not $.Expanded) (not $.Traced)}} 1060 <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1&rtraced=1">[Traced/Expanded]</a> 1061 {{else}} 1062 [Traced/Expanded] 1063 {{end}} 1064 {{end}} 1065 1066 {{if $.Total}} 1067 <p><em>Showing <b>{{len $.Traces}}</b> of <b>{{$.Total}}</b> traces.</em></p> 1068 {{end}} 1069 1070 <table id="reqs"> 1071 <caption> 1072 {{if $.Active}}Active{{else}}Completed{{end}} Requests 1073 </caption> 1074 <tr><th>When</th><th>Elapsed (s)</th></tr> 1075 {{range $tr := $.Traces}} 1076 <tr class="first"> 1077 <td class="when">{{$tr.When}}</td> 1078 <td class="elapsed">{{$tr.ElapsedTime}}</td> 1079 <td>{{$tr.Title}}</td> 1080 {{/* TODO: include traceID/spanID */}} 1081 </tr> 1082 {{if $.Expanded}} 1083 {{range $tr.Events}} 1084 <tr> 1085 <td class="when">{{.WhenString}}</td> 1086 <td class="elapsed">{{elapsed .Elapsed}}</td> 1087 <td>{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}<em>[redacted]</em>{{end}}</td> 1088 </tr> 1089 {{end}} 1090 {{end}} 1091 {{end}} 1092 </table> 1093 {{end}} {{/* if $.Traces */}} 1094 1095 {{if $.Histogram}} 1096 <h4>Latency (µs) of {{$.Family}} over {{$.HistogramWindow}}</h4> 1097 {{$.Histogram}} 1098 {{end}} {{/* if $.Histogram */}} 1099 1100 </body> 1101 </html> 1102 {{end}} {{/* end of Epilog */}} 1103 ` 1104