This repository has been archived by the owner on Aug 12, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
logger_test.go
348 lines (272 loc) · 9.22 KB
/
logger_test.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package sawmill
import (
"fmt"
"runtime"
"sync"
"testing"
"time"
"github.com/phemmer/sawmill/event"
"github.com/phemmer/sawmill/handler/channel"
"github.com/phemmer/sawmill/handler/filter"
"github.com/stretchr/testify/assert"
)
func TestLoggerEvent(t *testing.T) {
handler := channel.NewHandler()
logger := NewLogger()
logger.AddHandler("TestEvent", handler)
defer logger.RemoveHandler("TestEvent", false)
logger.Event(InfoLevel, "TestEvent", nil)
logEvent := handler.Next(time.Second)
if assert.NotNil(t, logEvent) {
assert.Equal(t, logEvent.Message, "TestEvent")
}
}
// this makes sure that a removed handler processes no further events
func TestLoggerRemoveHandler(t *testing.T) {
handler := channel.NewHandler()
logger := NewLogger()
logger.AddHandler("TestEvent", handler)
logger.RemoveHandler("TestEvent", true)
logger.Sync(logger.Event(InfoLevel, "TestEvent"))
assert.Nil(t, handler.Next(time.Millisecond))
}
// check that removing a handler already removed doesn't error
func TestLoggerRemoveHandlerTwice(t *testing.T) {
handler := channel.NewHandler()
logger := NewLogger()
logger.AddHandler("TestEvent", handler)
logger.RemoveHandler("TestEvent", false)
logger.RemoveHandler("TestEvent", false)
}
// check that removing a handler waits for the handler to finish processing
func TestLoggerRemoveHandlerWait(t *testing.T) {
handler := channel.NewHandler()
logger := NewLogger()
logger.AddHandler("TestEvent", handler)
eventId1 := logger.Event(InfoLevel, "TestEvent")
// first confirm the event is sitting unprocessed
assert.Equal(t, logger.eventHandlerMap["TestEvent"].lastSentEventId, eventId1)
assert.NotEqual(t, logger.eventHandlerMap["TestEvent"].lastProcessedEventId, eventId1)
// send a second event, just so we have one that's not sitting on the channel.Handler channel
eventId2 := logger.Event(InfoLevel, "TestEvent")
assert.Equal(t, logger.eventHandlerMap["TestEvent"].lastSentEventId, eventId2)
assert.NotEqual(t, logger.eventHandlerMap["TestEvent"].lastProcessedEventId, eventId2)
logger.RemoveHandler("TestEvent", false)
event1 := handler.Next(time.Second)
assert.NotNil(t, event1)
assert.Equal(t, event1.Id, eventId1)
event2 := handler.Next(time.Second)
assert.NotNil(t, event2.Id, eventId2)
assert.Nil(t, handler.Next(time.Millisecond))
}
// check that adding a handler under the same name overrides the first
func TestLoggerAddDuplicateHandler(t *testing.T) {
logger := NewLogger()
handler1 := channel.NewHandler()
logger.AddHandler("TestEvent", handler1)
defer logger.RemoveHandler("TestEvent", false)
handler2 := channel.NewHandler()
logger.AddHandler("TestEvent", handler2)
logger.Event(InfoLevel, "TestEvent")
assert.Nil(t, handler1.Next(time.Millisecond))
assert.NotNil(t, handler2.Next(time.Millisecond))
}
func TestLoggerFilterHandler(t *testing.T) {
logger := NewLogger()
channelHandler := channel.NewHandler()
handler := logger.FilterHandler(channelHandler)
assert.IsType(t, &filter.FilterHandler{}, handler)
}
func TestLoggerHelpers(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
handler := channel.NewHandler()
logger.AddHandler("TestEvent", handler)
type testLoggerHelper struct {
String string
Func func(string, ...interface{}) uint64
Level event.Level
}
testHelpers := []testLoggerHelper{
{"Emergency", logger.Emergency, EmergencyLevel},
{"Alert", logger.Alert, AlertLevel},
{"Critical", logger.Critical, CriticalLevel},
{"Error", logger.Error, ErrorLevel},
{"Warning", logger.Warning, WarningLevel},
{"Notice", logger.Notice, NoticeLevel},
{"Info", logger.Info, InfoLevel},
{"Debug", logger.Debug, DebugLevel},
}
for _, helper := range testHelpers {
message := fmt.Sprintf("TestHelper %s", helper.String)
helper.Func(message, Fields{"helper": helper.String})
logEvent := handler.Next(time.Millisecond)
if assert.NotNil(t, logEvent) {
assert.Equal(t, logEvent.Message, message)
assert.Equal(t, logEvent.Level, helper.Level)
if assert.NotNil(t, logEvent.Fields) {
assert.Equal(t, logEvent.FlatFields["helper"], helper.String)
}
}
}
}
func TestLoggerFatal(t *testing.T) {
exitBkup := exit
var exitCode int
exit = func(code int) { exitCode = code }
defer func() { exit = exitBkup }()
logger := NewLogger()
defer logger.Stop()
handler := channel.NewHandler()
logger.AddHandler("TestEvent", handler)
// logger.Fatal performs a Stop(), which waits for the handler to process the event. So we have to process it or we deadlock. We do this by starting a goroutine
var logEvent *event.Event
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
logEvent = handler.Next(time.Second)
wg.Done()
}()
logger.Fatal("TestHelper Fatal", Fields{"helper": "Fatal"})
wg.Wait()
if assert.NotNil(t, logEvent) {
assert.Equal(t, logEvent.Message, "TestHelper Fatal")
if assert.NotNil(t, logEvent.Fields) {
assert.Equal(t, logEvent.FlatFields["helper"], "Fatal")
}
}
assert.Equal(t, exitCode, 1)
}
func TestLoggerCheckPanic(t *testing.T) {
var panicErr interface{}
defer func() {
panicErr = recover()
}()
logger := NewLogger()
handler := channel.NewHandler()
logger.AddHandler("TestLoggerCheckPanic", handler)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
// goroutine is because the channel.Handler is unbuffered, meaning the
// CheckPanic will block trying to write a log event until handler.Next() is
// called.
defer func() {
panicErr = recover()
wg.Done()
}()
defer logger.CheckPanic()
panic("this is a panic")
}()
logEvent := handler.Next(time.Second)
assert.Equal(t, "panic", logEvent.Message)
assert.Equal(t, "this is a panic", logEvent.FlatFields["error"])
// make sure the panic was passed through
wg.Wait()
assert.Equal(t, "this is a panic", panicErr)
}
// this test is a bit complicated because we have to deal with synchronizing 2 goroutines
// The idea is:
// * We generate an event
// * A goroutine calls logger.Sync() to wait for the handler to process it
// * The handler doesn't process it until we call handler.Next()
// * handler.Next() gives us the event which we store in a shared variable
// * The Sync() goroutine notifies the main goroutine that it has set the shared variable
// * The main goroutine checks that the shared variable matches the generated event
// Then we run it 100 times just to make sure there's no racyness (there shouldn't be, but we want to make sure)
func TestSync(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
handler := channel.NewHandler()
logger.AddHandler("TestEvent", handler)
var lastHandledEventId uint64
cond := sync.NewCond(&sync.Mutex{})
for i := 0; i < 100; i++ {
eventId := logger.Info("Test sync", Fields{"i": i})
go func() {
logger.Sync(eventId) // will block waiting for handler.Next() to be called
cond.L.Lock()
lastHandledEventId = eventId
cond.Broadcast()
cond.L.Unlock()
}()
runtime.Gosched()
// This is the main bit of possible racyness, which is the reason for runtime.Gosched() above
// It shouldn't be racy at all, but in case it is, try to catch it
cond.L.Lock()
assert.True(t, lastHandledEventId < eventId)
cond.L.Unlock()
cond.L.Lock()
logEvent := handler.Next(time.Millisecond) // this should cause the logger.Sync() to return
cond.Wait()
assert.Equal(t, lastHandledEventId, eventId)
assert.Equal(t, eventId, logEvent.Id)
cond.L.Unlock()
}
}
func TestSetGetSync(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
logger.SetSync(true)
assert.True(t, logger.GetSync())
logger.SetSync(false)
assert.False(t, logger.GetSync())
}
func TestSyncMode(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
handler := channel.NewHandler()
logger.AddHandler("TestEvent", handler)
logger.SetSync(true)
//TODO might be nicer to have something where we get a stack trace, start the
// operation, get another stack trace, find the new goroutine, and verify it's
// within a logger.Sync() call.
for i := 0; i < 100; i++ {
cond := sync.NewCond(&sync.Mutex{})
complete := false
go func() {
logger.Info("Test sync", Fields{"i": i})
cond.L.Lock()
complete = true
cond.Broadcast()
cond.L.Unlock()
}()
runtime.Gosched()
cond.L.Lock()
assert.False(t, complete)
cond.L.Unlock()
// unblock the .Info() call
cond.L.Lock()
handler.Next(time.Millisecond)
cond.Wait()
assert.True(t, complete)
cond.L.Unlock()
}
}
func TestSetStackMinLevel(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
handler := channel.NewHandler()
logger.AddHandler("TestSetStackMinLevel", handler)
logger.SetStackMinLevel(ErrorLevel)
logger.Warning("foo")
logEvent := handler.Next(time.Second)
assert.Empty(t, logEvent.Stack)
logger.Error("bar")
logEvent = handler.Next(time.Second)
assert.NotEmpty(t, logEvent.Stack)
// Unfortunately we can't (easily) test that the stack trace includes this
// test function as event.New looks for the first function outside
// the sawmill package, and we're testing inside the package.
// However it should at least include the testing package as that's what
// called us.
}
func TestGetStackMinLevel(t *testing.T) {
logger := NewLogger()
defer logger.Stop()
assert.True(t, logger.GetStackMinLevel() > EmergencyLevel)
logger.SetStackMinLevel(DebugLevel)
assert.Equal(t, DebugLevel, logger.GetStackMinLevel())
}
// Test dropping
// Test Stop()