Skip to content

Commit 2dc82e0

Browse files
committed
Enhance and begin using error handling framework
Add automatic appending of callstack to error handling framework when CORE_LOGGING_ERROR is set to debug via core.yaml or calling "peer logging setlevel error debug". Implement error handling framework for peer logging CLI. Change-Id: Ia65bae43f9c0a10cafa0596d9b65e2231aceac24 Signed-off-by: Will Lahti <[email protected]>
1 parent da16559 commit 2dc82e0

File tree

11 files changed

+236
-60
lines changed

11 files changed

+236
-60
lines changed

core/errors/errors.go

+23-18
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,21 @@ import (
2121
"encoding/json"
2222
"fmt"
2323
"runtime"
24+
25+
"github.com/hyperledger/fabric/flogging"
26+
logging "github.com/op/go-logging"
2427
)
2528

2629
// MaxCallStackLength is the maximum length of the stored call stack
2730
const MaxCallStackLength = 30
2831

2932
// ComponentCode shows the originating component/module
30-
type ComponentCode uint
33+
type ComponentCode string
3134

3235
// ReasonCode for low level error description
33-
type ReasonCode uint
36+
type ReasonCode string
3437

35-
// Return codes
36-
const (
37-
Utility ComponentCode = iota
38-
)
39-
40-
// Result codes
41-
const (
42-
// Placeholders
43-
UnknownError ReasonCode = iota
44-
ErrorWithArg ReasonCode = 1
45-
)
38+
var errorLogger = logging.MustGetLogger("error")
4639

4740
// CallStackError is a general interface for
4841
// Fabric errors
@@ -67,7 +60,7 @@ func init() {
6760
}
6861

6962
func initErrors() {
70-
e := json.Unmarshal([]byte(errorCodes), &emap)
63+
e := json.Unmarshal([]byte(errorMapping), &emap)
7164
if e != nil {
7265
panic(e)
7366
}
@@ -98,7 +91,7 @@ func newHLError(debug bool) *hlError {
9891

9992
func setupHLError(e *hlError, debug bool) {
10093
e.componentcode = Utility
101-
e.reasoncode = UnknownError
94+
e.reasoncode = UtilityUnknownError
10295
if !debug {
10396
e.stackGetter = noopGetStack
10497
return
@@ -132,20 +125,28 @@ func (h *hlError) GetReasonCode() ReasonCode {
132125

133126
// GetErrorCode returns a formatted error code string
134127
func (h *hlError) GetErrorCode() string {
135-
return fmt.Sprintf("%d-%d", h.componentcode, h.reasoncode)
128+
return fmt.Sprintf("%s-%s", h.componentcode, h.reasoncode)
136129
}
137130

138131
// Message returns the corresponding error message for this error in default
139132
// language.
140133
// TODO - figure out the best way to read in system language instead of using
141134
// hard-coded default language
142135
func (h *hlError) Message() string {
143-
return fmt.Sprintf(emap[fmt.Sprintf("%d", h.componentcode)][fmt.Sprintf("%d", h.reasoncode)][language], h.args...)
136+
// initialize logging level for errors from core.yaml. it can also be set
137+
// for code running on the peer dynamically via CLI using
138+
// "peer logging setlevel error <log-level>"
139+
errorLogLevelString, _ := flogging.GetModuleLogLevel("error")
140+
if errorLogLevelString == logging.DEBUG.String() {
141+
messageWithCallStack := fmt.Sprintf(emap[fmt.Sprintf("%s", h.componentcode)][fmt.Sprintf("%s", h.reasoncode)][language], h.args...) + "\n" + h.GetStack()
142+
return messageWithCallStack
143+
}
144+
return fmt.Sprintf(emap[fmt.Sprintf("%s", h.componentcode)][fmt.Sprintf("%s", h.reasoncode)][language], h.args...)
144145
}
145146

146147
// MessageIn returns the corresponding error message for this error in 'language'
147148
func (h *hlError) MessageIn(language string) string {
148-
return fmt.Sprintf(emap[fmt.Sprintf("%d", h.componentcode)][fmt.Sprintf("%d", h.reasoncode)][language], h.args...)
149+
return fmt.Sprintf(emap[fmt.Sprintf("%s", h.componentcode)][fmt.Sprintf("%s", h.reasoncode)][language], h.args...)
149150
}
150151

151152
// Error creates a CallStackError using a specific Component Code and
@@ -174,6 +175,10 @@ func getStack(stack callstack) string {
174175
if stack == nil {
175176
return fmt.Sprintf("No call stack available")
176177
}
178+
// this removes the core/errors module calls from the callstack because they
179+
// are not useful for debugging
180+
const firstNonErrorModuleCall int = 2
181+
stack = stack[firstNonErrorModuleCall:]
177182
for _, pc := range stack {
178183
f := runtime.FuncForPC(pc)
179184
file, line := f.FileLine(pc)

core/errors/errors_json.go

+67-7
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,71 @@
1616

1717
package errors
1818

19-
const errorCodes string = `
20-
{"0" :
21-
{"0" :
22-
{"en": "An unknown error occurred."},
23-
"1":
24-
{"en": "An error occurred: %s"}
25-
}
19+
// Component codes - should be set to the same key in the errorCodes json below
20+
const (
21+
Utility ComponentCode = "Utility"
22+
Logging ComponentCode = "Logging"
23+
Peer ComponentCode = "Peer"
24+
)
25+
26+
// Reason codes - should be set to the same key in the errorCodes json below
27+
const (
28+
// Utility errors - placeholders
29+
UtilityUnknownError ReasonCode = "UtilityUnknownError"
30+
UtilityErrorWithArg ReasonCode = "UtilityErrorWithArg"
31+
32+
// Logging errors
33+
LoggingUnknownError ReasonCode = "LoggingUnknownError"
34+
LoggingErrorWithArg ReasonCode = "LoggingErrorWithArg"
35+
LoggingNoParameters ReasonCode = "LoggingNoParameters"
36+
LoggingNoLogLevelParameter ReasonCode = "LoggingNoLogLevelParameter"
37+
LoggingInvalidLogLevel ReasonCode = "LoggingInvalidLogLevel"
38+
39+
// Peer errors
40+
PeerConnectionError ReasonCode = "PeerConnectionError"
41+
)
42+
43+
// To use this file to define a new component code or reason code, please follow
44+
// these steps:
45+
// 1. Add the new component code below the last component code or add the
46+
// new reason code below the last reason code for the applicable component
47+
// in the json formatted string below
48+
// 2. Define the message in English using "en" as the key
49+
// 3. Add a constant above for each new component/reason code making sure it
50+
// matches the key used in the json formatted string below
51+
// 4. Import "github.com/hyperledger/fabric/core/errors" wherever the error
52+
// will be created.
53+
// 5. Reference the component and reason codes in the call to
54+
// errors.ErrorWithCallstack as follows:
55+
// err = errors.ErrorWithCallstack(errors.Logging, errors.LoggingNoParameters)
56+
// 6. Any code that receives this error will automatically have the callstack
57+
// appended if CORE_LOGGING_ERROR is set to DEBUG in peer/core.yaml, at the
58+
// command line when the peer is started, or if the error module is set
59+
// dynamically using the CLI call "peer logging setlevel error debug"
60+
// 6. For an example in context, see "peer/clilogging/common.go", which creates
61+
// a number of callstack errors, and "peer/clilogging/setlevel.go", which
62+
// receives the errors
63+
const errorMapping string = `
64+
{"Utility" :
65+
{"UtilityUnknownError" :
66+
{"en": "An unknown error occurred."},
67+
"UtilityErrorWithArg":
68+
{"en": "An error occurred: %s"}
69+
},
70+
"Logging":
71+
{"LoggingUnknownError" :
72+
{"en": "A logging error occurred."},
73+
"LoggingErrorWithArg":
74+
{"en": "A logging error occurred: %s"},
75+
"LoggingNoParameters":
76+
{"en": "No parameters provided."},
77+
"LoggingNoLogLevelParameter":
78+
{"en": "No log level provided."},
79+
"LoggingInvalidLogLevel":
80+
{"en": "Invalid log level provided - %s"}
81+
},
82+
"Peer" :
83+
{"PeerConnectionError" :
84+
{"en": "Error trying to connect to local peer: %s"}
85+
}
2686
}`

core/errors/errors_test.go

+95-21
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ package errors
1919
import (
2020
"fmt"
2121
"testing"
22+
23+
"github.com/hyperledger/fabric/flogging"
2224
)
2325

2426
func TestError(t *testing.T) {
25-
e := Error(Utility, UnknownError)
27+
e := Error(Utility, UtilityUnknownError)
2628
s := e.GetStack()
2729
if s != "" {
2830
t.Fatalf("No error stack should have been recorded.")
@@ -31,15 +33,15 @@ func TestError(t *testing.T) {
3133

3234
// TestErrorWithArg tests creating an error with a message argument
3335
func TestErrorWithArg(t *testing.T) {
34-
e := Error(Utility, ErrorWithArg, "arg1")
36+
e := Error(Utility, UtilityErrorWithArg, "arg1")
3537
s := e.GetStack()
3638
if s != "" {
3739
t.Fatalf("No error stack should have been recorded.")
3840
}
3941
}
4042

4143
func TestErrorWithCallstack(t *testing.T) {
42-
e := ErrorWithCallstack(Utility, UnknownError)
44+
e := ErrorWithCallstack(Utility, UtilityUnknownError)
4345
s := e.GetStack()
4446
if s == "" {
4547
t.Fatalf("No error stack was recorded.")
@@ -49,54 +51,126 @@ func TestErrorWithCallstack(t *testing.T) {
4951
// TestErrorWithCallstackAndArg tests creating an error with a callstack and
5052
// message argument
5153
func TestErrorWithCallstackAndArg(t *testing.T) {
52-
e := ErrorWithCallstack(Utility, ErrorWithArg, "arg1")
54+
e := ErrorWithCallstack(Utility, UtilityErrorWithArg, "arg1")
5355
s := e.GetStack()
5456
if s == "" {
5557
t.Fatalf("No error stack was recorded.")
5658
}
5759
}
5860

59-
func oops() CallStackError {
60-
return Error(Utility, UnknownError)
61-
}
62-
6361
func ExampleError() {
64-
err := oops()
62+
// when the 'error' module is set to anything but debug, the callstack will
63+
// not be appended to the error message
64+
flogging.SetModuleLogLevel("error", "warning")
65+
66+
err := ErrorWithCallstack(Utility, UtilityUnknownError)
67+
6568
if err != nil {
6669
fmt.Printf("%s\n", err.Error())
6770
fmt.Printf("%s\n", err.GetErrorCode())
68-
fmt.Printf("%d\n", err.GetComponentCode())
69-
fmt.Printf("%d\n", err.GetReasonCode())
71+
fmt.Printf("%s\n", err.GetComponentCode())
72+
fmt.Printf("%s\n", err.GetReasonCode())
7073
fmt.Printf("%s\n", err.Message())
7174
fmt.Printf("%s\n", err.MessageIn("en"))
7275
// Output:
7376
// An unknown error occurred.
74-
// 0-0
75-
// 0
76-
// 0
77+
// Utility-UtilityUnknownError
78+
// Utility
79+
// UtilityUnknownError
7780
// An unknown error occurred.
7881
// An unknown error occurred.
7982
}
8083
}
8184

8285
// ExampleErrorWithArg tests the output for a sample error with a message
8386
// argument
84-
func ExampleErrorWithArg() {
85-
err := Error(Utility, ErrorWithArg, "arg1")
87+
func ExampleUtilityErrorWithArg() {
88+
// when the 'error' module is set to anything but debug, the callstack will
89+
// not be appended to the error message
90+
flogging.SetModuleLogLevel("error", "warning")
91+
92+
err := ErrorWithCallstack(Utility, UtilityErrorWithArg, "arg1")
8693

8794
if err != nil {
8895
fmt.Printf("%s\n", err.Error())
8996
fmt.Printf("%s\n", err.GetErrorCode())
90-
fmt.Printf("%d\n", err.GetComponentCode())
91-
fmt.Printf("%d\n", err.GetReasonCode())
97+
fmt.Printf("%s\n", err.GetComponentCode())
98+
fmt.Printf("%s\n", err.GetReasonCode())
9299
fmt.Printf("%s\n", err.Message())
93100
fmt.Printf("%s\n", err.MessageIn("en"))
94101
// Output:
95102
// An error occurred: arg1
96-
// 0-1
97-
// 0
98-
// 1
103+
// Utility-UtilityErrorWithArg
104+
// Utility
105+
// UtilityErrorWithArg
99106
// An error occurred: arg1
100107
// An error occurred: arg1
101108
}
102109
}
110+
111+
// ExampleLoggingInvalidLogLevel tests the output for a logging error where
112+
// and an invalid log level has been provided
113+
func ExampleLoggingInvalidLogLevel() {
114+
// when the 'error' module is set to anything but debug, the callstack will
115+
// not be appended to the error message
116+
flogging.SetModuleLogLevel("error", "warning")
117+
118+
err := ErrorWithCallstack(Logging, LoggingInvalidLogLevel, "invalid")
119+
120+
if err != nil {
121+
fmt.Printf("%s\n", err.Error())
122+
fmt.Printf("%s\n", err.GetErrorCode())
123+
fmt.Printf("%s\n", err.GetComponentCode())
124+
fmt.Printf("%s\n", err.GetReasonCode())
125+
fmt.Printf("%s\n", err.Message())
126+
fmt.Printf("%s\n", err.MessageIn("en"))
127+
// Output:
128+
// Invalid log level provided - invalid
129+
// Logging-LoggingInvalidLogLevel
130+
// Logging
131+
// LoggingInvalidLogLevel
132+
// Invalid log level provided - invalid
133+
// Invalid log level provided - invalid
134+
}
135+
}
136+
137+
// ExampleLoggingInvalidLogLevel tests the output for a logging error where
138+
// and an invalid log level has been provided and the stack trace should be
139+
// displayed with the error message
140+
func ExampleLoggingInvalidLogLevel_withCallstack() {
141+
// when the 'error' module is set to debug, the callstack will be appended
142+
// to the error message
143+
flogging.SetModuleLogLevel("error", "debug")
144+
145+
err := ErrorWithCallstack(Logging, LoggingInvalidLogLevel, "invalid")
146+
147+
if err != nil {
148+
fmt.Printf("%s", err.Error())
149+
fmt.Printf("%s\n", err.GetErrorCode())
150+
fmt.Printf("%s\n", err.GetComponentCode())
151+
fmt.Printf("%s\n", err.GetReasonCode())
152+
fmt.Printf("%s", err.Message())
153+
fmt.Printf("%s\n", err.MessageIn("en"))
154+
// Output:
155+
// Invalid log level provided - invalid
156+
// /opt/gopath/src/github.com/hyperledger/fabric/core/errors/errors_test.go:145 github.com/hyperledger/fabric/core/errors.ExampleLoggingInvalidLogLevel_withCallstack
157+
// /opt/go/src/testing/example.go:115 testing.runExample
158+
// /opt/go/src/testing/example.go:38 testing.RunExamples
159+
// /opt/go/src/testing/testing.go:744 testing.(*M).Run
160+
// github.com/hyperledger/fabric/core/errors/_test/_testmain.go:116 main.main
161+
// /opt/go/src/runtime/proc.go:192 runtime.main
162+
// /opt/go/src/runtime/asm_amd64.s:2087 runtime.goexit
163+
// Logging-LoggingInvalidLogLevel
164+
// Logging
165+
// LoggingInvalidLogLevel
166+
// Invalid log level provided - invalid
167+
// /opt/gopath/src/github.com/hyperledger/fabric/core/errors/errors_test.go:145 github.com/hyperledger/fabric/core/errors.ExampleLoggingInvalidLogLevel_withCallstack
168+
// /opt/go/src/testing/example.go:115 testing.runExample
169+
// /opt/go/src/testing/example.go:38 testing.RunExamples
170+
// /opt/go/src/testing/testing.go:744 testing.(*M).Run
171+
// github.com/hyperledger/fabric/core/errors/_test/_testmain.go:116 main.main
172+
// /opt/go/src/runtime/proc.go:192 runtime.main
173+
// /opt/go/src/runtime/asm_amd64.s:2087 runtime.goexit
174+
// Invalid log level provided - invalid
175+
}
176+
}

flogging/logging.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func GetModuleLogLevel(module string) (string, error) {
106106
// flogging/logging.go
107107
level := logging.GetLevel(module).String()
108108

109-
loggingLogger.Infof("Module '%s' logger enabled for log level: %s", module, level)
109+
loggingLogger.Debugf("Module '%s' logger enabled for log level: %s", module, level)
110110

111111
return level, nil
112112
}
@@ -121,7 +121,7 @@ func SetModuleLogLevel(module string, logLevel string) (string, error) {
121121
loggingLogger.Warningf("Invalid logging level: %s - ignored", logLevel)
122122
} else {
123123
logging.SetLevel(logging.Level(level), module)
124-
loggingLogger.Infof("Module '%s' logger enabled for log level: %s", module, level)
124+
loggingLogger.Debugf("Module '%s' logger enabled for log level: %s", module, level)
125125
}
126126

127127
logLevelString := level.String()

0 commit comments

Comments
 (0)