-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "Add error handling framework"
- Loading branch information
Showing
3 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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."} | ||
} | ||
}` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
} | ||
} |