-
Notifications
You must be signed in to change notification settings - Fork 0
/
logging.go
255 lines (215 loc) · 6.1 KB
/
logging.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
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package flogging
import (
"fmt"
"io"
"os"
"sync"
"github.com/hyperledger/fabric/common/flogging/fabenc"
logging "github.com/op/go-logging"
zaplogfmt "github.com/sykesm/zap-logfmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Config is used to provide dependencies to a Logging instance.
type Config struct {
// Format is the log record format specifier for the Logging instance. If the
// spec is the string "json", log records will be formatted as JSON. Any
// other string will be provided to the FormatEncoder. Please see
// fabenc.ParseFormat for details on the supported verbs.
//
// If Format is not provided, a default format that provides basic information will
// be used.
Format string
// LogSpec determines the log levels that are enabled for the logging system. The
// spec must be in a format that can be processed by ActivateSpec.
//
// If LogSpec is not provided, loggers will be enabled at the INFO level.
LogSpec string
// Writer is the sink for encoded and formatted log records.
//
// If a Writer is not provided, os.Stderr will be used as the log sink.
Writer io.Writer
}
// Logging maintains the state associated with the fabric logging system. It is
// intended to bridge between the legacy logging infrastructure built around
// go-logging and the structured, level logging provided by zap.
type Logging struct {
*LoggerLevels
mutex sync.RWMutex
encoding Encoding
encoderConfig zapcore.EncoderConfig
multiFormatter *fabenc.MultiFormatter
writer zapcore.WriteSyncer
observer Observer
}
// New creates a new logging system and initializes it with the provided
// configuration.
func New(c Config) (*Logging, error) {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.NameKey = "name"
s := &Logging{
LoggerLevels: &LoggerLevels{
defaultLevel: defaultLevel,
},
encoderConfig: encoderConfig,
multiFormatter: fabenc.NewMultiFormatter(),
}
err := s.Apply(c)
if err != nil {
return nil, err
}
return s, nil
}
// Apply applies the provided configuration to the logging system.
func (s *Logging) Apply(c Config) error {
err := s.SetFormat(c.Format)
if err != nil {
return err
}
if c.LogSpec == "" {
c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC")
}
if c.LogSpec == "" {
c.LogSpec = defaultLevel.String()
}
err = s.LoggerLevels.ActivateSpec(c.LogSpec)
if err != nil {
return err
}
if c.Writer == nil {
c.Writer = os.Stderr
}
s.SetWriter(c.Writer)
var formatter logging.Formatter
switch s.Encoding() {
case JSON, LOGFMT:
formatter = SetFormat(defaultFormat)
default:
formatter = SetFormat(c.Format)
}
InitBackend(formatter, c.Writer)
return nil
}
// SetFormat updates how log records are formatted and encoded. Log entries
// created after this method has completed will use the new format.
//
// An error is returned if the log format specification cannot be parsed.
func (s *Logging) SetFormat(format string) error {
s.mutex.Lock()
defer s.mutex.Unlock()
if format == "" {
format = defaultFormat
}
if format == "json" {
s.encoding = JSON
return nil
}
if format == "logfmt" {
s.encoding = LOGFMT
return nil
}
formatters, err := fabenc.ParseFormat(format)
if err != nil {
return err
}
s.multiFormatter.SetFormatters(formatters)
s.encoding = CONSOLE
return nil
}
// SetWriter controls which writer formatted log records are written to.
// Writers, with the exception of an *os.File, need to be safe for concurrent
// use by multiple go routines.
func (s *Logging) SetWriter(w io.Writer) {
var sw zapcore.WriteSyncer
switch t := w.(type) {
case *os.File:
sw = zapcore.Lock(t)
case zapcore.WriteSyncer:
sw = t
default:
sw = zapcore.AddSync(w)
}
s.mutex.Lock()
s.writer = sw
s.mutex.Unlock()
}
// SetObserver is used to provide a log observer that will be called as log
// levels are checked or written.. Only a single observer is supported.
func (s *Logging) SetObserver(observer Observer) {
s.mutex.Lock()
s.observer = observer
s.mutex.Unlock()
}
// Write satisfies the io.Write contract. It delegates to the writer argument
// of SetWriter or the Writer field of Config. The Core uses this when encoding
// log records.
func (s *Logging) Write(b []byte) (int, error) {
s.mutex.RLock()
w := s.writer
s.mutex.RUnlock()
return w.Write(b)
}
// Sync satisfies the zapcore.WriteSyncer interface. It is used by the Core to
// flush log records before terminating the process.
func (s *Logging) Sync() error {
s.mutex.RLock()
w := s.writer
s.mutex.RUnlock()
return w.Sync()
}
// Encoding satisfies the Encoding interface. It determines whether the JSON or
// CONSOLE encoder should be used by the Core when log records are written.
func (s *Logging) Encoding() Encoding {
s.mutex.RLock()
e := s.encoding
s.mutex.RUnlock()
return e
}
// ZapLogger instantiates a new zap.Logger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) ZapLogger(name string) *zap.Logger {
if !isValidLoggerName(name) {
panic(fmt.Sprintf("invalid logger name: %s", name))
}
s.mutex.RLock()
core := &Core{
LevelEnabler: s.LoggerLevels,
Levels: s.LoggerLevels,
Encoders: map[Encoding]zapcore.Encoder{
JSON: zapcore.NewJSONEncoder(s.encoderConfig),
CONSOLE: fabenc.NewFormatEncoder(s.multiFormatter),
LOGFMT: zaplogfmt.NewEncoder(s.encoderConfig),
},
Selector: s,
Output: s,
Observer: s,
}
s.mutex.RUnlock()
return NewZapLogger(core).Named(name)
}
func (s *Logging) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) {
s.mutex.RLock()
observer := s.observer
s.mutex.RUnlock()
if observer != nil {
observer.Check(e, ce)
}
}
func (s *Logging) WriteEntry(e zapcore.Entry, fields []zapcore.Field) {
s.mutex.RLock()
observer := s.observer
s.mutex.RUnlock()
if observer != nil {
observer.WriteEntry(e, fields)
}
}
// Logger instantiates a new FabricLogger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) Logger(name string) *FabricLogger {
zl := s.ZapLogger(name)
return NewFabricLogger(zl)
}