-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprofiletimer.go
111 lines (95 loc) · 2.69 KB
/
profiletimer.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
package profiletimer
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"time"
)
// Timer is the interface for profile or stopwatch timer.
type Timer interface {
Step(label string)
WriteResults(w io.Writer) error
ShowResults() error
}
type profileTimer struct {
steps []*step
}
type step struct {
label string
time time.Time
}
// StartProfileTimer creates a profile timer and starts it.
func StartProfileTimer() *profileTimer {
t := &profileTimer{}
t.steps = append(t.steps, &step{"init", time.Now()})
return t
}
// 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 *profileTimer) Step(label string) {
t.steps = append(t.steps, &step{label, time.Now()})
}
// WriteResults writes the timer step results to the given writer.
func (t *profileTimer) 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
for i := 1; i < len(t.steps); i++ {
step := t.steps[i]
duration := step.time.Sub(t.steps[i-1].time)
durationStr := durationMillisecondStr(duration)
length, err := bw.WriteString(fmt.Sprintf(fmtstr, step.label, durationStr))
if err != nil {
return err
}
if length > longestLine {
longestLine = length
}
}
seperator := strings.Repeat("-", longestLine) + "\n"
_, err := bw.WriteString(seperator)
if err != nil {
return err
}
lastStep := t.steps[len(t.steps)-1]
totalDuration := lastStep.time.Sub(t.steps[0].time)
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 *profileTimer) ShowResults() error {
return t.WriteResults(os.Stdout)
}
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{}
}
// 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 }
func durationMillisecondStr(d time.Duration) string {
ms := float64(d) / float64(time.Millisecond)
return fmt.Sprintf("%fms", ms)
}