github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/errors/errors.go (about) 1 /* 2 Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package errors 18 19 import ( 20 "bytes" 21 "fmt" 22 "runtime" 23 "strings" 24 25 "github.com/hyperledger/fabric/common/flogging" 26 logging "github.com/op/go-logging" 27 ) 28 29 // MaxCallStackLength is the maximum length of the stored call stack 30 const MaxCallStackLength = 30 31 32 var errorLogger = logging.MustGetLogger("error") 33 34 // CallStackError is a general interface for 35 // Fabric errors 36 type CallStackError interface { 37 error 38 GetStack() string 39 GetErrorCode() string 40 GetComponentCode() string 41 GetReasonCode() string 42 Message() string 43 } 44 45 type callstack []uintptr 46 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 stack callstack 54 componentcode string 55 reasoncode string 56 message string 57 args []interface{} 58 stackGetter func(callstack) string 59 } 60 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 { 74 e.stackGetter = noopGetStack 75 return 76 } 77 e.stackGetter = getStack 78 stack := make([]uintptr, MaxCallStackLength) 79 skipCallersAndSetupHL := 2 80 length := runtime.Callers(skipCallersAndSetupHL, stack[:]) 81 e.stack = stack[:length] 82 } 83 84 // Error comes from the error interface 85 func (h *hlError) Error() string { 86 return h.Message() 87 } 88 89 // GetStack returns the call stack as a string 90 func (h *hlError) GetStack() string { 91 return h.stackGetter(h.stack) 92 } 93 94 // GetComponentCode returns the component name 95 func (h *hlError) GetComponentCode() string { 96 return h.componentcode 97 } 98 99 // GetReasonCode returns the reason code - i.e. why the error occurred 100 func (h *hlError) GetReasonCode() string { 101 return h.reasoncode 102 } 103 104 // GetErrorCode returns a formatted error code string 105 func (h *hlError) GetErrorCode() string { 106 return fmt.Sprintf("%s_%s", h.componentcode, h.reasoncode) 107 } 108 109 // Message returns the corresponding error message for this error in default 110 // language. 111 func (h *hlError) Message() string { 112 message := h.GetErrorCode() + " - " + fmt.Sprintf(h.message, h.args...) 113 114 // 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()) 123 } 124 } 125 126 return message 127 } 128 129 func appendCallStack(message string, callstack string) string { 130 messageWithCallStack := message + "\n" + callstack 131 132 return messageWithCallStack 133 } 134 135 // Error creates a CallStackError using a specific Component Code and 136 // Reason Code (no callstack is recorded) 137 func Error(componentcode string, reasoncode string, message string, args ...interface{}) CallStackError { 138 return newCustomError(componentcode, reasoncode, message, false, args...) 139 } 140 141 // ErrorWithCallstack creates a CallStackError using a specific Component Code and 142 // Reason Code and fills its callstack 143 func ErrorWithCallstack(componentcode string, reasoncode string, message string, args ...interface{}) CallStackError { 144 return newCustomError(componentcode, reasoncode, message, true, args...) 145 } 146 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) 152 } 153 if reasoncode != "" { 154 e.reasoncode = strings.ToUpper(reasoncode) 155 } 156 if message != "" { 157 e.message = message 158 } 159 e.args = args 160 return e 161 } 162 163 func getStack(stack callstack) string { 164 buf := bytes.Buffer{} 165 if stack == nil { 166 return fmt.Sprintf("No call stack available") 167 } 168 // this removes the core/errors module calls from the callstack because they 169 // are not useful for debugging 170 const firstNonErrorModuleCall int = 2 171 stack = stack[firstNonErrorModuleCall:] 172 for _, pc := range stack { 173 f := runtime.FuncForPC(pc) 174 file, line := f.FileLine(pc) 175 buf.WriteString(fmt.Sprintf("%s:%d %s\n", file, line, f.Name())) 176 } 177 178 return fmt.Sprintf("%s", buf.Bytes()) 179 } 180 181 func noopGetStack(stack callstack) string { 182 return "" 183 }