/
cclogging.go
283 lines (242 loc) · 10.5 KB
/
cclogging.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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package shim
import (
"os"
"strings"
"sync"
logging "github.com/op/go-logging"
)
// IsEnabledForLogLevel checks to see if the chaincodeLogger is enabled for a specific logging level
// used primarily for testing
func IsEnabledForLogLevel(logLevel string) bool {
lvl, _ := logging.LogLevel(logLevel)
return chaincodeLogger.IsEnabledFor(lvl)
}
var loggingSetup sync.Once
// SetupChaincodeLogging sets the chaincode logging format and the level
// to the values of CORE_CHAINCODE_LOGGING_FORMAT, CORE_CHAINCODE_LOGGING_LEVEL
// and CORE_CHAINCODE_LOGGING_SHIM set from core.yaml by chaincode_support.go
func SetupChaincodeLogging() {
loggingSetup.Do(setupChaincodeLogging)
}
func setupChaincodeLogging() {
// This is the default log config from 1.2
const defaultLogFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} -> %{level:.4s} %{id:03x}%{color:reset} %{message}"
const defaultLevel = logging.INFO
// setup process-wide logging backend
logFormat := os.Getenv("CORE_CHAINCODE_LOGGING_FORMAT")
if logFormat == "" {
logFormat = defaultLogFormat
}
formatter := logging.MustStringFormatter(logFormat)
backend := logging.NewLogBackend(os.Stderr, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, formatter)
logging.SetBackend(backendFormatter).SetLevel(defaultLevel, "")
// set default log level for all loggers
chaincodeLogLevelString := os.Getenv("CORE_CHAINCODE_LOGGING_LEVEL")
if chaincodeLogLevelString == "" {
chaincodeLogger.Infof("Chaincode log level not provided; defaulting to: %s", defaultLevel.String())
chaincodeLogLevelString = defaultLevel.String()
}
_, err := LogLevel(chaincodeLogLevelString)
if err != nil {
chaincodeLogger.Warningf("Error: '%s' for chaincode log level: %s; defaulting to %s", err, chaincodeLogLevelString, defaultLevel.String())
chaincodeLogLevelString = defaultLevel.String()
}
initFromSpec(chaincodeLogLevelString, defaultLevel)
// override the log level for the shim logger - note: if this value is
// blank or an invalid log level, then the above call to
// `initFromSpec` already set the default log level so no action
// is required here.
shimLogLevelString := os.Getenv("CORE_CHAINCODE_LOGGING_SHIM")
if shimLogLevelString != "" {
shimLogLevel, err := LogLevel(shimLogLevelString)
if err == nil {
SetLoggingLevel(shimLogLevel)
} else {
chaincodeLogger.Warningf("Error: %s for shim log level: %s", err, shimLogLevelString)
}
}
//now that logging is setup, print build level. This will help making sure
//chaincode is matched with peer.
buildLevel := os.Getenv("CORE_CHAINCODE_BUILDLEVEL")
chaincodeLogger.Infof("Chaincode (build level: %s) starting up ...", buildLevel)
}
// this has been moved from the 1.2 logging implementation
func initFromSpec(spec string, defaultLevel logging.Level) {
levelAll := defaultLevel
var err error
fields := strings.Split(spec, ":")
for _, field := range fields {
split := strings.Split(field, "=")
switch len(split) {
case 1:
if levelAll, err = logging.LogLevel(field); err != nil {
chaincodeLogger.Warningf("Logging level '%s' not recognized, defaulting to '%s': %s", field, defaultLevel, err)
levelAll = defaultLevel // need to reset cause original value was overwritten
}
case 2:
// <logger,<logger>...]=<level>
levelSingle, err := logging.LogLevel(split[1])
if err != nil {
chaincodeLogger.Warningf("Invalid logging level in '%s' ignored", field)
continue
}
if split[0] == "" {
chaincodeLogger.Warningf("Invalid logging override specification '%s' ignored - no logger specified", field)
} else {
loggers := strings.Split(split[0], ",")
for _, logger := range loggers {
chaincodeLogger.Debugf("Setting logging level for logger '%s' to '%s'", logger, levelSingle)
logging.SetLevel(levelSingle, logger)
}
}
default:
chaincodeLogger.Warningf("Invalid logging override '%s' ignored - missing ':'?", field)
}
}
logging.SetLevel(levelAll, "") // set the logging level for all loggers
}
// ------------- Logging Control and Chaincode Loggers ---------------
// As independent programs, Go language chaincodes can use any logging
// methodology they choose, from simple fmt.Printf() to os.Stdout, to
// decorated logs created by the author's favorite logging package. The
// chaincode "shim" interface, however, is defined by the Hyperledger fabric
// and implements its own logging methodology. This methodology currently
// includes severity-based logging control and a standard way of decorating
// the logs.
//
// The facilities defined here allow a Go language chaincode to control the
// logging level of its shim, and to create its own logs formatted
// consistently with, and temporally interleaved with the shim logs without
// any knowledge of the underlying implementation of the shim, and without any
// other package requirements. The lack of package requirements is especially
// important because even if the chaincode happened to explicitly use the same
// logging package as the shim, unless the chaincode is physically included as
// part of the hyperledger fabric source code tree it could actually end up
// using a distinct binary instance of the logging package, with different
// formats and severity levels than the binary package used by the shim.
//
// Another approach that might have been taken, and could potentially be taken
// in the future, would be for the chaincode to supply a logging object for
// the shim to use, rather than the other way around as implemented
// here. There would be some complexities associated with that approach, so
// for the moment we have chosen the simpler implementation below. The shim
// provides one or more abstract logging objects for the chaincode to use via
// the NewLogger() API, and allows the chaincode to control the severity level
// of shim logs using the SetLoggingLevel() API.
// LoggingLevel is an enumerated type of severity levels that control
// chaincode logging.
type LoggingLevel logging.Level
// These constants comprise the LoggingLevel enumeration
const (
LogDebug = LoggingLevel(logging.DEBUG)
LogInfo = LoggingLevel(logging.INFO)
LogNotice = LoggingLevel(logging.NOTICE)
LogWarning = LoggingLevel(logging.WARNING)
LogError = LoggingLevel(logging.ERROR)
LogCritical = LoggingLevel(logging.CRITICAL)
)
var shimLoggingLevel = LogInfo // Necessary for correct initialization; See Start()
// SetLoggingLevel allows a Go language chaincode to set the logging level of
// its shim.
func SetLoggingLevel(level LoggingLevel) {
shimLoggingLevel = level
logging.SetLevel(logging.Level(level), "shim")
}
// LogLevel converts a case-insensitive string chosen from CRITICAL, ERROR,
// WARNING, NOTICE, INFO or DEBUG into an element of the LoggingLevel
// type. In the event of errors the level returned is LogError.
func LogLevel(levelString string) (LoggingLevel, error) {
l, err := logging.LogLevel(levelString)
level := LoggingLevel(l)
if err != nil {
level = LogError
}
return level, err
}
// ChaincodeLogger is an abstraction of a logging object for use by
// chaincodes. These objects are created by the NewLogger API.
type ChaincodeLogger struct {
logger *logging.Logger
}
// NewLogger allows a Go language chaincode to create one or more logging
// objects whose logs will be formatted consistently with, and temporally
// interleaved with the logs created by the shim interface. The logs created
// by this object can be distinguished from shim logs by the name provided,
// which will appear in the logs.
func NewLogger(name string) *ChaincodeLogger {
return &ChaincodeLogger{logging.MustGetLogger(name)}
}
// SetLevel sets the logging level for a chaincode logger. Note that currently
// the levels are actually controlled by the name given when the logger is
// created, so loggers should be given unique names other than "shim".
func (c *ChaincodeLogger) SetLevel(level LoggingLevel) {
logging.SetLevel(logging.Level(level), c.logger.Module)
}
// IsEnabledFor returns true if the logger is enabled to creates logs at the
// given logging level.
func (c *ChaincodeLogger) IsEnabledFor(level LoggingLevel) bool {
return c.logger.IsEnabledFor(logging.Level(level))
}
// Debug logs will only appear if the ChaincodeLogger LoggingLevel is set to
// LogDebug.
func (c *ChaincodeLogger) Debug(args ...interface{}) {
c.logger.Debug(args...)
}
// Info logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogInfo or LogDebug.
func (c *ChaincodeLogger) Info(args ...interface{}) {
c.logger.Info(args...)
}
// Notice logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Notice(args ...interface{}) {
c.logger.Notice(args...)
}
// Warning logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Warning(args ...interface{}) {
c.logger.Warning(args...)
}
// Error logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogError, LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Error(args ...interface{}) {
c.logger.Error(args...)
}
// Critical logs always appear; They can not be disabled.
func (c *ChaincodeLogger) Critical(args ...interface{}) {
c.logger.Critical(args...)
}
// Debugf logs will only appear if the ChaincodeLogger LoggingLevel is set to
// LogDebug.
func (c *ChaincodeLogger) Debugf(format string, args ...interface{}) {
c.logger.Debugf(format, args...)
}
// Infof logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogInfo or LogDebug.
func (c *ChaincodeLogger) Infof(format string, args ...interface{}) {
c.logger.Infof(format, args...)
}
// Noticef logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Noticef(format string, args ...interface{}) {
c.logger.Noticef(format, args...)
}
// Warningf logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Warningf(format string, args ...interface{}) {
c.logger.Warningf(format, args...)
}
// Errorf logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogError, LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Errorf(format string, args ...interface{}) {
c.logger.Errorf(format, args...)
}
// Criticalf logs always appear; They can not be disabled.
func (c *ChaincodeLogger) Criticalf(format string, args ...interface{}) {
c.logger.Criticalf(format, args...)
}