1// Copyright 2018 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package log
16
17import (
18	"fmt"
19	"runtime"
20	"strings"
21	"sync"
22	"sync/atomic"
23	"time"
24
25	"go.uber.org/zap"
26	"go.uber.org/zap/zapcore"
27)
28
29// Scope let's you log data for an area of code, enabling the user full control over
30// the level of logging output produced.
31type Scope struct {
32	// immutable, set at creation
33	name        string
34	nameToEmit  string
35	description string
36	callerSkip  int
37
38	// set by the Configure method and adjustable dynamically
39	outputLevel     atomic.Value
40	stackTraceLevel atomic.Value
41	logCallers      atomic.Value
42}
43
44var scopes = make(map[string]*Scope)
45var lock = sync.RWMutex{}
46
47// RegisterScope registers a new logging scope. If the same name is used multiple times
48// for a single process, the same Scope struct is returned.
49//
50// Scope names cannot include colons, commas, or periods.
51func RegisterScope(name string, description string, callerSkip int) *Scope {
52	if strings.ContainsAny(name, ":,.") {
53		panic(fmt.Sprintf("scope name %s is invalid, it cannot contain colons, commas, or periods", name))
54	}
55
56	lock.Lock()
57	defer lock.Unlock()
58
59	s, ok := scopes[name]
60	if !ok {
61		s = &Scope{
62			name:        name,
63			description: description,
64			callerSkip:  callerSkip,
65		}
66		s.SetOutputLevel(InfoLevel)
67		s.SetStackTraceLevel(NoneLevel)
68		s.SetLogCallers(false)
69
70		if name != DefaultScopeName {
71			s.nameToEmit = name
72		}
73
74		scopes[name] = s
75	}
76
77	return s
78}
79
80// FindScope returns a previously registered scope, or nil if the named scope wasn't previously registered
81func FindScope(scope string) *Scope {
82	lock.RLock()
83	defer lock.RUnlock()
84
85	s := scopes[scope]
86	return s
87}
88
89// Scopes returns a snapshot of the currently defined set of scopes
90func Scopes() map[string]*Scope {
91	lock.RLock()
92	defer lock.RUnlock()
93
94	s := make(map[string]*Scope, len(scopes))
95	for k, v := range scopes {
96		s[k] = v
97	}
98
99	return s
100}
101
102// Fatal outputs a message at fatal level.
103func (s *Scope) Fatal(msg string, fields ...zapcore.Field) {
104	if s.GetOutputLevel() >= FatalLevel {
105		s.emit(zapcore.FatalLevel, s.GetStackTraceLevel() >= FatalLevel, msg, fields)
106	}
107}
108
109// Fatala uses fmt.Sprint to construct and log a message at fatal level.
110func (s *Scope) Fatala(args ...interface{}) {
111	if s.GetOutputLevel() >= FatalLevel {
112		s.emit(zapcore.FatalLevel, s.GetStackTraceLevel() >= FatalLevel, fmt.Sprint(args...), nil)
113	}
114}
115
116// Fatalf uses fmt.Sprintf to construct and log a message at fatal level.
117func (s *Scope) Fatalf(template string, args ...interface{}) {
118	if s.GetOutputLevel() >= FatalLevel {
119		msg := template
120		if len(args) > 0 {
121			msg = fmt.Sprintf(template, args...)
122		}
123		s.emit(zapcore.FatalLevel, s.GetStackTraceLevel() >= FatalLevel, msg, nil)
124	}
125}
126
127// FatalEnabled returns whether output of messages using this scope is currently enabled for fatal-level output.
128func (s *Scope) FatalEnabled() bool {
129	return s.GetOutputLevel() >= FatalLevel
130}
131
132// Error outputs a message at error level.
133func (s *Scope) Error(msg string, fields ...zapcore.Field) {
134	if s.GetOutputLevel() >= ErrorLevel {
135		s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields)
136	}
137}
138
139// Errora uses fmt.Sprint to construct and log a message at error level.
140func (s *Scope) Errora(args ...interface{}) {
141	if s.GetOutputLevel() >= ErrorLevel {
142		s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil)
143	}
144}
145
146// Errorf uses fmt.Sprintf to construct and log a message at error level.
147func (s *Scope) Errorf(template string, args ...interface{}) {
148	if s.GetOutputLevel() >= ErrorLevel {
149		msg := template
150		if len(args) > 0 {
151			msg = fmt.Sprintf(template, args...)
152		}
153		s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil)
154	}
155}
156
157// ErrorEnabled returns whether output of messages using this scope is currently enabled for error-level output.
158func (s *Scope) ErrorEnabled() bool {
159	return s.GetOutputLevel() >= ErrorLevel
160}
161
162// Warn outputs a message at warn level.
163func (s *Scope) Warn(msg string, fields ...zapcore.Field) {
164	if s.GetOutputLevel() >= WarnLevel {
165		s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields)
166	}
167}
168
169// Warna uses fmt.Sprint to construct and log a message at warn level.
170func (s *Scope) Warna(args ...interface{}) {
171	if s.GetOutputLevel() >= WarnLevel {
172		s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil)
173	}
174}
175
176// Warnf uses fmt.Sprintf to construct and log a message at warn level.
177func (s *Scope) Warnf(template string, args ...interface{}) {
178	if s.GetOutputLevel() >= WarnLevel {
179		msg := template
180		if len(args) > 0 {
181			msg = fmt.Sprintf(template, args...)
182		}
183		s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil)
184	}
185}
186
187// WarnEnabled returns whether output of messages using this scope is currently enabled for warn-level output.
188func (s *Scope) WarnEnabled() bool {
189	return s.GetOutputLevel() >= WarnLevel
190}
191
192// Info outputs a message at info level.
193func (s *Scope) Info(msg string, fields ...zapcore.Field) {
194	if s.GetOutputLevel() >= InfoLevel {
195		s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields)
196	}
197}
198
199// Infoa uses fmt.Sprint to construct and log a message at info level.
200func (s *Scope) Infoa(args ...interface{}) {
201	if s.GetOutputLevel() >= InfoLevel {
202		s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil)
203	}
204}
205
206// Infof uses fmt.Sprintf to construct and log a message at info level.
207func (s *Scope) Infof(template string, args ...interface{}) {
208	if s.GetOutputLevel() >= InfoLevel {
209		msg := template
210		if len(args) > 0 {
211			msg = fmt.Sprintf(template, args...)
212		}
213		s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil)
214	}
215}
216
217// InfoEnabled returns whether output of messages using this scope is currently enabled for info-level output.
218func (s *Scope) InfoEnabled() bool {
219	return s.GetOutputLevel() >= InfoLevel
220}
221
222// Debug outputs a message at debug level.
223func (s *Scope) Debug(msg string, fields ...zapcore.Field) {
224	if s.GetOutputLevel() >= DebugLevel {
225		s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields)
226	}
227}
228
229// Debuga uses fmt.Sprint to construct and log a message at debug level.
230func (s *Scope) Debuga(args ...interface{}) {
231	if s.GetOutputLevel() >= DebugLevel {
232		s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil)
233	}
234}
235
236// Debugf uses fmt.Sprintf to construct and log a message at debug level.
237func (s *Scope) Debugf(template string, args ...interface{}) {
238	if s.GetOutputLevel() >= DebugLevel {
239		msg := template
240		if len(args) > 0 {
241			msg = fmt.Sprintf(template, args...)
242		}
243		s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil)
244	}
245}
246
247// DebugEnabled returns whether output of messages using this scope is currently enabled for debug-level output.
248func (s *Scope) DebugEnabled() bool {
249	return s.GetOutputLevel() >= DebugLevel
250}
251
252// Name returns this scope's name.
253func (s *Scope) Name() string {
254	return s.name
255}
256
257// Description returns this scope's description
258func (s *Scope) Description() string {
259	return s.description
260}
261
262const callerSkipOffset = 2
263
264func (s *Scope) emit(level zapcore.Level, dumpStack bool, msg string, fields []zapcore.Field) {
265	e := zapcore.Entry{
266		Message:    msg,
267		Level:      level,
268		Time:       time.Now(),
269		LoggerName: s.nameToEmit,
270	}
271
272	if s.GetLogCallers() {
273		e.Caller = zapcore.NewEntryCaller(runtime.Caller(s.callerSkip + callerSkipOffset))
274	}
275
276	if dumpStack {
277		e.Stack = zap.Stack("").String
278	}
279
280	pt := funcs.Load().(patchTable)
281	if pt.write != nil {
282		if err := pt.write(e, fields); err != nil {
283			_, _ = fmt.Fprintf(pt.errorSink, "%v log write error: %v\n", time.Now(), err)
284			_ = pt.errorSink.Sync()
285		}
286	}
287}
288
289// SetOutputLevel adjusts the output level associated with the scope.
290func (s *Scope) SetOutputLevel(l Level) {
291	s.outputLevel.Store(l)
292}
293
294// GetOutputLevel returns the output level associated with the scope.
295func (s *Scope) GetOutputLevel() Level {
296	return s.outputLevel.Load().(Level)
297}
298
299// SetStackTraceLevel adjusts the stack tracing level associated with the scope.
300func (s *Scope) SetStackTraceLevel(l Level) {
301	s.stackTraceLevel.Store(l)
302}
303
304// GetStackTraceLevel returns the stack tracing level associated with the scope.
305func (s *Scope) GetStackTraceLevel() Level {
306	return s.stackTraceLevel.Load().(Level)
307}
308
309// SetLogCallers adjusts the output level associated with the scope.
310func (s *Scope) SetLogCallers(logCallers bool) {
311	s.logCallers.Store(logCallers)
312}
313
314// GetLogCallers returns the output level associated with the scope.
315func (s *Scope) GetLogCallers() bool {
316	return s.logCallers.Load().(bool)
317}
318