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  }