forked from inigolabs/stopwatch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstopwatch.go
188 lines (156 loc) · 3.9 KB
/
stopwatch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package stopwatch
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"time"
)
// Timer is the interface for the stopwatch timer.
type Timer interface {
Start()
Step(label string)
Stop()
WriteResults(w io.Writer) error
ShowResults() error
GetResults() *Results
}
type Results struct {
Steps []Step `json:"steps"`
}
type Step struct {
Label string `json:"label"`
Duration time.Duration `json:"duration"`
}
type timer struct {
steps []*step
now func() time.Time
running bool
}
type step struct {
label string
startTime time.Time
endTime time.Time
}
// Start creates a profile timer and starts it.
func NewTimer() *timer {
t := &timer{
now: time.Now,
}
t.Start()
return t
}
// Start starts the stopwatch
func (t *timer) Start() {
if t.running {
panic("stopwatch already running")
}
t.running = true
t.steps = append(t.steps, &step{
startTime: t.now(),
})
}
// Stop stops the stopwatch
func (t *timer) Stop() {
if !t.running {
panic("stopwatch already stopped")
}
t.running = false
t.steps = t.steps[:len(t.steps)-1]
}
// Step is like a lap on a stopwatch, it records the amount of time since the
// the last step and marks this step with the given label
func (t *timer) Step(label string) {
if !t.running {
panic("stopwatch not running")
}
now := t.now()
lastStep := t.steps[len(t.steps)-1]
lastStep.label = label
lastStep.endTime = now
t.steps = append(t.steps, &step{
startTime: now,
})
}
// WriteResults writes the timer step results to the given writer.
func (t *timer) WriteResults(w io.Writer) error {
maxLabelLength := 0
for _, s := range t.steps {
if len(s.label) > maxLabelLength {
maxLabelLength = len(s.label)
}
}
fmtstr := fmt.Sprintf("%%-%ds : %%v\n", maxLabelLength)
bw := bufio.NewWriter(w)
longestLine := 0
totalDuration := time.Duration(0)
for i := 0; i < len(t.steps); i++ {
step := t.steps[i]
duration := step.endTime.Sub(step.startTime)
durationStr := durationMillisecondStr(duration)
length, err := bw.WriteString(fmt.Sprintf(fmtstr, step.label, durationStr))
if err != nil {
return err
}
if length > longestLine {
longestLine = length
}
totalDuration += duration
}
seperator := strings.Repeat("-", longestLine) + "\n"
_, err := bw.WriteString(seperator)
if err != nil {
return err
}
totalDurationStr := durationMillisecondStr(totalDuration)
_, err = bw.WriteString(fmt.Sprintf(fmtstr, "total", totalDurationStr))
if err != nil {
return err
}
err = bw.Flush()
if err != nil {
return err
}
return nil
}
// ShowResults outputs the timer step results to stdout.
func (t *timer) ShowResults() error {
return t.WriteResults(os.Stdout)
}
// GetResults returns the timer step results.
func (t *timer) GetResults() *Results {
results := &Results{}
for i := 0; i < len(t.steps); i++ {
step := t.steps[i]
duration := step.endTime.Sub(step.startTime)
results.Steps = append(results.Steps, Step{
Label: step.label,
Duration: duration,
})
}
return results
}
type noopTimer struct{}
// StartNoopTimer creates a timer that does nothing. This is useful in cases
// where you want to keep the timer step call in production code, but don't
// want them to have any logical or performance effects.
func StartNoopTimer() *noopTimer {
return &noopTimer{}
}
// Start for noop timer does nothing
func (t *noopTimer) Start() {}
// Stop for noop timer does nothing
func (t *noopTimer) Stop() {}
// Step for noop timer does nothing
func (t *noopTimer) Step(label string) {}
// WriteResults for noop timer does nothing
func (t *noopTimer) WriteResults(w io.Writer) error { return nil }
// ShowResults for noop timer does nothing
func (t *noopTimer) ShowResults() error { return nil }
// GetResults for noop timer does nothing
func (t *noopTimer) GetResults() *Results { return nil }
func durationMillisecondStr(d time.Duration) string {
ms := float64(d) / float64(time.Millisecond)
return fmt.Sprintf("%fms", ms)
}