1package level_test
2
3import (
4	"bytes"
5	"errors"
6	"io"
7	"strings"
8	"testing"
9
10	"github.com/go-kit/kit/log"
11	"github.com/go-kit/kit/log/level"
12)
13
14func TestVariousLevels(t *testing.T) {
15	testCases := []struct {
16		name    string
17		allowed level.Option
18		want    string
19	}{
20		{
21			"AllowAll",
22			level.AllowAll(),
23			strings.Join([]string{
24				`{"level":"debug","this is":"debug log"}`,
25				`{"level":"info","this is":"info log"}`,
26				`{"level":"warn","this is":"warn log"}`,
27				`{"level":"error","this is":"error log"}`,
28			}, "\n"),
29		},
30		{
31			"AllowDebug",
32			level.AllowDebug(),
33			strings.Join([]string{
34				`{"level":"debug","this is":"debug log"}`,
35				`{"level":"info","this is":"info log"}`,
36				`{"level":"warn","this is":"warn log"}`,
37				`{"level":"error","this is":"error log"}`,
38			}, "\n"),
39		},
40		{
41			"AllowInfo",
42			level.AllowInfo(),
43			strings.Join([]string{
44				`{"level":"info","this is":"info log"}`,
45				`{"level":"warn","this is":"warn log"}`,
46				`{"level":"error","this is":"error log"}`,
47			}, "\n"),
48		},
49		{
50			"AllowWarn",
51			level.AllowWarn(),
52			strings.Join([]string{
53				`{"level":"warn","this is":"warn log"}`,
54				`{"level":"error","this is":"error log"}`,
55			}, "\n"),
56		},
57		{
58			"AllowError",
59			level.AllowError(),
60			strings.Join([]string{
61				`{"level":"error","this is":"error log"}`,
62			}, "\n"),
63		},
64		{
65			"AllowNone",
66			level.AllowNone(),
67			``,
68		},
69	}
70
71	for _, tc := range testCases {
72		t.Run(tc.name, func(t *testing.T) {
73			var buf bytes.Buffer
74			logger := level.NewFilter(log.NewJSONLogger(&buf), tc.allowed)
75
76			level.Debug(logger).Log("this is", "debug log")
77			level.Info(logger).Log("this is", "info log")
78			level.Warn(logger).Log("this is", "warn log")
79			level.Error(logger).Log("this is", "error log")
80
81			if want, have := tc.want, strings.TrimSpace(buf.String()); want != have {
82				t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
83			}
84		})
85	}
86}
87
88func TestErrNotAllowed(t *testing.T) {
89	myError := errors.New("squelched!")
90	opts := []level.Option{
91		level.AllowWarn(),
92		level.ErrNotAllowed(myError),
93	}
94	logger := level.NewFilter(log.NewNopLogger(), opts...)
95
96	if want, have := myError, level.Info(logger).Log("foo", "bar"); want != have {
97		t.Errorf("want %#+v, have %#+v", want, have)
98	}
99
100	if want, have := error(nil), level.Warn(logger).Log("foo", "bar"); want != have {
101		t.Errorf("want %#+v, have %#+v", want, have)
102	}
103}
104
105func TestErrNoLevel(t *testing.T) {
106	myError := errors.New("no level specified")
107
108	var buf bytes.Buffer
109	opts := []level.Option{
110		level.SquelchNoLevel(true),
111		level.ErrNoLevel(myError),
112	}
113	logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
114
115	if want, have := myError, logger.Log("foo", "bar"); want != have {
116		t.Errorf("want %v, have %v", want, have)
117	}
118	if want, have := ``, strings.TrimSpace(buf.String()); want != have {
119		t.Errorf("\nwant '%s'\nhave '%s'", want, have)
120	}
121}
122
123func TestAllowNoLevel(t *testing.T) {
124	var buf bytes.Buffer
125	opts := []level.Option{
126		level.SquelchNoLevel(false),
127		level.ErrNoLevel(errors.New("I should never be returned!")),
128	}
129	logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
130
131	if want, have := error(nil), logger.Log("foo", "bar"); want != have {
132		t.Errorf("want %v, have %v", want, have)
133	}
134	if want, have := `{"foo":"bar"}`, strings.TrimSpace(buf.String()); want != have {
135		t.Errorf("\nwant '%s'\nhave '%s'", want, have)
136	}
137}
138
139func TestLevelContext(t *testing.T) {
140	var buf bytes.Buffer
141
142	// Wrapping the level logger with a context allows users to use
143	// log.DefaultCaller as per normal.
144	var logger log.Logger
145	logger = log.NewLogfmtLogger(&buf)
146	logger = level.NewFilter(logger, level.AllowAll())
147	logger = log.With(logger, "caller", log.DefaultCaller)
148
149	level.Info(logger).Log("foo", "bar")
150	if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have {
151		t.Errorf("\nwant '%s'\nhave '%s'", want, have)
152	}
153}
154
155func TestContextLevel(t *testing.T) {
156	var buf bytes.Buffer
157
158	// Wrapping a context with the level logger still works, but requires users
159	// to specify a higher callstack depth value.
160	var logger log.Logger
161	logger = log.NewLogfmtLogger(&buf)
162	logger = log.With(logger, "caller", log.Caller(5))
163	logger = level.NewFilter(logger, level.AllowAll())
164
165	level.Info(logger).Log("foo", "bar")
166	if want, have := `caller=level_test.go:165 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have {
167		t.Errorf("\nwant '%s'\nhave '%s'", want, have)
168	}
169}
170
171func TestLevelFormatting(t *testing.T) {
172	testCases := []struct {
173		name   string
174		format func(io.Writer) log.Logger
175		output string
176	}{
177		{
178			name:   "logfmt",
179			format: log.NewLogfmtLogger,
180			output: `level=info foo=bar`,
181		},
182		{
183			name:   "JSON",
184			format: log.NewJSONLogger,
185			output: `{"foo":"bar","level":"info"}`,
186		},
187	}
188
189	for _, tc := range testCases {
190		t.Run(tc.name, func(t *testing.T) {
191			var buf bytes.Buffer
192
193			logger := tc.format(&buf)
194			level.Info(logger).Log("foo", "bar")
195			if want, have := tc.output, strings.TrimSpace(buf.String()); want != have {
196				t.Errorf("\nwant: '%s'\nhave '%s'", want, have)
197			}
198		})
199	}
200}
201
202func TestInjector(t *testing.T) {
203	var (
204		output []interface{}
205		logger log.Logger
206	)
207
208	logger = log.LoggerFunc(func(keyvals ...interface{}) error {
209		output = keyvals
210		return nil
211	})
212	logger = level.NewInjector(logger, level.InfoValue())
213
214	logger.Log("foo", "bar")
215	if got, want := len(output), 4; got != want {
216		t.Errorf("missing level not injected: got len==%d, want len==%d", got, want)
217	}
218	if got, want := output[0], level.Key(); got != want {
219		t.Errorf("wrong level key: got %#v, want %#v", got, want)
220	}
221	if got, want := output[1], level.InfoValue(); got != want {
222		t.Errorf("wrong level value: got %#v, want %#v", got, want)
223	}
224
225	level.Error(logger).Log("foo", "bar")
226	if got, want := len(output), 4; got != want {
227		t.Errorf("leveled record modified: got len==%d, want len==%d", got, want)
228	}
229	if got, want := output[0], level.Key(); got != want {
230		t.Errorf("wrong level key: got %#v, want %#v", got, want)
231	}
232	if got, want := output[1], level.ErrorValue(); got != want {
233		t.Errorf("wrong level value: got %#v, want %#v", got, want)
234	}
235}
236