forked from cirruslabs/echelon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple.go
111 lines (102 loc) · 3.47 KB
/
simple.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 renderers
import (
"fmt"
"github.com/roberChen/echelon"
"github.com/roberChen/echelon/renderers/internal/console"
"github.com/roberChen/echelon/terminal"
"github.com/roberChen/echelon/utils"
"io"
"strings"
"time"
)
// SimpleRenderer is a simple renderer with an io.Writer for output, a color for output
// color, and a map to save time stamps. The key of time stamps is the path of scope.
type SimpleRenderer struct {
out io.Writer
colors *terminal.ColorSchema
startTimes map[string]time.Time
}
// NewSimpleRenderer creates a simple renderer
func NewSimpleRenderer(out io.Writer, colors *terminal.ColorSchema) *SimpleRenderer {
if colors == nil {
colors = terminal.DefaultColorSchema()
}
_ = console.PrepareTerminalEnvironment()
return &SimpleRenderer{
out: out,
colors: colors,
startTimes: make(map[string]time.Time),
}
}
// RenderScopeStarted function of SimpleRenderer, it will start rendering an message of entry.
func (r SimpleRenderer) RenderScopeStarted(entry *echelon.LogScopeStarted) {
scopes := entry.GetScopes()
level := len(scopes)
if level == 0 {
return
}
timeKey := strings.Join(scopes, "/")
if _, ok := r.startTimes[timeKey]; ok {
// duplicate event
return
}
r.startTimes[timeKey] = time.Now()
lastScope := scopes[level-1]
message := terminal.GetColoredText(r.colors.NeutralColor, fmt.Sprintf("Started %s", quotedIfNeeded(lastScope)))
r.renderEntry(message)
}
// RenderScopeFinished will render a finished entry, which will print to task result of an entry.
func (r SimpleRenderer) RenderScopeFinished(entry *echelon.LogScopeFinished) {
scopes := entry.GetScopes()
level := len(scopes)
if level == 0 {
return
}
now := time.Now()
startTime := now
if t, ok := r.startTimes[strings.Join(scopes, "/")]; ok {
startTime = t
}
duration := now.Sub(startTime)
formatedDuration := utils.FormatDuration(duration, true)
lastScope := scopes[level-1]
if entry.Success() {
message := fmt.Sprintf("%s succeeded in %s!", quotedIfNeeded(lastScope), formatedDuration)
coloredMessage := terminal.GetColoredText(r.colors.SuccessColor, message)
r.renderEntry(coloredMessage)
} else {
message := fmt.Sprintf("%s failed in %s!", quotedIfNeeded(lastScope), formatedDuration)
coloredMessage := terminal.GetColoredText(r.colors.NeutralColor, message)
r.renderEntry(coloredMessage)
}
}
// RenderMessage will render message from entry for simple renderer, it sends message of
// entry to renderEntry of renderer.
func (r SimpleRenderer) RenderMessage(entry *echelon.LogEntryMessage) {
r.renderEntry(entry.GetMessage())
}
// RenderProcess function of SimpleRenderer, it will do nothing, simple renderer doesn't
// support process rendering
func (r SimpleRenderer) RenderProcess(entry *echelon.LogProcessMessage) {}
// renderEntry will render message of simple renderer, it directly output the message to io.Writer of SimpleRenderer
func (r SimpleRenderer) renderEntry(message string) {
_, _ = r.out.Write([]byte(message + "\n"))
}
// ScopeHasStarted returns whether the scope specified by path 'scpoes' has started. A finished scope is still
// started.
func (r SimpleRenderer) ScopeHasStarted(scopes []string) bool {
level := len(scopes)
if level == 0 {
return true
}
timeKey := strings.Join(scopes, "/")
_, result := r.startTimes[timeKey]
return result
}
// quotedIfNeeded will quotes string with ' if no ' or " appears in string
func quotedIfNeeded(s string) string {
if strings.ContainsAny(s, "'\"") {
return s
}
return "'" + s + "'"
}