-
Notifications
You must be signed in to change notification settings - Fork 288
/
pipeline_state.go
128 lines (105 loc) · 3.27 KB
/
pipeline_state.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
package build
import (
"context"
"fmt"
"strings"
"time"
"github.com/tilt-dev/tilt/pkg/logger"
)
type PipelineState struct {
totalPipelineStepCount int
curBuildStep int
curPipelineStart time.Time
pipelineSteps []PipelineStep
c Clock
}
type PipelineStep struct {
Name string // for logging
StartTime time.Time
Duration time.Duration // not populated until end of the step
}
type Clock interface {
Now() time.Time
}
type realClock struct{}
func (realClock) Now() time.Time { return time.Now() }
func ProvideClock() Clock {
return realClock{}
}
const buildStepOutputPrefix = " "
func NewPipelineState(ctx context.Context, totalStepCount int, c Clock) *PipelineState {
return &PipelineState{
totalPipelineStepCount: totalStepCount,
pipelineSteps: []PipelineStep{},
curPipelineStart: c.Now(),
c: c,
}
}
// NOTE(maia): this func should always be deferred in a closure, so that the `err` arg
// is bound at the time of calling rather than at the time of deferring. I.e., do:
// defer func() { ps.End(ctx, err) }()
// and NOT:
// defer ps.End(ctx, err)
func (ps *PipelineState) End(ctx context.Context, err error) {
ps.curBuildStep = 0
if err != nil {
return
}
l := logger.Get(ctx)
elapsed := ps.c.Now().Sub(ps.curPipelineStart)
for i, step := range ps.pipelineSteps {
l.Infof("%sStep %d - %.2fs (%s)", buildStepOutputPrefix, i+1, step.Duration.Seconds(), step.Name)
}
t := logger.Blue(l).Sprintf("%.2fs", elapsed.Seconds())
l.Infof("%sDONE IN: %s \n", buildStepOutputPrefix, t)
}
func (ps *PipelineState) curPipelineIndex() int {
// human-readable i.e. 1-indexed
return len(ps.pipelineSteps)
}
func (ps *PipelineState) curPipelineStep() PipelineStep {
if len(ps.pipelineSteps) == 0 {
return PipelineStep{}
}
return ps.pipelineSteps[len(ps.pipelineSteps)-1]
}
func (ps *PipelineState) StartPipelineStep(ctx context.Context, format string, a ...interface{}) {
l := logger.Get(ctx)
stepName := fmt.Sprintf(format, a...)
ps.pipelineSteps = append(ps.pipelineSteps, PipelineStep{
Name: stepName,
StartTime: ps.c.Now(),
})
line := logger.Blue(l).Sprintf("STEP %d/%d", ps.curPipelineIndex(), ps.totalPipelineStepCount)
l.Infof("%s — %s", line, stepName)
ps.curBuildStep = 1
}
func (ps *PipelineState) EndPipelineStep(ctx context.Context) {
elapsed := ps.c.Now().Sub(ps.curPipelineStep().StartTime)
logger.Get(ctx).Infof("")
ps.pipelineSteps[len(ps.pipelineSteps)-1].Duration = elapsed
}
func (ps *PipelineState) StartBuildStep(ctx context.Context, format string, a ...interface{}) {
l := logger.Get(ctx)
l.Infof("%s%s", buildStepOutputPrefix, fmt.Sprintf(format, a...))
ps.curBuildStep++
}
func (ps *PipelineState) Printf(ctx context.Context, format string, a ...interface{}) {
l := logger.Get(ctx)
if ps.curBuildStep == 0 {
l.Infof(format, a...)
} else {
message := fmt.Sprintf(format, a...)
message = strings.Replace(message, "\n", "\n"+buildStepOutputPrefix, -1)
l.Infof("%s%s", buildStepOutputPrefix, message)
}
}
func (ps *PipelineState) AttachLogger(ctx context.Context) context.Context {
l := logger.Get(ctx)
if ps.curBuildStep == 0 {
return ctx
} else {
return logger.WithLogger(ctx,
logger.NewPrefixedLogger(buildStepOutputPrefix, l))
}
}