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