1// Copyright (c) 2016 Uber Technologies, Inc.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21package zap_test
22
23import (
24	"encoding/json"
25	"io/ioutil"
26	"log"
27	"os"
28	"time"
29
30	"go.uber.org/zap"
31	"go.uber.org/zap/zapcore"
32)
33
34func Example_presets() {
35	// Using zap's preset constructors is the simplest way to get a feel for the
36	// package, but they don't allow much customization.
37	logger := zap.NewExample() // or NewProduction, or NewDevelopment
38	defer logger.Sync()
39
40	const url = "http://example.com"
41
42	// In most circumstances, use the SugaredLogger. It's 4-10x faster than most
43	// other structured logging packages and has a familiar, loosely-typed API.
44	sugar := logger.Sugar()
45	sugar.Infow("Failed to fetch URL.",
46		// Structured context as loosely typed key-value pairs.
47		"url", url,
48		"attempt", 3,
49		"backoff", time.Second,
50	)
51	sugar.Infof("Failed to fetch URL: %s", url)
52
53	// In the unusual situations where every microsecond matters, use the
54	// Logger. It's even faster than the SugaredLogger, but only supports
55	// structured logging.
56	logger.Info("Failed to fetch URL.",
57		// Structured context as strongly typed fields.
58		zap.String("url", url),
59		zap.Int("attempt", 3),
60		zap.Duration("backoff", time.Second),
61	)
62	// Output:
63	// {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
64	// {"level":"info","msg":"Failed to fetch URL: http://example.com"}
65	// {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
66}
67
68func Example_basicConfiguration() {
69	// For some users, the presets offered by the NewProduction, NewDevelopment,
70	// and NewExample constructors won't be appropriate. For most of those
71	// users, the bundled Config struct offers the right balance of flexibility
72	// and convenience. (For more complex needs, see the AdvancedConfiguration
73	// example.)
74	//
75	// See the documentation for Config and zapcore.EncoderConfig for all the
76	// available options.
77	rawJSON := []byte(`{
78	  "level": "debug",
79	  "encoding": "json",
80	  "outputPaths": ["stdout", "/tmp/logs"],
81	  "errorOutputPaths": ["stderr"],
82	  "initialFields": {"foo": "bar"},
83	  "encoderConfig": {
84	    "messageKey": "message",
85	    "levelKey": "level",
86	    "levelEncoder": "lowercase"
87	  }
88	}`)
89
90	var cfg zap.Config
91	if err := json.Unmarshal(rawJSON, &cfg); err != nil {
92		panic(err)
93	}
94	logger, err := cfg.Build()
95	if err != nil {
96		panic(err)
97	}
98	defer logger.Sync()
99
100	logger.Info("logger construction succeeded")
101	// Output:
102	// {"level":"info","message":"logger construction succeeded","foo":"bar"}
103}
104
105func Example_advancedConfiguration() {
106	// The bundled Config struct only supports the most common configuration
107	// options. More complex needs, like splitting logs between multiple files
108	// or writing to non-file outputs, require use of the zapcore package.
109	//
110	// In this example, imagine we're both sending our logs to Kafka and writing
111	// them to the console. We'd like to encode the console output and the Kafka
112	// topics differently, and we'd also like special treatment for
113	// high-priority logs.
114
115	// First, define our level-handling logic.
116	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
117		return lvl >= zapcore.ErrorLevel
118	})
119	lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
120		return lvl < zapcore.ErrorLevel
121	})
122
123	// Assume that we have clients for two Kafka topics. The clients implement
124	// zapcore.WriteSyncer and are safe for concurrent use. (If they only
125	// implement io.Writer, we can use zapcore.AddSync to add a no-op Sync
126	// method. If they're not safe for concurrent use, we can add a protecting
127	// mutex with zapcore.Lock.)
128	topicDebugging := zapcore.AddSync(ioutil.Discard)
129	topicErrors := zapcore.AddSync(ioutil.Discard)
130
131	// High-priority output should also go to standard error, and low-priority
132	// output should also go to standard out.
133	consoleDebugging := zapcore.Lock(os.Stdout)
134	consoleErrors := zapcore.Lock(os.Stderr)
135
136	// Optimize the Kafka output for machine consumption and the console output
137	// for human operators.
138	kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
139	consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
140
141	// Join the outputs, encoders, and level-handling functions into
142	// zapcore.Cores, then tee the four cores together.
143	core := zapcore.NewTee(
144		zapcore.NewCore(kafkaEncoder, topicErrors, highPriority),
145		zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
146		zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),
147		zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
148	)
149
150	// From a zapcore.Core, it's easy to construct a Logger.
151	logger := zap.New(core)
152	defer logger.Sync()
153	logger.Info("constructed a logger")
154}
155
156func ExampleNamespace() {
157	logger := zap.NewExample()
158	defer logger.Sync()
159
160	logger.With(
161		zap.Namespace("metrics"),
162		zap.Int("counter", 1),
163	).Info("tracked some metrics")
164	// Output:
165	// {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}}
166}
167
168func ExampleNewStdLog() {
169	logger := zap.NewExample()
170	defer logger.Sync()
171
172	std := zap.NewStdLog(logger)
173	std.Print("standard logger wrapper")
174	// Output:
175	// {"level":"info","msg":"standard logger wrapper"}
176}
177
178func ExampleRedirectStdLog() {
179	logger := zap.NewExample()
180	defer logger.Sync()
181
182	undo := zap.RedirectStdLog(logger)
183	defer undo()
184
185	log.Print("redirected standard library")
186	// Output:
187	// {"level":"info","msg":"redirected standard library"}
188}
189
190func ExampleReplaceGlobals() {
191	logger := zap.NewExample()
192	defer logger.Sync()
193
194	undo := zap.ReplaceGlobals(logger)
195	defer undo()
196
197	zap.L().Info("replaced zap's global loggers")
198	// Output:
199	// {"level":"info","msg":"replaced zap's global loggers"}
200}
201
202func ExampleAtomicLevel() {
203	atom := zap.NewAtomicLevel()
204
205	// To keep the example deterministic, disable timestamps in the output.
206	encoderCfg := zap.NewProductionEncoderConfig()
207	encoderCfg.TimeKey = ""
208
209	logger := zap.New(zapcore.NewCore(
210		zapcore.NewJSONEncoder(encoderCfg),
211		zapcore.Lock(os.Stdout),
212		atom,
213	))
214	defer logger.Sync()
215
216	logger.Info("info logging enabled")
217
218	atom.SetLevel(zap.ErrorLevel)
219	logger.Info("info logging disabled")
220	// Output:
221	// {"level":"info","msg":"info logging enabled"}
222}
223
224func ExampleAtomicLevel_config() {
225	// The zap.Config struct includes an AtomicLevel. To use it, keep a
226	// reference to the Config.
227	rawJSON := []byte(`{
228		"level": "info",
229		"outputPaths": ["stdout"],
230		"errorOutputPaths": ["stderr"],
231		"encoding": "json",
232		"encoderConfig": {
233			"messageKey": "message",
234			"levelKey": "level",
235			"levelEncoder": "lowercase"
236		}
237	}`)
238	var cfg zap.Config
239	if err := json.Unmarshal(rawJSON, &cfg); err != nil {
240		panic(err)
241	}
242	logger, err := cfg.Build()
243	if err != nil {
244		panic(err)
245	}
246	defer logger.Sync()
247
248	logger.Info("info logging enabled")
249
250	cfg.Level.SetLevel(zap.ErrorLevel)
251	logger.Info("info logging disabled")
252	// Output:
253	// {"level":"info","message":"info logging enabled"}
254}
255
256func ExampleLogger_Check() {
257	logger := zap.NewExample()
258	defer logger.Sync()
259
260	if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil {
261		// If debug-level log output isn't enabled or if zap's sampling would have
262		// dropped this log entry, we don't allocate the slice that holds these
263		// fields.
264		ce.Write(
265			zap.String("foo", "bar"),
266			zap.String("baz", "quux"),
267		)
268	}
269
270	// Output:
271	// {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"}
272}
273
274func ExampleLogger_Named() {
275	logger := zap.NewExample()
276	defer logger.Sync()
277
278	// By default, Loggers are unnamed.
279	logger.Info("no name")
280
281	// The first call to Named sets the Logger name.
282	main := logger.Named("main")
283	main.Info("main logger")
284
285	// Additional calls to Named create a period-separated path.
286	main.Named("subpackage").Info("sub-logger")
287	// Output:
288	// {"level":"info","msg":"no name"}
289	// {"level":"info","logger":"main","msg":"main logger"}
290	// {"level":"info","logger":"main.subpackage","msg":"sub-logger"}
291}
292
293func ExampleWrapCore_replace() {
294	// Replacing a Logger's core can alter fundamental behaviors.
295	// For example, it can convert a Logger to a no-op.
296	nop := zap.WrapCore(func(zapcore.Core) zapcore.Core {
297		return zapcore.NewNopCore()
298	})
299
300	logger := zap.NewExample()
301	defer logger.Sync()
302
303	logger.Info("working")
304	logger.WithOptions(nop).Info("no-op")
305	logger.Info("original logger still works")
306	// Output:
307	// {"level":"info","msg":"working"}
308	// {"level":"info","msg":"original logger still works"}
309}
310
311func ExampleWrapCore_wrap() {
312	// Wrapping a Logger's core can extend its functionality. As a trivial
313	// example, it can double-write all logs.
314	doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
315		return zapcore.NewTee(c, c)
316	})
317
318	logger := zap.NewExample()
319	defer logger.Sync()
320
321	logger.Info("single")
322	logger.WithOptions(doubled).Info("doubled")
323	// Output:
324	// {"level":"info","msg":"single"}
325	// {"level":"info","msg":"doubled"}
326	// {"level":"info","msg":"doubled"}
327}
328