Skip to content

Commit

Permalink
Merge "Add error handling framework"
Browse files Browse the repository at this point in the history
  • Loading branch information
Srinivasan Muralidharan authored and Gerrit Code Review committed Oct 26, 2016
2 parents 40d9afb + 60503cf commit 0157a51
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
180 changes: 180 additions & 0 deletions core/errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package errors

import (
"bytes"
"encoding/json"
"fmt"
"runtime"
)

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

// ComponentCode shows the originating component/module
type ComponentCode uint

// ReasonCode for low level error description
type ReasonCode uint

// Return codes
const (
Utility ComponentCode = iota
)

// Result codes
const (
// Placeholder
UnknownError ReasonCode = iota
)

// CallStackError is a general interface for
// Fabric errors
type CallStackError interface {
error
GetStack() string
GetErrorCode() string
GetComponentCode() ComponentCode
GetReasonCode() ReasonCode
}

type errormap map[string]map[string]map[string]string

var emap errormap

const language string = "en"

func init() {
initErrors()
}

func initErrors() {
e := json.Unmarshal([]byte(errorCodes), &emap)
if e != nil {
panic(e)
}
}

type callstack []uintptr

// the main idea is to have an error package
// HLError is the 'super class' of all errors
// It has a predefined, general error message
// One has to create his own error in order to
// create something more useful
type hlError struct {
stack callstack
componentcode ComponentCode
reasoncode ReasonCode
stackGetter func(callstack) string
}

// newHLError creates a general HL error with a predefined message
// and a stacktrace.
func newHLError(debug bool) *hlError {
e := &hlError{}
setupHLError(e, debug)
return e
}

func setupHLError(e *hlError, debug bool) {
e.componentcode = Utility
e.reasoncode = UnknownError
if !debug {
e.stackGetter = noopGetStack
return
}
e.stackGetter = getStack
stack := make([]uintptr, MaxCallStackLength)
skipCallersAndSetupHL := 2
length := runtime.Callers(skipCallersAndSetupHL, stack[:])
e.stack = stack[:length]
}

// Error comes from the error interface
func (h *hlError) Error() string {
return h.componentcode.Message(h.reasoncode)
}

// GetStack returns the call stack as a string
func (h *hlError) GetStack() string {
return h.stackGetter(h.stack)
}

// GetComponentCode returns the Return code
func (h *hlError) GetComponentCode() ComponentCode {
return h.componentcode
}

// GetReasonCode returns the Reason code
func (h *hlError) GetReasonCode() ReasonCode {
return h.reasoncode
}

// GetErrorCode returns a formatted error code string
func (h *hlError) GetErrorCode() string {
return fmt.Sprintf("%d-%d", h.componentcode, h.reasoncode)
}

// Message returns the corresponding error message for this code in default language
func (c ComponentCode) Message(reasoncode ReasonCode) string {
return emap[fmt.Sprintf("%d", c)][fmt.Sprintf("%d", reasoncode)][language]
}

// MessageIn returns the corresponding error message for this code in 'language'
func (c ComponentCode) MessageIn(reasoncode ReasonCode, language string) string {
return emap[fmt.Sprintf("%d", c)][fmt.Sprintf("%d", reasoncode)][language]
}

// Error creates a CallStackError using a specific Component Code and
// Reason Code (no callstack is recorded)
func Error(componentcode ComponentCode, reasoncode ReasonCode) CallStackError {
return newCustomError(componentcode, reasoncode, false)
}

// ErrorWithCallstack creates a CallStackError using a specific Component Code and
// Reason Code and fills its callstack
func ErrorWithCallstack(componentcode ComponentCode, reasoncode ReasonCode) CallStackError {
return newCustomError(componentcode, reasoncode, true)
}

func newCustomError(componentcode ComponentCode, reasoncode ReasonCode, generateStack bool) CallStackError {
e := &hlError{}
setupHLError(e, generateStack)
e.componentcode = componentcode
e.reasoncode = reasoncode
return e
}

func getStack(stack callstack) string {
buf := bytes.Buffer{}
if stack == nil {
return fmt.Sprintf("No call stack available")
}
for _, pc := range stack {
f := runtime.FuncForPC(pc)
file, line := f.FileLine(pc)
buf.WriteString(fmt.Sprintf("%s:%d %s\n", file, line, f.Name()))
}

return fmt.Sprintf("%s", buf.Bytes())
}

func noopGetStack(stack callstack) string {
return ""
}
24 changes: 24 additions & 0 deletions core/errors/errors_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package errors

const errorCodes string = `
{"0" :
{"0" :
{"en": "An unknown error occured."}
}
}`
61 changes: 61 additions & 0 deletions core/errors/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package errors

import (
"fmt"
"testing"
)

func TestError(t *testing.T) {
e := Error(Utility, UnknownError)
s := e.GetStack()
if s != "" {
t.Fatalf("No error stack should have been recorded.")
}
}

func TestErrorWithCallstack(t *testing.T) {
e := ErrorWithCallstack(Utility, UnknownError)
s := e.GetStack()
if s == "" {
t.Fatalf("No error stack was recorded.")
}
}

func oops() CallStackError {
return Error(Utility, UnknownError)
}

func ExampleError() {
err := oops()
if err != nil {
fmt.Printf("%s\n", err.Error())
fmt.Printf("%s\n", err.GetErrorCode())
fmt.Printf("%d\n", err.GetComponentCode())
fmt.Printf("%d\n", err.GetReasonCode())
fmt.Printf("%s\n", err.GetComponentCode().Message(err.GetReasonCode()))
fmt.Printf("%s", err.GetComponentCode().MessageIn(err.GetReasonCode(), "en"))
// Output:
// An unknown error occured.
// 0-0
// 0
// 0
// An unknown error occured.
// An unknown error occured.
}
}

0 comments on commit 0157a51

Please sign in to comment.