podman

Форк
0
1130 строк · 28.1 Кб
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 // import "golang.org/x/net/trace"
64

65
import (
66
	"bytes"
67
	"context"
68
	"fmt"
69
	"html/template"
70
	"io"
71
	"log"
72
	"net"
73
	"net/http"
74
	"net/url"
75
	"runtime"
76
	"sort"
77
	"strconv"
78
	"sync"
79
	"sync/atomic"
80
	"time"
81

82
	"golang.org/x/net/internal/timeseries"
83
)
84

85
// DebugUseAfterFinish controls whether to debug uses of Trace values after finishing.
86
// FOR DEBUGGING ONLY. This will slow down the program.
87
var DebugUseAfterFinish = false
88

89
// HTTP ServeMux paths.
90
const (
91
	debugRequestsPath = "/debug/requests"
92
	debugEventsPath   = "/debug/events"
93
)
94

95
// AuthRequest determines whether a specific request is permitted to load the
96
// /debug/requests or /debug/events pages.
97
//
98
// It returns two bools; the first indicates whether the page may be viewed at all,
99
// and the second indicates whether sensitive events will be shown.
100
//
101
// AuthRequest may be replaced by a program to customize its authorization requirements.
102
//
103
// The default AuthRequest function returns (true, true) if and only if the request
104
// comes from localhost/127.0.0.1/[::1].
105
var AuthRequest = func(req *http.Request) (any, sensitive bool) {
106
	// RemoteAddr is commonly in the form "IP" or "IP:port".
107
	// If it is in the form "IP:port", split off the port.
108
	host, _, err := net.SplitHostPort(req.RemoteAddr)
109
	if err != nil {
110
		host = req.RemoteAddr
111
	}
112
	switch host {
113
	case "localhost", "127.0.0.1", "::1":
114
		return true, true
115
	default:
116
		return false, false
117
	}
118
}
119

120
func init() {
121
	_, pat := http.DefaultServeMux.Handler(&http.Request{URL: &url.URL{Path: debugRequestsPath}})
122
	if pat == debugRequestsPath {
123
		panic("/debug/requests is already registered. You may have two independent copies of " +
124
			"golang.org/x/net/trace in your binary, trying to maintain separate state. This may " +
125
			"involve a vendored copy of golang.org/x/net/trace.")
126
	}
127

128
	// TODO(jbd): Serve Traces from /debug/traces in the future?
129
	// There is no requirement for a request to be present to have traces.
130
	http.HandleFunc(debugRequestsPath, Traces)
131
	http.HandleFunc(debugEventsPath, Events)
132
}
133

134
// NewContext returns a copy of the parent context
135
// and associates it with a Trace.
136
func NewContext(ctx context.Context, tr Trace) context.Context {
137
	return context.WithValue(ctx, contextKey, tr)
138
}
139

140
// FromContext returns the Trace bound to the context, if any.
141
func FromContext(ctx context.Context) (tr Trace, ok bool) {
142
	tr, ok = ctx.Value(contextKey).(Trace)
143
	return
144
}
145

146
// Traces responds with traces from the program.
147
// The package initialization registers it in http.DefaultServeMux
148
// at /debug/requests.
149
//
150
// It performs authorization by running AuthRequest.
151
func Traces(w http.ResponseWriter, req *http.Request) {
152
	any, sensitive := AuthRequest(req)
153
	if !any {
154
		http.Error(w, "not allowed", http.StatusUnauthorized)
155
		return
156
	}
157
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
158
	Render(w, req, sensitive)
159
}
160

161
// Events responds with a page of events collected by EventLogs.
162
// The package initialization registers it in http.DefaultServeMux
163
// at /debug/events.
164
//
165
// It performs authorization by running AuthRequest.
166
func Events(w http.ResponseWriter, req *http.Request) {
167
	any, sensitive := AuthRequest(req)
168
	if !any {
169
		http.Error(w, "not allowed", http.StatusUnauthorized)
170
		return
171
	}
172
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
173
	RenderEvents(w, req, sensitive)
174
}
175

176
// Render renders the HTML page typically served at /debug/requests.
177
// It does not do any auth checking. The request may be nil.
178
//
179
// Most users will use the Traces handler.
180
func Render(w io.Writer, req *http.Request, sensitive bool) {
181
	data := &struct {
182
		Families         []string
183
		ActiveTraceCount map[string]int
184
		CompletedTraces  map[string]*family
185

186
		// Set when a bucket has been selected.
187
		Traces        traceList
188
		Family        string
189
		Bucket        int
190
		Expanded      bool
191
		Traced        bool
192
		Active        bool
193
		ShowSensitive bool // whether to show sensitive events
194

195
		Histogram       template.HTML
196
		HistogramWindow string // e.g. "last minute", "last hour", "all time"
197

198
		// If non-zero, the set of traces is a partial set,
199
		// and this is the total number.
200
		Total int
201
	}{
202
		CompletedTraces: completedTraces,
203
	}
204

205
	data.ShowSensitive = sensitive
206
	if req != nil {
207
		// Allow show_sensitive=0 to force hiding of sensitive data for testing.
208
		// This only goes one way; you can't use show_sensitive=1 to see things.
209
		if req.FormValue("show_sensitive") == "0" {
210
			data.ShowSensitive = false
211
		}
212

213
		if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
214
			data.Expanded = exp
215
		}
216
		if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil {
217
			data.Traced = exp
218
		}
219
	}
220

221
	completedMu.RLock()
222
	data.Families = make([]string, 0, len(completedTraces))
223
	for fam := range completedTraces {
224
		data.Families = append(data.Families, fam)
225
	}
226
	completedMu.RUnlock()
227
	sort.Strings(data.Families)
228

229
	// We are careful here to minimize the time spent locking activeMu,
230
	// since that lock is required every time an RPC starts and finishes.
231
	data.ActiveTraceCount = make(map[string]int, len(data.Families))
232
	activeMu.RLock()
233
	for fam, s := range activeTraces {
234
		data.ActiveTraceCount[fam] = s.Len()
235
	}
236
	activeMu.RUnlock()
237

238
	var ok bool
239
	data.Family, data.Bucket, ok = parseArgs(req)
240
	switch {
241
	case !ok:
242
		// No-op
243
	case data.Bucket == -1:
244
		data.Active = true
245
		n := data.ActiveTraceCount[data.Family]
246
		data.Traces = getActiveTraces(data.Family)
247
		if len(data.Traces) < n {
248
			data.Total = n
249
		}
250
	case data.Bucket < bucketsPerFamily:
251
		if b := lookupBucket(data.Family, data.Bucket); b != nil {
252
			data.Traces = b.Copy(data.Traced)
253
		}
254
	default:
255
		if f := getFamily(data.Family, false); f != nil {
256
			var obs timeseries.Observable
257
			f.LatencyMu.RLock()
258
			switch o := data.Bucket - bucketsPerFamily; o {
259
			case 0:
260
				obs = f.Latency.Minute()
261
				data.HistogramWindow = "last minute"
262
			case 1:
263
				obs = f.Latency.Hour()
264
				data.HistogramWindow = "last hour"
265
			case 2:
266
				obs = f.Latency.Total()
267
				data.HistogramWindow = "all time"
268
			}
269
			f.LatencyMu.RUnlock()
270
			if obs != nil {
271
				data.Histogram = obs.(*histogram).html()
272
			}
273
		}
274
	}
275

276
	if data.Traces != nil {
277
		defer data.Traces.Free()
278
		sort.Sort(data.Traces)
279
	}
280

281
	completedMu.RLock()
282
	defer completedMu.RUnlock()
283
	if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil {
284
		log.Printf("net/trace: Failed executing template: %v", err)
285
	}
286
}
287

288
func parseArgs(req *http.Request) (fam string, b int, ok bool) {
289
	if req == nil {
290
		return "", 0, false
291
	}
292
	fam, bStr := req.FormValue("fam"), req.FormValue("b")
293
	if fam == "" || bStr == "" {
294
		return "", 0, false
295
	}
296
	b, err := strconv.Atoi(bStr)
297
	if err != nil || b < -1 {
298
		return "", 0, false
299
	}
300

301
	return fam, b, true
302
}
303

304
func lookupBucket(fam string, b int) *traceBucket {
305
	f := getFamily(fam, false)
306
	if f == nil || b < 0 || b >= len(f.Buckets) {
307
		return nil
308
	}
309
	return f.Buckets[b]
310
}
311

312
type contextKeyT string
313

314
var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
315

316
// Trace represents an active request.
317
type Trace interface {
318
	// LazyLog adds x to the event log. It will be evaluated each time the
319
	// /debug/requests page is rendered. Any memory referenced by x will be
320
	// pinned until the trace is finished and later discarded.
321
	LazyLog(x fmt.Stringer, sensitive bool)
322

323
	// LazyPrintf evaluates its arguments with fmt.Sprintf each time the
324
	// /debug/requests page is rendered. Any memory referenced by a will be
325
	// pinned until the trace is finished and later discarded.
326
	LazyPrintf(format string, a ...interface{})
327

328
	// SetError declares that this trace resulted in an error.
329
	SetError()
330

331
	// SetRecycler sets a recycler for the trace.
332
	// f will be called for each event passed to LazyLog at a time when
333
	// it is no longer required, whether while the trace is still active
334
	// and the event is discarded, or when a completed trace is discarded.
335
	SetRecycler(f func(interface{}))
336

337
	// SetTraceInfo sets the trace info for the trace.
338
	// This is currently unused.
339
	SetTraceInfo(traceID, spanID uint64)
340

341
	// SetMaxEvents sets the maximum number of events that will be stored
342
	// in the trace. This has no effect if any events have already been
343
	// added to the trace.
344
	SetMaxEvents(m int)
345

346
	// Finish declares that this trace is complete.
347
	// The trace should not be used after calling this method.
348
	Finish()
349
}
350

351
type lazySprintf struct {
352
	format string
353
	a      []interface{}
354
}
355

356
func (l *lazySprintf) String() string {
357
	return fmt.Sprintf(l.format, l.a...)
358
}
359

360
// New returns a new Trace with the specified family and title.
361
func New(family, title string) Trace {
362
	tr := newTrace()
363
	tr.ref()
364
	tr.Family, tr.Title = family, title
365
	tr.Start = time.Now()
366
	tr.maxEvents = maxEventsPerTrace
367
	tr.events = tr.eventsBuf[:0]
368

369
	activeMu.RLock()
370
	s := activeTraces[tr.Family]
371
	activeMu.RUnlock()
372
	if s == nil {
373
		activeMu.Lock()
374
		s = activeTraces[tr.Family] // check again
375
		if s == nil {
376
			s = new(traceSet)
377
			activeTraces[tr.Family] = s
378
		}
379
		activeMu.Unlock()
380
	}
381
	s.Add(tr)
382

383
	// Trigger allocation of the completed trace structure for this family.
384
	// This will cause the family to be present in the request page during
385
	// the first trace of this family. We don't care about the return value,
386
	// nor is there any need for this to run inline, so we execute it in its
387
	// own goroutine, but only if the family isn't allocated yet.
388
	completedMu.RLock()
389
	if _, ok := completedTraces[tr.Family]; !ok {
390
		go allocFamily(tr.Family)
391
	}
392
	completedMu.RUnlock()
393

394
	return tr
395
}
396

397
func (tr *trace) Finish() {
398
	elapsed := time.Since(tr.Start)
399
	tr.mu.Lock()
400
	tr.Elapsed = elapsed
401
	tr.mu.Unlock()
402

403
	if DebugUseAfterFinish {
404
		buf := make([]byte, 4<<10) // 4 KB should be enough
405
		n := runtime.Stack(buf, false)
406
		tr.finishStack = buf[:n]
407
	}
408

409
	activeMu.RLock()
410
	m := activeTraces[tr.Family]
411
	activeMu.RUnlock()
412
	m.Remove(tr)
413

414
	f := getFamily(tr.Family, true)
415
	tr.mu.RLock() // protects tr fields in Cond.match calls
416
	for _, b := range f.Buckets {
417
		if b.Cond.match(tr) {
418
			b.Add(tr)
419
		}
420
	}
421
	tr.mu.RUnlock()
422

423
	// Add a sample of elapsed time as microseconds to the family's timeseries
424
	h := new(histogram)
425
	h.addMeasurement(elapsed.Nanoseconds() / 1e3)
426
	f.LatencyMu.Lock()
427
	f.Latency.Add(h)
428
	f.LatencyMu.Unlock()
429

430
	tr.unref() // matches ref in New
431
}
432

433
const (
434
	bucketsPerFamily    = 9
435
	tracesPerBucket     = 10
436
	maxActiveTraces     = 20 // Maximum number of active traces to show.
437
	maxEventsPerTrace   = 10
438
	numHistogramBuckets = 38
439
)
440

441
var (
442
	// The active traces.
443
	activeMu     sync.RWMutex
444
	activeTraces = make(map[string]*traceSet) // family -> traces
445

446
	// Families of completed traces.
447
	completedMu     sync.RWMutex
448
	completedTraces = make(map[string]*family) // family -> traces
449
)
450

451
type traceSet struct {
452
	mu sync.RWMutex
453
	m  map[*trace]bool
454

455
	// We could avoid the entire map scan in FirstN by having a slice of all the traces
456
	// ordered by start time, and an index into that from the trace struct, with a periodic
457
	// repack of the slice after enough traces finish; we could also use a skip list or similar.
458
	// However, that would shift some of the expense from /debug/requests time to RPC time,
459
	// which is probably the wrong trade-off.
460
}
461

462
func (ts *traceSet) Len() int {
463
	ts.mu.RLock()
464
	defer ts.mu.RUnlock()
465
	return len(ts.m)
466
}
467

468
func (ts *traceSet) Add(tr *trace) {
469
	ts.mu.Lock()
470
	if ts.m == nil {
471
		ts.m = make(map[*trace]bool)
472
	}
473
	ts.m[tr] = true
474
	ts.mu.Unlock()
475
}
476

477
func (ts *traceSet) Remove(tr *trace) {
478
	ts.mu.Lock()
479
	delete(ts.m, tr)
480
	ts.mu.Unlock()
481
}
482

483
// FirstN returns the first n traces ordered by time.
484
func (ts *traceSet) FirstN(n int) traceList {
485
	ts.mu.RLock()
486
	defer ts.mu.RUnlock()
487

488
	if n > len(ts.m) {
489
		n = len(ts.m)
490
	}
491
	trl := make(traceList, 0, n)
492

493
	// Fast path for when no selectivity is needed.
494
	if n == len(ts.m) {
495
		for tr := range ts.m {
496
			tr.ref()
497
			trl = append(trl, tr)
498
		}
499
		sort.Sort(trl)
500
		return trl
501
	}
502

503
	// Pick the oldest n traces.
504
	// This is inefficient. See the comment in the traceSet struct.
505
	for tr := range ts.m {
506
		// Put the first n traces into trl in the order they occur.
507
		// When we have n, sort trl, and thereafter maintain its order.
508
		if len(trl) < n {
509
			tr.ref()
510
			trl = append(trl, tr)
511
			if len(trl) == n {
512
				// This is guaranteed to happen exactly once during this loop.
513
				sort.Sort(trl)
514
			}
515
			continue
516
		}
517
		if tr.Start.After(trl[n-1].Start) {
518
			continue
519
		}
520

521
		// Find where to insert this one.
522
		tr.ref()
523
		i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) })
524
		trl[n-1].unref()
525
		copy(trl[i+1:], trl[i:])
526
		trl[i] = tr
527
	}
528

529
	return trl
530
}
531

532
func getActiveTraces(fam string) traceList {
533
	activeMu.RLock()
534
	s := activeTraces[fam]
535
	activeMu.RUnlock()
536
	if s == nil {
537
		return nil
538
	}
539
	return s.FirstN(maxActiveTraces)
540
}
541

542
func getFamily(fam string, allocNew bool) *family {
543
	completedMu.RLock()
544
	f := completedTraces[fam]
545
	completedMu.RUnlock()
546
	if f == nil && allocNew {
547
		f = allocFamily(fam)
548
	}
549
	return f
550
}
551

552
func allocFamily(fam string) *family {
553
	completedMu.Lock()
554
	defer completedMu.Unlock()
555
	f := completedTraces[fam]
556
	if f == nil {
557
		f = newFamily()
558
		completedTraces[fam] = f
559
	}
560
	return f
561
}
562

563
// family represents a set of trace buckets and associated latency information.
564
type family struct {
565
	// traces may occur in multiple buckets.
566
	Buckets [bucketsPerFamily]*traceBucket
567

568
	// latency time series
569
	LatencyMu sync.RWMutex
570
	Latency   *timeseries.MinuteHourSeries
571
}
572

573
func newFamily() *family {
574
	return &family{
575
		Buckets: [bucketsPerFamily]*traceBucket{
576
			{Cond: minCond(0)},
577
			{Cond: minCond(50 * time.Millisecond)},
578
			{Cond: minCond(100 * time.Millisecond)},
579
			{Cond: minCond(200 * time.Millisecond)},
580
			{Cond: minCond(500 * time.Millisecond)},
581
			{Cond: minCond(1 * time.Second)},
582
			{Cond: minCond(10 * time.Second)},
583
			{Cond: minCond(100 * time.Second)},
584
			{Cond: errorCond{}},
585
		},
586
		Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }),
587
	}
588
}
589

590
// traceBucket represents a size-capped bucket of historic traces,
591
// along with a condition for a trace to belong to the bucket.
592
type traceBucket struct {
593
	Cond cond
594

595
	// Ring buffer implementation of a fixed-size FIFO queue.
596
	mu     sync.RWMutex
597
	buf    [tracesPerBucket]*trace
598
	start  int // < tracesPerBucket
599
	length int // <= tracesPerBucket
600
}
601

602
func (b *traceBucket) Add(tr *trace) {
603
	b.mu.Lock()
604
	defer b.mu.Unlock()
605

606
	i := b.start + b.length
607
	if i >= tracesPerBucket {
608
		i -= tracesPerBucket
609
	}
610
	if b.length == tracesPerBucket {
611
		// "Remove" an element from the bucket.
612
		b.buf[i].unref()
613
		b.start++
614
		if b.start == tracesPerBucket {
615
			b.start = 0
616
		}
617
	}
618
	b.buf[i] = tr
619
	if b.length < tracesPerBucket {
620
		b.length++
621
	}
622
	tr.ref()
623
}
624

625
// Copy returns a copy of the traces in the bucket.
626
// If tracedOnly is true, only the traces with trace information will be returned.
627
// The logs will be ref'd before returning; the caller should call
628
// the Free method when it is done with them.
629
// TODO(dsymonds): keep track of traced requests in separate buckets.
630
func (b *traceBucket) Copy(tracedOnly bool) traceList {
631
	b.mu.RLock()
632
	defer b.mu.RUnlock()
633

634
	trl := make(traceList, 0, b.length)
635
	for i, x := 0, b.start; i < b.length; i++ {
636
		tr := b.buf[x]
637
		if !tracedOnly || tr.spanID != 0 {
638
			tr.ref()
639
			trl = append(trl, tr)
640
		}
641
		x++
642
		if x == b.length {
643
			x = 0
644
		}
645
	}
646
	return trl
647
}
648

649
func (b *traceBucket) Empty() bool {
650
	b.mu.RLock()
651
	defer b.mu.RUnlock()
652
	return b.length == 0
653
}
654

655
// cond represents a condition on a trace.
656
type cond interface {
657
	match(t *trace) bool
658
	String() string
659
}
660

661
type minCond time.Duration
662

663
func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) }
664
func (m minCond) String() string      { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) }
665

666
type errorCond struct{}
667

668
func (e errorCond) match(t *trace) bool { return t.IsError }
669
func (e errorCond) String() string      { return "errors" }
670

671
type traceList []*trace
672

673
// Free calls unref on each element of the list.
674
func (trl traceList) Free() {
675
	for _, t := range trl {
676
		t.unref()
677
	}
678
}
679

680
// traceList may be sorted in reverse chronological order.
681
func (trl traceList) Len() int           { return len(trl) }
682
func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) }
683
func (trl traceList) Swap(i, j int)      { trl[i], trl[j] = trl[j], trl[i] }
684

685
// An event is a timestamped log entry in a trace.
686
type event struct {
687
	When       time.Time
688
	Elapsed    time.Duration // since previous event in trace
689
	NewDay     bool          // whether this event is on a different day to the previous event
690
	Recyclable bool          // whether this event was passed via LazyLog
691
	Sensitive  bool          // whether this event contains sensitive information
692
	What       interface{}   // string or fmt.Stringer
693
}
694

695
// WhenString returns a string representation of the elapsed time of the event.
696
// It will include the date if midnight was crossed.
697
func (e event) WhenString() string {
698
	if e.NewDay {
699
		return e.When.Format("2006/01/02 15:04:05.000000")
700
	}
701
	return e.When.Format("15:04:05.000000")
702
}
703

704
// discarded represents a number of discarded events.
705
// It is stored as *discarded to make it easier to update in-place.
706
type discarded int
707

708
func (d *discarded) String() string {
709
	return fmt.Sprintf("(%d events discarded)", int(*d))
710
}
711

712
// trace represents an active or complete request,
713
// either sent or received by this program.
714
type trace struct {
715
	// Family is the top-level grouping of traces to which this belongs.
716
	Family string
717

718
	// Title is the title of this trace.
719
	Title string
720

721
	// Start time of the this trace.
722
	Start time.Time
723

724
	mu        sync.RWMutex
725
	events    []event // Append-only sequence of events (modulo discards).
726
	maxEvents int
727
	recycler  func(interface{})
728
	IsError   bool          // Whether this trace resulted in an error.
729
	Elapsed   time.Duration // Elapsed time for this trace, zero while active.
730
	traceID   uint64        // Trace information if non-zero.
731
	spanID    uint64
732

733
	refs int32     // how many buckets this is in
734
	disc discarded // scratch space to avoid allocation
735

736
	finishStack []byte // where finish was called, if DebugUseAfterFinish is set
737

738
	eventsBuf [4]event // preallocated buffer in case we only log a few events
739
}
740

741
func (tr *trace) reset() {
742
	// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
743
	tr.Family = ""
744
	tr.Title = ""
745
	tr.Start = time.Time{}
746

747
	tr.mu.Lock()
748
	tr.Elapsed = 0
749
	tr.traceID = 0
750
	tr.spanID = 0
751
	tr.IsError = false
752
	tr.maxEvents = 0
753
	tr.events = nil
754
	tr.recycler = nil
755
	tr.mu.Unlock()
756

757
	tr.refs = 0
758
	tr.disc = 0
759
	tr.finishStack = nil
760
	for i := range tr.eventsBuf {
761
		tr.eventsBuf[i] = event{}
762
	}
763
}
764

765
// delta returns the elapsed time since the last event or the trace start,
766
// and whether it spans midnight.
767
// L >= tr.mu
768
func (tr *trace) delta(t time.Time) (time.Duration, bool) {
769
	if len(tr.events) == 0 {
770
		return t.Sub(tr.Start), false
771
	}
772
	prev := tr.events[len(tr.events)-1].When
773
	return t.Sub(prev), prev.Day() != t.Day()
774
}
775

776
func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
777
	if DebugUseAfterFinish && tr.finishStack != nil {
778
		buf := make([]byte, 4<<10) // 4 KB should be enough
779
		n := runtime.Stack(buf, false)
780
		log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n])
781
	}
782

783
	/*
784
		NOTE TO DEBUGGERS
785

786
		If you are here because your program panicked in this code,
787
		it is almost definitely the fault of code using this package,
788
		and very unlikely to be the fault of this code.
789

790
		The most likely scenario is that some code elsewhere is using
791
		a trace.Trace after its Finish method is called.
792
		You can temporarily set the DebugUseAfterFinish var
793
		to help discover where that is; do not leave that var set,
794
		since it makes this package much less efficient.
795
	*/
796

797
	e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive}
798
	tr.mu.Lock()
799
	e.Elapsed, e.NewDay = tr.delta(e.When)
800
	if len(tr.events) < tr.maxEvents {
801
		tr.events = append(tr.events, e)
802
	} else {
803
		// Discard the middle events.
804
		di := int((tr.maxEvents - 1) / 2)
805
		if d, ok := tr.events[di].What.(*discarded); ok {
806
			(*d)++
807
		} else {
808
			// disc starts at two to count for the event it is replacing,
809
			// plus the next one that we are about to drop.
810
			tr.disc = 2
811
			if tr.recycler != nil && tr.events[di].Recyclable {
812
				go tr.recycler(tr.events[di].What)
813
			}
814
			tr.events[di].What = &tr.disc
815
		}
816
		// The timestamp of the discarded meta-event should be
817
		// the time of the last event it is representing.
818
		tr.events[di].When = tr.events[di+1].When
819

820
		if tr.recycler != nil && tr.events[di+1].Recyclable {
821
			go tr.recycler(tr.events[di+1].What)
822
		}
823
		copy(tr.events[di+1:], tr.events[di+2:])
824
		tr.events[tr.maxEvents-1] = e
825
	}
826
	tr.mu.Unlock()
827
}
828

829
func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) {
830
	tr.addEvent(x, true, sensitive)
831
}
832

833
func (tr *trace) LazyPrintf(format string, a ...interface{}) {
834
	tr.addEvent(&lazySprintf{format, a}, false, false)
835
}
836

837
func (tr *trace) SetError() {
838
	tr.mu.Lock()
839
	tr.IsError = true
840
	tr.mu.Unlock()
841
}
842

843
func (tr *trace) SetRecycler(f func(interface{})) {
844
	tr.mu.Lock()
845
	tr.recycler = f
846
	tr.mu.Unlock()
847
}
848

849
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
850
	tr.mu.Lock()
851
	tr.traceID, tr.spanID = traceID, spanID
852
	tr.mu.Unlock()
853
}
854

855
func (tr *trace) SetMaxEvents(m int) {
856
	tr.mu.Lock()
857
	// Always keep at least three events: first, discarded count, last.
858
	if len(tr.events) == 0 && m > 3 {
859
		tr.maxEvents = m
860
	}
861
	tr.mu.Unlock()
862
}
863

864
func (tr *trace) ref() {
865
	atomic.AddInt32(&tr.refs, 1)
866
}
867

868
func (tr *trace) unref() {
869
	if atomic.AddInt32(&tr.refs, -1) == 0 {
870
		tr.mu.RLock()
871
		if tr.recycler != nil {
872
			// freeTrace clears tr, so we hold tr.recycler and tr.events here.
873
			go func(f func(interface{}), es []event) {
874
				for _, e := range es {
875
					if e.Recyclable {
876
						f(e.What)
877
					}
878
				}
879
			}(tr.recycler, tr.events)
880
		}
881
		tr.mu.RUnlock()
882

883
		freeTrace(tr)
884
	}
885
}
886

887
func (tr *trace) When() string {
888
	return tr.Start.Format("2006/01/02 15:04:05.000000")
889
}
890

891
func (tr *trace) ElapsedTime() string {
892
	tr.mu.RLock()
893
	t := tr.Elapsed
894
	tr.mu.RUnlock()
895

896
	if t == 0 {
897
		// Active trace.
898
		t = time.Since(tr.Start)
899
	}
900
	return fmt.Sprintf("%.6f", t.Seconds())
901
}
902

903
func (tr *trace) Events() []event {
904
	tr.mu.RLock()
905
	defer tr.mu.RUnlock()
906
	return tr.events
907
}
908

909
var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool?
910

911
// newTrace returns a trace ready to use.
912
func newTrace() *trace {
913
	select {
914
	case tr := <-traceFreeList:
915
		return tr
916
	default:
917
		return new(trace)
918
	}
919
}
920

921
// freeTrace adds tr to traceFreeList if there's room.
922
// This is non-blocking.
923
func freeTrace(tr *trace) {
924
	if DebugUseAfterFinish {
925
		return // never reuse
926
	}
927
	tr.reset()
928
	select {
929
	case traceFreeList <- tr:
930
	default:
931
	}
932
}
933

934
func elapsed(d time.Duration) string {
935
	b := []byte(fmt.Sprintf("%.6f", d.Seconds()))
936

937
	// For subsecond durations, blank all zeros before decimal point,
938
	// and all zeros between the decimal point and the first non-zero digit.
939
	if d < time.Second {
940
		dot := bytes.IndexByte(b, '.')
941
		for i := 0; i < dot; i++ {
942
			b[i] = ' '
943
		}
944
		for i := dot + 1; i < len(b); i++ {
945
			if b[i] == '0' {
946
				b[i] = ' '
947
			} else {
948
				break
949
			}
950
		}
951
	}
952

953
	return string(b)
954
}
955

956
var pageTmplCache *template.Template
957
var pageTmplOnce sync.Once
958

959
func pageTmpl() *template.Template {
960
	pageTmplOnce.Do(func() {
961
		pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{
962
			"elapsed": elapsed,
963
			"add":     func(a, b int) int { return a + b },
964
		}).Parse(pageHTML))
965
	})
966
	return pageTmplCache
967
}
968

969
const pageHTML = `
970
{{template "Prolog" .}}
971
{{template "StatusTable" .}}
972
{{template "Epilog" .}}
973

974
{{define "Prolog"}}
975
<html>
976
	<head>
977
	<title>/debug/requests</title>
978
	<style type="text/css">
979
		body {
980
			font-family: sans-serif;
981
		}
982
		table#tr-status td.family {
983
			padding-right: 2em;
984
		}
985
		table#tr-status td.active {
986
			padding-right: 1em;
987
		}
988
		table#tr-status td.latency-first {
989
			padding-left: 1em;
990
		}
991
		table#tr-status td.empty {
992
			color: #aaa;
993
		}
994
		table#reqs {
995
			margin-top: 1em;
996
		}
997
		table#reqs tr.first {
998
			{{if $.Expanded}}font-weight: bold;{{end}}
999
		}
1000
		table#reqs td {
1001
			font-family: monospace;
1002
		}
1003
		table#reqs td.when {
1004
			text-align: right;
1005
			white-space: nowrap;
1006
		}
1007
		table#reqs td.elapsed {
1008
			padding: 0 0.5em;
1009
			text-align: right;
1010
			white-space: pre;
1011
			width: 10em;
1012
		}
1013
		address {
1014
			font-size: smaller;
1015
			margin-top: 5em;
1016
		}
1017
	</style>
1018
	</head>
1019
	<body>
1020

1021
<h1>/debug/requests</h1>
1022
{{end}} {{/* end of Prolog */}}
1023

1024
{{define "StatusTable"}}
1025
<table id="tr-status">
1026
	{{range $fam := .Families}}
1027
	<tr>
1028
		<td class="family">{{$fam}}</td>
1029

1030
		{{$n := index $.ActiveTraceCount $fam}}
1031
		<td class="active {{if not $n}}empty{{end}}">
1032
			{{if $n}}<a href="?fam={{$fam}}&b=-1{{if $.Expanded}}&exp=1{{end}}">{{end}}
1033
			[{{$n}} active]
1034
			{{if $n}}</a>{{end}}
1035
		</td>
1036

1037
		{{$f := index $.CompletedTraces $fam}}
1038
		{{range $i, $b := $f.Buckets}}
1039
		{{$empty := $b.Empty}}
1040
		<td {{if $empty}}class="empty"{{end}}>
1041
		{{if not $empty}}<a href="?fam={{$fam}}&b={{$i}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
1042
		[{{.Cond}}]
1043
		{{if not $empty}}</a>{{end}}
1044
		</td>
1045
		{{end}}
1046

1047
		{{$nb := len $f.Buckets}}
1048
		<td class="latency-first">
1049
		<a href="?fam={{$fam}}&b={{$nb}}">[minute]</a>
1050
		</td>
1051
		<td>
1052
		<a href="?fam={{$fam}}&b={{add $nb 1}}">[hour]</a>
1053
		</td>
1054
		<td>
1055
		<a href="?fam={{$fam}}&b={{add $nb 2}}">[total]</a>
1056
		</td>
1057

1058
	</tr>
1059
	{{end}}
1060
</table>
1061
{{end}} {{/* end of StatusTable */}}
1062

1063
{{define "Epilog"}}
1064
{{if $.Traces}}
1065
<hr />
1066
<h3>Family: {{$.Family}}</h3>
1067

1068
{{if or $.Expanded $.Traced}}
1069
  <a href="?fam={{$.Family}}&b={{$.Bucket}}">[Normal/Summary]</a>
1070
{{else}}
1071
  [Normal/Summary]
1072
{{end}}
1073

1074
{{if or (not $.Expanded) $.Traced}}
1075
  <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">[Normal/Expanded]</a>
1076
{{else}}
1077
  [Normal/Expanded]
1078
{{end}}
1079

1080
{{if not $.Active}}
1081
	{{if or $.Expanded (not $.Traced)}}
1082
	<a href="?fam={{$.Family}}&b={{$.Bucket}}&rtraced=1">[Traced/Summary]</a>
1083
	{{else}}
1084
	[Traced/Summary]
1085
	{{end}}
1086
	{{if or (not $.Expanded) (not $.Traced)}}
1087
	<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1&rtraced=1">[Traced/Expanded]</a>
1088
        {{else}}
1089
	[Traced/Expanded]
1090
	{{end}}
1091
{{end}}
1092

1093
{{if $.Total}}
1094
<p><em>Showing <b>{{len $.Traces}}</b> of <b>{{$.Total}}</b> traces.</em></p>
1095
{{end}}
1096

1097
<table id="reqs">
1098
	<caption>
1099
		{{if $.Active}}Active{{else}}Completed{{end}} Requests
1100
	</caption>
1101
	<tr><th>When</th><th>Elapsed&nbsp;(s)</th></tr>
1102
	{{range $tr := $.Traces}}
1103
	<tr class="first">
1104
		<td class="when">{{$tr.When}}</td>
1105
		<td class="elapsed">{{$tr.ElapsedTime}}</td>
1106
		<td>{{$tr.Title}}</td>
1107
		{{/* TODO: include traceID/spanID */}}
1108
	</tr>
1109
	{{if $.Expanded}}
1110
	{{range $tr.Events}}
1111
	<tr>
1112
		<td class="when">{{.WhenString}}</td>
1113
		<td class="elapsed">{{elapsed .Elapsed}}</td>
1114
		<td>{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}<em>[redacted]</em>{{end}}</td>
1115
	</tr>
1116
	{{end}}
1117
	{{end}}
1118
	{{end}}
1119
</table>
1120
{{end}} {{/* if $.Traces */}}
1121

1122
{{if $.Histogram}}
1123
<h4>Latency (&micro;s) of {{$.Family}} over {{$.HistogramWindow}}</h4>
1124
{{$.Histogram}}
1125
{{end}} {{/* if $.Histogram */}}
1126

1127
	</body>
1128
</html>
1129
{{end}} {{/* end of Epilog */}}
1130
`
1131

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.