1
1
/*
2
2
Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved.
3
+ Copyright IBM Corp. 2017 All Rights Reserved.
3
4
4
5
Licensed under the Apache License, Version 2.0 (the "License");
5
6
you may not use this file except in compliance with the License.
@@ -19,8 +20,8 @@ package errors
19
20
import (
20
21
"bytes"
21
22
"fmt"
23
+ "regexp"
22
24
"runtime"
23
- "strings"
24
25
25
26
"github.com/hyperledger/fabric/common/flogging"
26
27
logging "github.com/op/go-logging"
@@ -29,135 +30,151 @@ import (
29
30
// MaxCallStackLength is the maximum length of the stored call stack
30
31
const MaxCallStackLength = 30
31
32
32
- var errorLogger = logging .MustGetLogger ("error" )
33
+ var (
34
+ errorLogger = logging .MustGetLogger ("error" )
35
+ componentPattern = "[A-Za-z]{3}"
36
+ reasonPattern = "[0-9]{3}"
37
+ )
33
38
34
- // CallStackError is a general interface for
35
- // Fabric errors
39
+ // CallStackError is a general interface for Fabric errors
36
40
type CallStackError interface {
37
41
error
38
42
GetStack () string
39
43
GetErrorCode () string
40
44
GetComponentCode () string
41
45
GetReasonCode () string
42
46
Message () string
47
+ GenerateStack (bool ) CallStackError
48
+ WrapError (error ) CallStackError
43
49
}
44
50
45
51
type callstack []uintptr
46
52
47
- // the main idea is to have an error package
48
- // HLError is the 'super class' of all errors
49
- // It has a predefined, general error message
50
- // One has to create his own error in order to
51
- // create something more useful
52
- type hlError struct {
53
+ // callError is the 'super class' of all errors
54
+ type callError struct {
53
55
stack callstack
54
56
componentcode string
55
57
reasoncode string
56
58
message string
57
59
args []interface {}
58
60
stackGetter func (callstack ) string
61
+ prevErr error
59
62
}
60
63
61
- // newHLError creates a general HL error with a predefined message
62
- // and a stacktrace.
63
- func newHLError (debug bool ) * hlError {
64
- e := & hlError {}
65
- setupHLError (e , debug )
66
- return e
67
- }
68
-
69
- func setupHLError (e * hlError , debug bool ) {
70
- e .componentcode = "UTILITY"
71
- e .reasoncode = "UNKNOWNERROR"
72
- e .message = "An unknown error occurred."
73
- if ! debug {
64
+ func setupCallError (e * callError , generateStack bool ) {
65
+ if ! generateStack {
74
66
e .stackGetter = noopGetStack
75
67
return
76
68
}
77
69
e .stackGetter = getStack
78
70
stack := make ([]uintptr , MaxCallStackLength )
79
- skipCallersAndSetupHL := 2
80
- length := runtime .Callers (skipCallersAndSetupHL , stack [:])
71
+ skipCallersAndSetup := 2
72
+ length := runtime .Callers (skipCallersAndSetup , stack [:])
81
73
e .stack = stack [:length ]
82
74
}
83
75
84
76
// Error comes from the error interface
85
- func (h * hlError ) Error () string {
86
- return h .Message ()
77
+ func (e * callError ) Error () string {
78
+ return e .Message ()
87
79
}
88
80
89
81
// GetStack returns the call stack as a string
90
- func (h * hlError ) GetStack () string {
91
- return h .stackGetter (h .stack )
82
+ func (e * callError ) GetStack () string {
83
+ return e .stackGetter (e .stack )
92
84
}
93
85
94
86
// GetComponentCode returns the component name
95
- func (h * hlError ) GetComponentCode () string {
96
- return h .componentcode
87
+ func (e * callError ) GetComponentCode () string {
88
+ return e .componentcode
97
89
}
98
90
99
91
// GetReasonCode returns the reason code - i.e. why the error occurred
100
- func (h * hlError ) GetReasonCode () string {
101
- return h .reasoncode
92
+ func (e * callError ) GetReasonCode () string {
93
+ return e .reasoncode
102
94
}
103
95
104
96
// GetErrorCode returns a formatted error code string
105
- func (h * hlError ) GetErrorCode () string {
106
- return fmt .Sprintf ("%s_ %s" , h .componentcode , h .reasoncode )
97
+ func (e * callError ) GetErrorCode () string {
98
+ return fmt .Sprintf ("%s: %s" , e .componentcode , e .reasoncode )
107
99
}
108
100
109
101
// Message returns the corresponding error message for this error in default
110
102
// language.
111
- func (h * hlError ) Message () string {
112
- message := h .GetErrorCode () + " - " + fmt .Sprintf (h .message , h .args ... )
113
-
103
+ func (e * callError ) Message () string {
104
+ message := e .GetErrorCode () + " - " + fmt .Sprintf (e .message , e .args ... )
114
105
// check that the error has a callstack before proceeding
115
- if h .GetStack () != "" {
116
- // initialize logging level for errors from core.yaml. it can also be set
117
- // for code running on the peer dynamically via CLI using
118
- // "peer logging setlevel error <log-level>"
119
- errorLogLevelString := flogging .GetModuleLevel ("error" )
120
-
121
- if errorLogLevelString == logging .DEBUG .String () {
122
- message = appendCallStack (message , h .GetStack ())
106
+ if e .GetStack () != "" {
107
+ // stacktrace is enabled when `logging.error` in core.yaml is set to
108
+ // DEBUG. it can also be toggled for code running on the peer dynamically
109
+ // via CLI using `peer logging setlevel error <log-level>`
110
+ errorLevel := flogging .GetModuleLevel ("error" )
111
+ if errorLevel == logging .DEBUG .String () {
112
+ message = appendCallStack (message , e .GetStack ())
123
113
}
124
114
}
125
-
115
+ if e .prevErr != nil {
116
+ message += "\n Caused by: " + e .prevErr .Error ()
117
+ }
126
118
return message
127
119
}
128
120
129
121
func appendCallStack (message string , callstack string ) string {
130
- messageWithCallStack := message + "\n " + callstack
131
-
132
- return messageWithCallStack
122
+ return message + "\n " + callstack
133
123
}
134
124
135
- // Error creates a CallStackError using a specific Component Code and
136
- // Reason Code (no callstack is recorded )
125
+ // Error creates a CallStackError using a specific component code and reason
126
+ // code (no callstack is generated )
137
127
func Error (componentcode string , reasoncode string , message string , args ... interface {}) CallStackError {
138
- return newCustomError (componentcode , reasoncode , message , false , args ... )
128
+ return newError (componentcode , reasoncode , message , args ... ). GenerateStack ( false )
139
129
}
140
130
141
- // ErrorWithCallstack creates a CallStackError using a specific Component Code and
142
- // Reason Code and fills its callstack
131
+ // ErrorWithCallstack creates a CallStackError using a specific component code
132
+ // and reason code and generates its callstack
143
133
func ErrorWithCallstack (componentcode string , reasoncode string , message string , args ... interface {}) CallStackError {
144
- return newCustomError (componentcode , reasoncode , message , true , args ... )
134
+ return newError (componentcode , reasoncode , message , args ... ).GenerateStack (true )
135
+ }
136
+
137
+ func newError (componentcode string , reasoncode string , message string , args ... interface {}) CallStackError {
138
+ e := & callError {}
139
+ e .setErrorFields (componentcode , reasoncode , message , args ... )
140
+ return e
141
+ }
142
+
143
+ // GenerateStack generates the callstack for a CallStackError
144
+ func (e * callError ) GenerateStack (flag bool ) CallStackError {
145
+ setupCallError (e , flag )
146
+ return e
147
+ }
148
+
149
+ // WrapError wraps a previous error into a CallStackError
150
+ func (e * callError ) WrapError (prevErr error ) CallStackError {
151
+ e .prevErr = prevErr
152
+ return e
145
153
}
146
154
147
- func newCustomError (componentcode string , reasoncode string , message string , generateStack bool , args ... interface {}) CallStackError {
148
- e := & hlError {}
149
- setupHLError (e , generateStack )
150
- if componentcode != "" {
151
- e .componentcode = strings .ToUpper (componentcode )
155
+ func (e * callError ) setErrorFields (componentcode string , reasoncode string , message string , args ... interface {}) {
156
+ if isValidComponentOrReasonCode (componentcode , componentPattern ) {
157
+ e .componentcode = componentcode
152
158
}
153
- if reasoncode != "" {
154
- e .reasoncode = strings . ToUpper ( reasoncode )
159
+ if isValidComponentOrReasonCode ( reasoncode , reasonPattern ) {
160
+ e .reasoncode = reasoncode
155
161
}
156
162
if message != "" {
157
163
e .message = message
158
164
}
159
165
e .args = args
160
- return e
166
+ }
167
+
168
+ func isValidComponentOrReasonCode (componentOrReasonCode string , regExp string ) bool {
169
+ if componentOrReasonCode == "" {
170
+ return false
171
+ }
172
+ re , _ := regexp .Compile (regExp )
173
+ matched := re .FindString (componentOrReasonCode )
174
+ if len (matched ) != len (componentOrReasonCode ) {
175
+ return false
176
+ }
177
+ return true
161
178
}
162
179
163
180
func getStack (stack callstack ) string {
@@ -169,12 +186,15 @@ func getStack(stack callstack) string {
169
186
// are not useful for debugging
170
187
const firstNonErrorModuleCall int = 2
171
188
stack = stack [firstNonErrorModuleCall :]
172
- for _ , pc := range stack {
189
+ for i , pc := range stack {
173
190
f := runtime .FuncForPC (pc )
174
191
file , line := f .FileLine (pc )
175
- buf .WriteString (fmt .Sprintf ("%s:%d %s\n " , file , line , f .Name ()))
192
+ if i != len (stack )- 1 {
193
+ buf .WriteString (fmt .Sprintf ("%s:%d %s\n " , file , line , f .Name ()))
194
+ } else {
195
+ buf .WriteString (fmt .Sprintf ("%s:%d %s" , file , line , f .Name ()))
196
+ }
176
197
}
177
-
178
198
return fmt .Sprintf ("%s" , buf .Bytes ())
179
199
}
180
200
0 commit comments