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