1package zerolog
2
3import (
4	"bytes"
5	"io/ioutil"
6	"testing"
7)
8
9var (
10	levelNameHook = HookFunc(func(e *Event, level Level, msg string) {
11		levelName := level.String()
12		if level == NoLevel {
13			levelName = "nolevel"
14		}
15		e.Str("level_name", levelName)
16	})
17	simpleHook = HookFunc(func(e *Event, level Level, msg string) {
18		e.Bool("has_level", level != NoLevel)
19		e.Str("test", "logged")
20	})
21	copyHook = HookFunc(func(e *Event, level Level, msg string) {
22		hasLevel := level != NoLevel
23		e.Bool("copy_has_level", hasLevel)
24		if hasLevel {
25			e.Str("copy_level", level.String())
26		}
27		e.Str("copy_msg", msg)
28	})
29	nopHook = HookFunc(func(e *Event, level Level, message string) {
30	})
31	discardHook = HookFunc(func(e *Event, level Level, message string) {
32		e.Discard()
33	})
34)
35
36func TestHook(t *testing.T) {
37	tests := []struct {
38		name string
39		want string
40		test func(log Logger)
41	}{
42		{"Message", `{"level_name":"nolevel","message":"test message"}` + "\n", func(log Logger) {
43			log = log.Hook(levelNameHook)
44			log.Log().Msg("test message")
45		}},
46		{"NoLevel", `{"level_name":"nolevel"}` + "\n", func(log Logger) {
47			log = log.Hook(levelNameHook)
48			log.Log().Msg("")
49		}},
50		{"Print", `{"level":"debug","level_name":"debug"}` + "\n", func(log Logger) {
51			log = log.Hook(levelNameHook)
52			log.Print("")
53		}},
54		{"Error", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
55			log = log.Hook(levelNameHook)
56			log.Error().Msg("")
57		}},
58		{"Copy/1", `{"copy_has_level":false,"copy_msg":""}` + "\n", func(log Logger) {
59			log = log.Hook(copyHook)
60			log.Log().Msg("")
61		}},
62		{"Copy/2", `{"level":"info","copy_has_level":true,"copy_level":"info","copy_msg":"a message","message":"a message"}` + "\n", func(log Logger) {
63			log = log.Hook(copyHook)
64			log.Info().Msg("a message")
65		}},
66		{"Multi", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
67			log = log.Hook(levelNameHook).Hook(simpleHook)
68			log.Error().Msg("")
69		}},
70		{"Multi/Message", `{"level":"error","level_name":"error","has_level":true,"test":"logged","message":"a message"}` + "\n", func(log Logger) {
71			log = log.Hook(levelNameHook).Hook(simpleHook)
72			log.Error().Msg("a message")
73		}},
74		{"Output/single/pre", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
75			ignored := &bytes.Buffer{}
76			log = New(ignored).Hook(levelNameHook).Output(log.w)
77			log.Error().Msg("")
78		}},
79		{"Output/single/post", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
80			ignored := &bytes.Buffer{}
81			log = New(ignored).Output(log.w).Hook(levelNameHook)
82			log.Error().Msg("")
83		}},
84		{"Output/multi/pre", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
85			ignored := &bytes.Buffer{}
86			log = New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(log.w)
87			log.Error().Msg("")
88		}},
89		{"Output/multi/post", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
90			ignored := &bytes.Buffer{}
91			log = New(ignored).Output(log.w).Hook(levelNameHook).Hook(simpleHook)
92			log.Error().Msg("")
93		}},
94		{"Output/mixed", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
95			ignored := &bytes.Buffer{}
96			log = New(ignored).Hook(levelNameHook).Output(log.w).Hook(simpleHook)
97			log.Error().Msg("")
98		}},
99		{"With/single/pre", `{"level":"error","with":"pre","level_name":"error"}` + "\n", func(log Logger) {
100			log = log.Hook(levelNameHook).With().Str("with", "pre").Logger()
101			log.Error().Msg("")
102		}},
103		{"With/single/post", `{"level":"error","with":"post","level_name":"error"}` + "\n", func(log Logger) {
104			log = log.With().Str("with", "post").Logger().Hook(levelNameHook)
105			log.Error().Msg("")
106		}},
107		{"With/multi/pre", `{"level":"error","with":"pre","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
108			log = log.Hook(levelNameHook).Hook(simpleHook).With().Str("with", "pre").Logger()
109			log.Error().Msg("")
110		}},
111		{"With/multi/post", `{"level":"error","with":"post","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
112			log = log.With().Str("with", "post").Logger().Hook(levelNameHook).Hook(simpleHook)
113			log.Error().Msg("")
114		}},
115		{"With/mixed", `{"level":"error","with":"mixed","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
116			log = log.Hook(levelNameHook).With().Str("with", "mixed").Logger().Hook(simpleHook)
117			log.Error().Msg("")
118		}},
119		{"Discard", "", func(log Logger) {
120			log = log.Hook(discardHook)
121			log.Log().Msg("test message")
122		}},
123		{"None", `{"level":"error"}` + "\n", func(log Logger) {
124			log.Error().Msg("")
125		}},
126	}
127	for _, tt := range tests {
128		tt := tt
129		t.Run(tt.name, func(t *testing.T) {
130			out := &bytes.Buffer{}
131			log := New(out)
132			tt.test(log)
133			if got, want := decodeIfBinaryToString(out.Bytes()), tt.want; got != want {
134				t.Errorf("invalid log output:\ngot:  %v\nwant: %v", got, want)
135			}
136		})
137	}
138}
139
140func BenchmarkHooks(b *testing.B) {
141	logger := New(ioutil.Discard)
142	b.ResetTimer()
143	b.Run("Nop/Single", func(b *testing.B) {
144		log := logger.Hook(nopHook)
145		b.RunParallel(func(pb *testing.PB) {
146			for pb.Next() {
147				log.Log().Msg("")
148			}
149		})
150	})
151	b.Run("Nop/Multi", func(b *testing.B) {
152		log := logger.Hook(nopHook).Hook(nopHook)
153		b.RunParallel(func(pb *testing.PB) {
154			for pb.Next() {
155				log.Log().Msg("")
156			}
157		})
158	})
159	b.Run("Simple", func(b *testing.B) {
160		log := logger.Hook(simpleHook)
161		b.RunParallel(func(pb *testing.PB) {
162			for pb.Next() {
163				log.Log().Msg("")
164			}
165		})
166	})
167}
168