/
logger.go
265 lines (227 loc) · 8.38 KB
/
logger.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Copyright 2022 Matrix Origin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"context"
"fmt"
"strings"
"time"
"github.com/matrixorigin/matrixone/pkg/pb/metadata"
"github.com/matrixorigin/matrixone/pkg/util/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// GetServiceLogger returns service logger, it will using the service as the logger name, and
// append FieldNameServiceUUID field to the logger
func GetServiceLogger(logger *zap.Logger, service metadata.ServiceType, uuid string) *MOLogger {
return wrap(logger.
Named(fmt.Sprintf("%s-service", strings.ToLower(service.String()))).
With(zap.String(FieldNameServiceUUID, uuid)))
}
// GetModuleLogger returns the module logger, it will add ".module" to logger name.
// e.g. if the logger's name is cn-service, module is txn, the new logger's name is
// "cn-service.txn".
func GetModuleLogger(logger *MOLogger, module Module) *MOLogger {
return wrap(logger.logger.Named(string(module)))
}
// With creates a child logger and adds structured context to it. Fields added
// to the child don't affect the parent, and vice versa.
func (l *MOLogger) With(fields ...zap.Field) *MOLogger {
return newMOLogger(l.logger.With(fields...), l.ctx)
}
// WithOptions creates a child logger with zap options.
func (l *MOLogger) WithOptions(opts ...zap.Option) *MOLogger {
return newMOLogger(l.logger.WithOptions(opts...), l.ctx)
}
// Named adds a new path segment to the logger's name. Segments are joined by
// periods. By default, Loggers are unnamed.
func (l *MOLogger) Named(name string) *MOLogger {
return newMOLogger(l.logger.Named(name), l.ctx)
}
func (l *MOLogger) WithContext(ctx context.Context) *MOLogger {
if ctx == nil || ctx == context.TODO() || ctx == context.Background() {
panic("nil, context.TODO() and context.Background() are not supported")
}
if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() {
panic("context with empty SpanContext are not supported")
}
return newMOLogger(l.logger, ctx)
}
func newMOLogger(logger *zap.Logger, ctx context.Context) *MOLogger {
return &MOLogger{
logger: logger,
ctx: ctx,
m: map[int]*zap.Logger{
1: logger.WithOptions(zap.AddCallerSkip(1)),
2: logger.WithOptions(zap.AddCallerSkip(2)),
3: logger.WithOptions(zap.AddCallerSkip(3)),
},
}
}
// WithProcess if the current log belongs to a certain process, the process name and process ID
// can be recorded. When analyzing the log, all related logs can be retrieved according to the
// process ID.
func (l *MOLogger) WithProcess(process Process) *MOLogger {
return l.With(zap.String(FieldNameProcess, string(process)))
}
// Enabled returns true if the level is enabled
func (l *MOLogger) Enabled(level zapcore.Level) bool {
return l.logger.Core().Enabled(level)
}
// RawLogger returns the raw zap logger
func (l *MOLogger) RawLogger() *zap.Logger {
return l.logger
}
// Info shortcuts to print info log
func (l *MOLogger) Info(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...)
}
// InfoAction shortcuts to print info action log
func (l *MOLogger) InfoAction(msg string, fields ...zap.Field) func() {
return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...)
}
// Debug shortcuts to print debug log
func (l *MOLogger) Debug(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...)
}
// InfoDebugAction shortcuts to print debug action log
func (l *MOLogger) DebugAction(msg string, fields ...zap.Field) func() {
return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...)
}
// Error shortcuts to print error log
func (l *MOLogger) Error(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.ErrorLevel).AddCallerSkip(1), fields...)
}
// Warn shortcuts to print warn log
func (l *MOLogger) Warn(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.WarnLevel).AddCallerSkip(1), fields...)
}
// Panic shortcuts to print panic log
func (l *MOLogger) Panic(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.PanicLevel).AddCallerSkip(1), fields...)
}
// Fatal shortcuts to print fatal log
func (l *MOLogger) Fatal(msg string, fields ...zap.Field) bool {
return l.Log(msg, DefaultLogOptions().WithLevel(zap.FatalLevel).AddCallerSkip(1), fields...)
}
// Log is the entry point for mo log printing. Return true to indicate that the log
// is being recorded by the current LogContext.
func (l *MOLogger) Log(msg string, opts LogOptions, fields ...zap.Field) bool {
if l.logger == nil {
panic("missing logger")
}
for _, fiter := range filters {
if !fiter(opts) {
return false
}
}
if opts.ctx == nil {
opts.ctx = l.ctx
}
logger, has := l.m[opts.callerSkip+1]
if !has {
logger = l.logger
}
if ce := logger.Check(opts.level, msg); ce != nil {
if len(opts.fields) > 0 {
fields = append(fields, opts.fields...)
}
if opts.ctx != nil {
fields = append(fields, trace.ContextField(opts.ctx))
}
ce.Write(fields...)
return true
}
return false
}
// LogAction used to log an action, or generate 2 logs, the first log occurring
// at the place where the call is made and the second log occurring at the end
// of the function where the LogAction is called, with the additional time consuming.
// e.g.:
//
// func LogActionExample() {
// defer log.Info(zapLogger).LogAction("example action")()
// }
//
// This method should often be used to log the elapsed time of a function and, as the
// logs appear in pairs, can also be used to check whether a function has been executed.
func (l *MOLogger) LogAction(action string, opts LogOptions, fields ...zap.Field) func() {
if !l.Log(action, opts.AddCallerSkip(1), fields...) {
return nothing
}
startAt := time.Now()
return func() {
fields = append(fields, zap.Duration(FieldNameCost, time.Since(startAt)))
l.Log(action, opts, fields...)
}
}
func wrap(logger *zap.Logger) *MOLogger {
return wrapWithContext(logger, nil)
}
func wrapWithContext(logger *zap.Logger, ctx context.Context) *MOLogger {
if logger == nil {
panic("zap logger is nil")
}
if ctx != nil &&
(ctx == context.TODO() || ctx == context.Background()) {
panic("TODO and Background are not supported")
}
return newMOLogger(
logger,
ctx,
)
}
func nothing() {}
// DefaultLogOptions default log options
func DefaultLogOptions() LogOptions {
return LogOptions{}
}
// WithContext set log trace context.
func (opts LogOptions) WithContext(ctx context.Context) LogOptions {
if ctx == nil {
panic("context is nil")
}
if ctx == context.TODO() || ctx == context.Background() {
panic("TODO and Background contexts are not supported")
}
if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() {
panic("context with empty SpanContext are not supported")
}
opts.ctx = ctx
return opts
}
// WithLevel set log print level
func (opts LogOptions) WithLevel(level zapcore.Level) LogOptions {
opts.level = level
return opts
}
// WithSample sample print the log, using log counts as sampling frequency. First time must output.
func (opts LogOptions) WithSample(sampleType SampleType) LogOptions {
opts.sampleType = sampleType
return opts
}
// AddCallerSkip help to show the logger real caller, by skip n call stack
func (opts LogOptions) AddCallerSkip(n int) LogOptions {
opts.callerSkip += n
return opts
}
// WithProcess if the current log belongs to a certain process, the process name and process ID
// can be recorded. When analyzing the log, all related logs can be retrieved according to the
// process ID.
func (opts LogOptions) WithProcess(process Process, processID string) LogOptions {
opts.fields = append(opts.fields,
zap.String(FieldNameProcess, string(process)),
zap.String(FieldNameProcessID, processID))
return opts
}