...
1 package debug
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "sync"
9 "sync/atomic"
10 "time"
11 )
12
13
14
15
16 type Debug struct {
17 mutex sync.Mutex
18 timers map[string]*Timer
19 values map[string]*Value
20
21 clock ClockFunc
22 }
23
24
25 type Value atomic.Value
26
27 func (v *Value) MarshalJSON() ([]byte, error) {
28 return json.MarshalIndent((*atomic.Value)(v).Load(), "", " ")
29 }
30
31
32 var root = NewDebug()
33
34
35 var key = &struct{}{}
36
37
38 func NewContext(parent context.Context, debug *Debug) context.Context {
39 return context.WithValue(parent, key, debug)
40 }
41
42
43 func FromContext(ctx context.Context) (result *Debug) {
44 value := ctx.Value(key)
45 if value != nil {
46 result = value.(*Debug)
47 } else {
48 result = root
49 }
50 return
51 }
52
53
54 func NewDebug() *Debug {
55 return NewDebugWithClock(time.Now)
56 }
57
58
59 func NewDebugWithClock(clock ClockFunc) *Debug {
60 return &Debug{clock: clock, timers: map[string]*Timer{}, values: map[string]*Value{}}
61 }
62
63
64 func (d *Debug) withMutex(f func()) {
65 d.mutex.Lock()
66 defer d.mutex.Unlock()
67 f()
68 }
69
70
71 func (d *Debug) Timer(name string) (result *Timer) {
72 d.withMutex(func() {
73 var ok bool
74 result, ok = d.timers[name]
75 if !ok {
76 result = NewTimerWithClock(d.clock)
77 d.timers[name] = result
78 }
79 })
80 return
81 }
82
83
84 func (d *Debug) Value(name string) (result *atomic.Value) {
85 d.withMutex(func() {
86 r, ok := d.values[name]
87 if !ok {
88 r = &Value{}
89 d.values[name] = r
90 }
91 result = (*atomic.Value)(r)
92 })
93 return
94 }
95
96
97 func (d *Debug) ServeHTTP(w http.ResponseWriter, r *http.Request) {
98 d.withMutex(func() {
99 bytes, err := json.MarshalIndent(map[string]interface{}{
100 "timers": d.timers,
101 "values": d.values,
102 }, "", " ")
103
104 if err != nil {
105 http.Error(w, fmt.Sprintf("error marshalling debug info: %v", err), http.StatusInternalServerError)
106 } else {
107 _, _ = w.Write(append(bytes, '\n'))
108 }
109 })
110 }
111
View as plain text