| // Copyright (c) 2016 Uber Technologies, Inc. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| package zap |
| |
| import ( |
| "bytes" |
| "fmt" |
| "log" |
| "os" |
| "sync" |
| |
| "go.uber.org/zap/zapcore" |
| ) |
| |
| const ( |
| _loggerWriterDepth = 2 |
| _programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " + |
| "https://github.com/uber-go/zap/issues/new and reference this error: %v" |
| ) |
| |
| var ( |
| _globalMu sync.RWMutex |
| _globalL = NewNop() |
| _globalS = _globalL.Sugar() |
| ) |
| |
| // L returns the global Logger, which can be reconfigured with ReplaceGlobals. |
| // It's safe for concurrent use. |
| func L() *Logger { |
| _globalMu.RLock() |
| l := _globalL |
| _globalMu.RUnlock() |
| return l |
| } |
| |
| // S returns the global SugaredLogger, which can be reconfigured with |
| // ReplaceGlobals. It's safe for concurrent use. |
| func S() *SugaredLogger { |
| _globalMu.RLock() |
| s := _globalS |
| _globalMu.RUnlock() |
| return s |
| } |
| |
| // ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a |
| // function to restore the original values. It's safe for concurrent use. |
| func ReplaceGlobals(logger *Logger) func() { |
| _globalMu.Lock() |
| prev := _globalL |
| _globalL = logger |
| _globalS = logger.Sugar() |
| _globalMu.Unlock() |
| return func() { ReplaceGlobals(prev) } |
| } |
| |
| // NewStdLog returns a *log.Logger which writes to the supplied zap Logger at |
| // InfoLevel. To redirect the standard library's package-global logging |
| // functions, use RedirectStdLog instead. |
| func NewStdLog(l *Logger) *log.Logger { |
| logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) |
| f := logger.Info |
| return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */) |
| } |
| |
| // NewStdLogAt returns *log.Logger which writes to supplied zap logger at |
| // required level. |
| func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) { |
| logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) |
| logFunc, err := levelToFunc(logger, level) |
| if err != nil { |
| return nil, err |
| } |
| return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil |
| } |
| |
| // RedirectStdLog redirects output from the standard library's package-global |
| // logger to the supplied logger at InfoLevel. Since zap already handles caller |
| // annotations, timestamps, etc., it automatically disables the standard |
| // library's annotations and prefixing. |
| // |
| // It returns a function to restore the original prefix and flags and reset the |
| // standard library's output to os.Stderr. |
| func RedirectStdLog(l *Logger) func() { |
| f, err := redirectStdLogAt(l, InfoLevel) |
| if err != nil { |
| // Can't get here, since passing InfoLevel to redirectStdLogAt always |
| // works. |
| panic(fmt.Sprintf(_programmerErrorTemplate, err)) |
| } |
| return f |
| } |
| |
| // RedirectStdLogAt redirects output from the standard library's package-global |
| // logger to the supplied logger at the specified level. Since zap already |
| // handles caller annotations, timestamps, etc., it automatically disables the |
| // standard library's annotations and prefixing. |
| // |
| // It returns a function to restore the original prefix and flags and reset the |
| // standard library's output to os.Stderr. |
| func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { |
| return redirectStdLogAt(l, level) |
| } |
| |
| func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { |
| flags := log.Flags() |
| prefix := log.Prefix() |
| log.SetFlags(0) |
| log.SetPrefix("") |
| logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) |
| logFunc, err := levelToFunc(logger, level) |
| if err != nil { |
| return nil, err |
| } |
| log.SetOutput(&loggerWriter{logFunc}) |
| return func() { |
| log.SetFlags(flags) |
| log.SetPrefix(prefix) |
| log.SetOutput(os.Stderr) |
| }, nil |
| } |
| |
| func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) { |
| switch lvl { |
| case DebugLevel: |
| return logger.Debug, nil |
| case InfoLevel: |
| return logger.Info, nil |
| case WarnLevel: |
| return logger.Warn, nil |
| case ErrorLevel: |
| return logger.Error, nil |
| case DPanicLevel: |
| return logger.DPanic, nil |
| case PanicLevel: |
| return logger.Panic, nil |
| case FatalLevel: |
| return logger.Fatal, nil |
| } |
| return nil, fmt.Errorf("unrecognized level: %q", lvl) |
| } |
| |
| type loggerWriter struct { |
| logFunc func(msg string, fields ...Field) |
| } |
| |
| func (l *loggerWriter) Write(p []byte) (int, error) { |
| p = bytes.TrimSpace(p) |
| l.logFunc(string(p)) |
| return len(p), nil |
| } |