forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
202 lines (167 loc) · 5.91 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
package log
import (
"encoding"
"encoding/json"
"fmt"
"io"
"github.com/bytedance/sonic"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors"
)
func init() {
zerolog.InterfaceMarshalFunc = func(i any) ([]byte, error) {
switch v := i.(type) {
case json.Marshaler:
return sonic.Marshal(i)
case encoding.TextMarshaler:
return sonic.Marshal(i)
case fmt.Stringer:
return sonic.Marshal(v.String())
default:
return sonic.Marshal(i)
}
}
}
// ModuleKey defines a module logging key.
const ModuleKey = "module"
// ContextKey is used to store the logger in the context.
var ContextKey contextKey
type contextKey struct{}
// Logger is the Cosmos SDK logger interface.
// It extends cosmossdk.io/core/log.Logger to return a child logger.
// Use cosmossdk.io/core/log.Logger instead in modules.
type Logger interface {
// Info takes a message and a set of key/value pairs and logs with level INFO.
// The key of the tuple must be a string.
Info(msg string, keyVals ...any)
// Warn takes a message and a set of key/value pairs and logs with level WARN.
// The key of the tuple must be a string.
Warn(msg string, keyVals ...any)
// Error takes a message and a set of key/value pairs and logs with level ERR.
// The key of the tuple must be a string.
Error(msg string, keyVals ...any)
// Debug takes a message and a set of key/value pairs and logs with level DEBUG.
// The key of the tuple must be a string.
Debug(msg string, keyVals ...any)
// With returns a new wrapped logger with additional context provided by a set.
With(keyVals ...any) Logger
// Impl returns the underlying logger implementation.
// It is used to access the full functionalities of the underlying logger.
// Advanced users can type cast the returned value to the actual logger.
Impl() any
}
// WithJSONMarshal configures zerolog global json encoding.
func WithJSONMarshal(marshaler func(v any) ([]byte, error)) {
zerolog.InterfaceMarshalFunc = func(i any) ([]byte, error) {
switch v := i.(type) {
case json.Marshaler:
return marshaler(i)
case encoding.TextMarshaler:
return marshaler(i)
case fmt.Stringer:
return marshaler(v.String())
default:
return marshaler(i)
}
}
}
type zeroLogWrapper struct {
*zerolog.Logger
}
// NewLogger returns a new logger that writes to the given destination.
//
// Typical usage from a main function is:
//
// logger := log.NewLogger(os.Stderr)
//
// Stderr is the typical destination for logs,
// so that any output from your application can still be piped to other processes.
// The returned value can be safely cast to cosmossdk.io/core/log.Logger.
func NewLogger(dst io.Writer, options ...Option) Logger {
logCfg := defaultConfig
for _, opt := range options {
opt(&logCfg)
}
output := dst
if !logCfg.OutputJSON {
output = zerolog.ConsoleWriter{
Out: dst,
NoColor: !logCfg.Color,
TimeFormat: logCfg.TimeFormat,
}
}
if logCfg.Filter != nil {
output = NewFilterWriter(output, logCfg.Filter)
}
logger := zerolog.New(output)
if logCfg.StackTrace {
zerolog.ErrorStackMarshaler = func(err error) interface{} {
return pkgerrors.MarshalStack(errors.WithStack(err))
}
logger = logger.With().Stack().Logger()
}
if logCfg.TimeFormat != "" {
logger = logger.With().Timestamp().Logger()
}
if logCfg.Level != zerolog.NoLevel {
logger = logger.Level(logCfg.Level)
}
logger = logger.Hook(logCfg.Hooks...)
return zeroLogWrapper{&logger}
}
// NewCustomLogger returns a new logger with the given zerolog logger.
func NewCustomLogger(logger zerolog.Logger) Logger {
return zeroLogWrapper{&logger}
}
// Info takes a message and a set of key/value pairs and logs with level INFO.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Info(msg string, keyVals ...interface{}) {
l.Logger.Info().Fields(keyVals).Msg(msg)
}
// Warn takes a message and a set of key/value pairs and logs with level WARN.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Warn(msg string, keyVals ...interface{}) {
l.Logger.Warn().Fields(keyVals).Msg(msg)
}
// Error takes a message and a set of key/value pairs and logs with level ERROR.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Error(msg string, keyVals ...interface{}) {
l.Logger.Error().Fields(keyVals).Msg(msg)
}
// Debug takes a message and a set of key/value pairs and logs with level DEBUG.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Debug(msg string, keyVals ...interface{}) {
l.Logger.Debug().Fields(keyVals).Msg(msg)
}
// With returns a new wrapped logger with additional context provided by a set.
func (l zeroLogWrapper) With(keyVals ...interface{}) Logger {
logger := l.Logger.With().Fields(keyVals).Logger()
return zeroLogWrapper{&logger}
}
// WithContext returns a new wrapped logger with additional context provided by a set.
func (l zeroLogWrapper) WithContext(keyVals ...interface{}) any {
logger := l.Logger.With().Fields(keyVals).Logger()
return zeroLogWrapper{&logger}
}
// Impl returns the underlying zerolog logger.
// It can be used to used zerolog structured API directly instead of the wrapper.
func (l zeroLogWrapper) Impl() interface{} {
return l.Logger
}
// NewNopLogger returns a new logger that does nothing.
func NewNopLogger() Logger {
// The custom nopLogger is about 3x faster than a zeroLogWrapper with zerolog.Nop().
return nopLogger{}
}
// nopLogger is a Logger that does nothing when called.
// See the "specialized nop logger" benchmark and compare with the "zerolog nop logger" benchmark.
// The custom implementation is about 3x faster.
type nopLogger struct{}
func (nopLogger) Info(string, ...any) {}
func (nopLogger) Warn(string, ...any) {}
func (nopLogger) Error(string, ...any) {}
func (nopLogger) Debug(string, ...any) {}
func (nopLogger) With(...any) Logger { return nopLogger{} }
func (nopLogger) WithContext(...any) any { return nopLogger{} }
func (nopLogger) Impl() any { return nopLogger{} }