github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/common/errors/errors.go (about) 1 /* 2 Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved. 3 Copyright IBM Corp. 2017 All Rights Reserved. 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package errors 19 20 import ( 21 "bytes" 22 "fmt" 23 "regexp" 24 "runtime" 25 ) 26 27 // MaxCallStackLength is the maximum length of the stored call stack 28 const MaxCallStackLength = 30 29 30 var ( 31 componentPattern = "[A-Za-z]{3}" 32 reasonPattern = "[0-9]{3}" 33 ) 34 35 // CallStackError is a general interface for Fabric errors 36 type CallStackError interface { 37 error 38 GetStack() string 39 GetErrorCode() string 40 GetComponentCode() string 41 GetReasonCode() string 42 Message() string 43 GenerateStack(bool) CallStackError 44 WrapError(error) CallStackError 45 } 46 47 type callstack []uintptr 48 49 // callError is the 'super class' of all errors 50 type callError struct { 51 stack callstack 52 componentcode string 53 reasoncode string 54 message string 55 args []interface{} 56 stackGetter func(callstack) string 57 prevErr error 58 } 59 60 func setupCallError(e *callError, generateStack bool) { 61 if !generateStack { 62 e.stackGetter = noopGetStack 63 return 64 } 65 e.stackGetter = getStack 66 stack := make([]uintptr, MaxCallStackLength) 67 skipCallersAndSetup := 2 68 length := runtime.Callers(skipCallersAndSetup, stack[:]) 69 e.stack = stack[:length] 70 } 71 72 // Error comes from the error interface - it returns the error message and 73 // appends the callstack, if available 74 func (e *callError) Error() string { 75 message := e.GetErrorCode() + " - " + fmt.Sprintf(e.message, e.args...) 76 // check that the error has a callstack before proceeding 77 if e.GetStack() != "" { 78 message = appendCallStack(message, e.GetStack()) 79 } 80 if e.prevErr != nil { 81 message += "\nCaused by: " + e.prevErr.Error() 82 } 83 return message 84 } 85 86 // GetStack returns the call stack as a string 87 func (e *callError) GetStack() string { 88 return e.stackGetter(e.stack) 89 } 90 91 // GetComponentCode returns the component name 92 func (e *callError) GetComponentCode() string { 93 return e.componentcode 94 } 95 96 // GetReasonCode returns the reason code - i.e. why the error occurred 97 func (e *callError) GetReasonCode() string { 98 return e.reasoncode 99 } 100 101 // GetErrorCode returns a formatted error code string 102 func (e *callError) GetErrorCode() string { 103 return fmt.Sprintf("%s:%s", e.componentcode, e.reasoncode) 104 } 105 106 // Message returns the corresponding error message for this error in default 107 // language. 108 func (e *callError) Message() string { 109 message := e.GetErrorCode() + " - " + fmt.Sprintf(e.message, e.args...) 110 111 if e.prevErr != nil { 112 switch previousError := e.prevErr.(type) { 113 case CallStackError: 114 message += "\nCaused by: " + previousError.Message() 115 default: 116 message += "\nCaused by: " + e.prevErr.Error() 117 } 118 } 119 return message 120 } 121 122 func appendCallStack(message string, callstack string) string { 123 return message + "\n" + callstack 124 } 125 126 // Error creates a CallStackError using a specific component code and reason 127 // code (no callstack is generated) 128 func Error(componentcode string, reasoncode string, message string, args ...interface{}) CallStackError { 129 return newError(componentcode, reasoncode, message, args...).GenerateStack(false) 130 } 131 132 // ErrorWithCallstack creates a CallStackError using a specific component code 133 // and reason code and generates its callstack 134 func ErrorWithCallstack(componentcode string, reasoncode string, message string, args ...interface{}) CallStackError { 135 return newError(componentcode, reasoncode, message, args...).GenerateStack(true) 136 } 137 138 func newError(componentcode string, reasoncode string, message string, args ...interface{}) CallStackError { 139 e := &callError{} 140 e.setErrorFields(componentcode, reasoncode, message, args...) 141 return e 142 } 143 144 // GenerateStack generates the callstack for a CallStackError 145 func (e *callError) GenerateStack(flag bool) CallStackError { 146 setupCallError(e, flag) 147 return e 148 } 149 150 // WrapError wraps a previous error into a CallStackError 151 func (e *callError) WrapError(prevErr error) CallStackError { 152 e.prevErr = prevErr 153 return e 154 } 155 156 func (e *callError) setErrorFields(componentcode string, reasoncode string, message string, args ...interface{}) { 157 if isValidComponentOrReasonCode(componentcode, componentPattern) { 158 e.componentcode = componentcode 159 } 160 if isValidComponentOrReasonCode(reasoncode, reasonPattern) { 161 e.reasoncode = reasoncode 162 } 163 if message != "" { 164 e.message = message 165 } 166 e.args = args 167 } 168 169 func isValidComponentOrReasonCode(componentOrReasonCode string, regExp string) bool { 170 if componentOrReasonCode == "" { 171 return false 172 } 173 re, _ := regexp.Compile(regExp) 174 matched := re.FindString(componentOrReasonCode) 175 if len(matched) != len(componentOrReasonCode) { 176 return false 177 } 178 return true 179 } 180 181 func getStack(stack callstack) string { 182 buf := bytes.Buffer{} 183 if stack == nil { 184 return fmt.Sprintf("No call stack available") 185 } 186 // this removes the core/errors module calls from the callstack because they 187 // are not useful for debugging 188 const firstNonErrorModuleCall int = 2 189 stack = stack[firstNonErrorModuleCall:] 190 for i, pc := range stack { 191 f := runtime.FuncForPC(pc) 192 file, line := f.FileLine(pc) 193 if i != len(stack)-1 { 194 buf.WriteString(fmt.Sprintf("%s:%d %s\n", file, line, f.Name())) 195 } else { 196 buf.WriteString(fmt.Sprintf("%s:%d %s", file, line, f.Name())) 197 } 198 } 199 return fmt.Sprintf("%s", buf.Bytes()) 200 } 201 202 func noopGetStack(stack callstack) string { 203 return "" 204 }