-
Notifications
You must be signed in to change notification settings - Fork 0
/
buflogr.go
178 lines (152 loc) · 4.35 KB
/
buflogr.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
package buflogr
import (
"bytes"
"fmt"
"strings"
"sync"
"github.com/go-logr/logr"
)
const (
LevelError = "ERROR"
LevelInfo = "INFO"
LevelV = "V[%d]"
)
var (
// NameSeparator separates names for logr.WithName.
NameSeparator = "/"
// KVFormatter is a function that renders a slice of key/value pairs into a
// string with the signature: `func(kv ...interface{}) string`.
KVFormatter = defaultKVFormatter
)
var (
_ logr.LogSink = &bufLogger{}
_ Underlier = &bufLogger{}
)
// New returns a logr.Logger with logr.LogSink implemented by bufLogger using a
// new bytes.Buffer.
func New() logr.Logger {
return NewWithBuffer(nil)
}
// New returns a logr.Logger with logr.LogSink implemented by bufLogger with an
// existing bytes.Buffer.
func NewWithBuffer(b *bytes.Buffer) logr.Logger {
if b == nil {
b = &bytes.Buffer{}
}
bl := &bufLogger{
verbosity: 9,
buf: b,
}
return logr.New(bl)
}
// bufLogger implements the LogSink interface.
type bufLogger struct {
name string
values []interface{}
verbosity int
buf *bytes.Buffer // pointer so logged text are persisted across all invocations
mu sync.Mutex
}
// Init is not implemented and does not use any runtime info.
func (l *bufLogger) Init(info logr.RuntimeInfo) {
// not implemented
}
// Enabled implements logr.Logger.Enabled by checking if the current
// verbosity level is less or equal than the logger's maximum verbosity.
func (l *bufLogger) Enabled(level int) bool {
return level <= l.verbosity
}
// Info implements logr.Logger.Info by writing the line to the internal buffer.
func (l *bufLogger) Info(level int, msg string, kv ...interface{}) {
if l.Enabled(level) {
l.writeLine(l.levelString(level), msg, kv...)
}
}
// Error implements logr.Logger.Error by prefixing the line with "ERROR" and
// write it to the internal buffer.
func (l *bufLogger) Error(err error, msg string, kv ...interface{}) {
l.writeLine(fmt.Sprintf("%s %v", LevelError, err), msg, kv...)
}
// WithValues returns a new LogSink with additional key/value pairs.
// The new LogSink has a new sync.Mutex.
func (l *bufLogger) WithValues(kv ...interface{}) logr.LogSink {
return &bufLogger{
name: l.name,
values: append(l.values, kv...),
verbosity: l.verbosity,
buf: l.buf,
}
}
// WithName returns a new LogSink with the specified name, appended with
// NameSeparator if there is already a name.
// The new LogSink has a new sync.Mutex.
func (l *bufLogger) WithName(name string) logr.LogSink {
if l.name != "" {
name = l.name + NameSeparator + name
}
return &bufLogger{
name: name,
values: l.values,
verbosity: l.verbosity,
buf: l.buf,
}
}
func defaultKVFormatter(kv ...interface{}) string {
s := strings.Join(strings.Fields(fmt.Sprint(kv...)), " ")
s = strings.TrimPrefix(s, "[")
s = strings.TrimSuffix(s, "]")
return s
}
func (l *bufLogger) writeLine(level, msg string, kv ...interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
var line []string
fields := []string{level, l.name, msg, KVFormatter(l.values), KVFormatter(kv)}
for _, f := range fields {
if f != "" {
line = append(line, f)
}
}
l.buf.WriteString(strings.Join(line, " "))
if !strings.HasSuffix(line[len(line)-1], "\n") {
l.buf.WriteRune('\n')
}
}
func (l *bufLogger) levelString(level int) string {
if level > 0 {
return fmt.Sprintf(LevelV, level)
}
return LevelInfo
}
// Underlier exposes access to the underlying testing.T instance. Since
// callers only have a logr.Logger, they have to know which
// implementation is in use, so this interface is less of an
// abstraction and more of a way to test type conversion.
type Underlier interface {
GetUnderlying() *bufLogger
}
// GetUnderlying returns the bufLogger underneath this logSink.
func (l *bufLogger) GetUnderlying() *bufLogger {
return l
}
// Additional methods
// Buf returns the internal buffer.
// Wrap with Mutex().Lock() and Mutex().Unlock() when doing write calls to
// preserve the write order.
func (l *bufLogger) Buf() *bytes.Buffer {
return l.buf
}
// Mutex returns the sync.Mutex used to preserve the order of writes to the buffer.
func (l *bufLogger) Mutex() *sync.Mutex {
return &l.mu
}
// Reset clears the buffer, name, and values, as well as resets the verbosity to
// maximum (9).
func (l *bufLogger) Reset() {
l.mu.Lock()
defer l.mu.Unlock()
l.name = ""
l.values = nil
l.verbosity = 9
l.buf = &bytes.Buffer{}
}