/
logging.go
158 lines (132 loc) · 3.73 KB
/
logging.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
// Package logging implements the logger for the pack CLI.
package logging
import (
"fmt"
"io"
"io/ioutil"
"sync"
"time"
"github.com/apex/log"
"golang.org/x/crypto/ssh/terminal"
"github.com/buildpacks/pack/internal/style"
"github.com/buildpacks/pack/logging"
)
const (
errorLevelText = "ERROR: "
warnLevelText = "Warning: "
lineFeed = '\n'
// log level to use when quiet is true
quietLevel = log.WarnLevel
// log level to use when debug is true
verboseLevel = log.DebugLevel
// time format the out logging uses
timeFmt = "2006/01/02 15:04:05.000000"
// InvalidFileDescriptor based on https://golang.org/src/os/file_unix.go?s=2183:2210#L57
InvalidFileDescriptor = ^(uintptr(0))
)
// LogWithWriters is a logger used with the pack CLI, allowing users to print logs for various levels, including Info, Debug and Error
type LogWithWriters struct {
sync.Mutex
log.Logger
wantTime bool
clock func() time.Time
out io.Writer
errOut io.Writer
}
// NewLogWithWriters creates a logger to be used with pack CLI.
func NewLogWithWriters(stdout, stderr io.Writer, opts ...func(*LogWithWriters)) *LogWithWriters {
lw := &LogWithWriters{
Logger: log.Logger{
Level: log.InfoLevel,
},
wantTime: false,
clock: time.Now,
out: stdout,
errOut: stderr,
}
lw.Logger.Handler = lw
for _, opt := range opts {
opt(lw)
}
return lw
}
// WithClock is an option used to initialize a LogWithWriters with a given clock function
func WithClock(clock func() time.Time) func(writers *LogWithWriters) {
return func(logger *LogWithWriters) {
logger.clock = clock
}
}
// WithVerbose is an option used to initialize a LogWithWriters with Verbose turned on
func WithVerbose() func(writers *LogWithWriters) {
return func(logger *LogWithWriters) {
logger.Level = log.DebugLevel
}
}
// HandleLog handles log events, printing entries appropriately
func (lw *LogWithWriters) HandleLog(e *log.Entry) error {
lw.Lock()
defer lw.Unlock()
writer := lw.WriterForLevel(logging.Level(e.Level))
_, err := fmt.Fprint(writer, appendMissingLineFeed(fmt.Sprintf("%s%s", formatLevel(e.Level), e.Message)))
return err
}
// WriterForLevel returns a Writer for the given logging.Level
func (lw *LogWithWriters) WriterForLevel(level logging.Level) io.Writer {
if lw.Level > log.Level(level) {
return ioutil.Discard
}
if level == logging.ErrorLevel {
return NewLogWriter(lw.errOut, lw.clock, lw.wantTime)
}
return NewLogWriter(lw.out, lw.clock, lw.wantTime)
}
// Writer returns the base Writer for the LogWithWriters
func (lw *LogWithWriters) Writer() io.Writer {
return lw.out
}
// WantTime turns timestamps on in log entries
func (lw *LogWithWriters) WantTime(f bool) {
lw.wantTime = f
}
// WantQuiet reduces the number of logs returned
func (lw *LogWithWriters) WantQuiet(f bool) {
if f {
lw.Level = quietLevel
}
}
// WantVerbose increases the number of logs returned
func (lw *LogWithWriters) WantVerbose(f bool) {
if f {
lw.Level = verboseLevel
}
}
// IsVerbose returns whether verbose logging is on
func (lw *LogWithWriters) IsVerbose() bool {
return lw.Level == log.DebugLevel
}
// IsTerminal returns whether a writer is a terminal
func IsTerminal(w io.Writer) (uintptr, bool) {
if f, ok := w.(hasDescriptor); ok {
termFd := f.Fd()
isTerm := terminal.IsTerminal(int(termFd))
return termFd, isTerm
}
return InvalidFileDescriptor, false
}
func formatLevel(ll log.Level) string {
switch ll {
case log.ErrorLevel:
return style.Error(errorLevelText)
case log.WarnLevel:
return style.Warn(warnLevelText)
}
return ""
}
// Preserve behavior of other loggers
func appendMissingLineFeed(msg string) string {
buff := []byte(msg)
if len(buff) == 0 || buff[len(buff)-1] != lineFeed {
buff = append(buff, lineFeed)
}
return string(buff)
}