blob: c1ac0507cd9b001fb35215cacbe4fb04dfa9a9b5 [file] [log] [blame]
Scott Bakereee8dd82019-09-24 12:52:34 -07001// 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 (
Scott Bakereee8dd82019-09-24 12:52:34 -070034 _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}