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
22
23import (
24	"bytes"
25	"fmt"
26	"log"
27	"os"
28	"sync"
29
30	"go.uber.org/zap/zapcore"
31)
32
33const (
34	_loggerWriterDepth       = 2
35	_programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " +
36		"https://github.com/uber-go/zap/issues/new and reference this error: %v"
37)
38
39var (
40	_globalMu sync.RWMutex
41	_globalL  = NewNop()
42	_globalS  = _globalL.Sugar()
43)
44
45// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
46// It's safe for concurrent use.
47func L() *Logger {
48	_globalMu.RLock()
49	l := _globalL
50	_globalMu.RUnlock()
51	return l
52}
53
54// S returns the global SugaredLogger, which can be reconfigured with
55// ReplaceGlobals. It's safe for concurrent use.
56func S() *SugaredLogger {
57	_globalMu.RLock()
58	s := _globalS
59	_globalMu.RUnlock()
60	return s
61}
62
63// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
64// function to restore the original values. It's safe for concurrent use.
65func ReplaceGlobals(logger *Logger) func() {
66	_globalMu.Lock()
67	prev := _globalL
68	_globalL = logger
69	_globalS = logger.Sugar()
70	_globalMu.Unlock()
71	return func() { ReplaceGlobals(prev) }
72}
73
74// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at
75// InfoLevel. To redirect the standard library's package-global logging
76// functions, use RedirectStdLog instead.
77func NewStdLog(l *Logger) *log.Logger {
78	logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
79	f := logger.Info
80	return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */)
81}
82
83// NewStdLogAt returns *log.Logger which writes to supplied zap logger at
84// required level.
85func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {
86	logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
87	logFunc, err := levelToFunc(logger, level)
88	if err != nil {
89		return nil, err
90	}
91	return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil
92}
93
94// RedirectStdLog redirects output from the standard library's package-global
95// logger to the supplied logger at InfoLevel. Since zap already handles caller
96// annotations, timestamps, etc., it automatically disables the standard
97// library's annotations and prefixing.
98//
99// It returns a function to restore the original prefix and flags and reset the
100// standard library's output to os.Stderr.
101func RedirectStdLog(l *Logger) func() {
102	f, err := redirectStdLogAt(l, InfoLevel)
103	if err != nil {
104		// Can't get here, since passing InfoLevel to redirectStdLogAt always
105		// works.
106		panic(fmt.Sprintf(_programmerErrorTemplate, err))
107	}
108	return f
109}
110
111// RedirectStdLogAt redirects output from the standard library's package-global
112// logger to the supplied logger at the specified level. Since zap already
113// handles caller annotations, timestamps, etc., it automatically disables the
114// standard library's annotations and prefixing.
115//
116// It returns a function to restore the original prefix and flags and reset the
117// standard library's output to os.Stderr.
118func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
119	return redirectStdLogAt(l, level)
120}
121
122func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
123	flags := log.Flags()
124	prefix := log.Prefix()
125	log.SetFlags(0)
126	log.SetPrefix("")
127	logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
128	logFunc, err := levelToFunc(logger, level)
129	if err != nil {
130		return nil, err
131	}
132	log.SetOutput(&loggerWriter{logFunc})
133	return func() {
134		log.SetFlags(flags)
135		log.SetPrefix(prefix)
136		log.SetOutput(os.Stderr)
137	}, nil
138}
139
140func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {
141	switch lvl {
142	case DebugLevel:
143		return logger.Debug, nil
144	case InfoLevel:
145		return logger.Info, nil
146	case WarnLevel:
147		return logger.Warn, nil
148	case ErrorLevel:
149		return logger.Error, nil
150	case DPanicLevel:
151		return logger.DPanic, nil
152	case PanicLevel:
153		return logger.Panic, nil
154	case FatalLevel:
155		return logger.Fatal, nil
156	}
157	return nil, fmt.Errorf("unrecognized level: %q", lvl)
158}
159
160type loggerWriter struct {
161	logFunc func(msg string, fields ...Field)
162}
163
164func (l *loggerWriter) Write(p []byte) (int, error) {
165	p = bytes.TrimSpace(p)
166	l.logFunc(string(p))
167	return len(p), nil
168}
169