Skip to content

Commit 60503cf

Browse files
committed
Add error handling framework
Change-Id: I79e914a63fe0834e165c799bc39065613ea5d729 Signed-off-by: Gabor Hosszu <[email protected]>
1 parent 4843e7f commit 60503cf

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

core/errors/errors.go

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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+
"encoding/json"
22+
"fmt"
23+
"runtime"
24+
)
25+
26+
// MaxCallStackLength is the maximum length of the stored call stack
27+
const MaxCallStackLength = 30
28+
29+
// ComponentCode shows the originating component/module
30+
type ComponentCode uint
31+
32+
// ReasonCode for low level error description
33+
type ReasonCode uint
34+
35+
// Return codes
36+
const (
37+
Utility ComponentCode = iota
38+
)
39+
40+
// Result codes
41+
const (
42+
// Placeholder
43+
UnknownError ReasonCode = iota
44+
)
45+
46+
// CallStackError is a general interface for
47+
// Fabric errors
48+
type CallStackError interface {
49+
error
50+
GetStack() string
51+
GetErrorCode() string
52+
GetComponentCode() ComponentCode
53+
GetReasonCode() ReasonCode
54+
}
55+
56+
type errormap map[string]map[string]map[string]string
57+
58+
var emap errormap
59+
60+
const language string = "en"
61+
62+
func init() {
63+
initErrors()
64+
}
65+
66+
func initErrors() {
67+
e := json.Unmarshal([]byte(errorCodes), &emap)
68+
if e != nil {
69+
panic(e)
70+
}
71+
}
72+
73+
type callstack []uintptr
74+
75+
// the main idea is to have an error package
76+
// HLError is the 'super class' of all errors
77+
// It has a predefined, general error message
78+
// One has to create his own error in order to
79+
// create something more useful
80+
type hlError struct {
81+
stack callstack
82+
componentcode ComponentCode
83+
reasoncode ReasonCode
84+
stackGetter func(callstack) string
85+
}
86+
87+
// newHLError creates a general HL error with a predefined message
88+
// and a stacktrace.
89+
func newHLError(debug bool) *hlError {
90+
e := &hlError{}
91+
setupHLError(e, debug)
92+
return e
93+
}
94+
95+
func setupHLError(e *hlError, debug bool) {
96+
e.componentcode = Utility
97+
e.reasoncode = UnknownError
98+
if !debug {
99+
e.stackGetter = noopGetStack
100+
return
101+
}
102+
e.stackGetter = getStack
103+
stack := make([]uintptr, MaxCallStackLength)
104+
skipCallersAndSetupHL := 2
105+
length := runtime.Callers(skipCallersAndSetupHL, stack[:])
106+
e.stack = stack[:length]
107+
}
108+
109+
// Error comes from the error interface
110+
func (h *hlError) Error() string {
111+
return h.componentcode.Message(h.reasoncode)
112+
}
113+
114+
// GetStack returns the call stack as a string
115+
func (h *hlError) GetStack() string {
116+
return h.stackGetter(h.stack)
117+
}
118+
119+
// GetComponentCode returns the Return code
120+
func (h *hlError) GetComponentCode() ComponentCode {
121+
return h.componentcode
122+
}
123+
124+
// GetReasonCode returns the Reason code
125+
func (h *hlError) GetReasonCode() ReasonCode {
126+
return h.reasoncode
127+
}
128+
129+
// GetErrorCode returns a formatted error code string
130+
func (h *hlError) GetErrorCode() string {
131+
return fmt.Sprintf("%d-%d", h.componentcode, h.reasoncode)
132+
}
133+
134+
// Message returns the corresponding error message for this code in default language
135+
func (c ComponentCode) Message(reasoncode ReasonCode) string {
136+
return emap[fmt.Sprintf("%d", c)][fmt.Sprintf("%d", reasoncode)][language]
137+
}
138+
139+
// MessageIn returns the corresponding error message for this code in 'language'
140+
func (c ComponentCode) MessageIn(reasoncode ReasonCode, language string) string {
141+
return emap[fmt.Sprintf("%d", c)][fmt.Sprintf("%d", reasoncode)][language]
142+
}
143+
144+
// Error creates a CallStackError using a specific Component Code and
145+
// Reason Code (no callstack is recorded)
146+
func Error(componentcode ComponentCode, reasoncode ReasonCode) CallStackError {
147+
return newCustomError(componentcode, reasoncode, false)
148+
}
149+
150+
// ErrorWithCallstack creates a CallStackError using a specific Component Code and
151+
// Reason Code and fills its callstack
152+
func ErrorWithCallstack(componentcode ComponentCode, reasoncode ReasonCode) CallStackError {
153+
return newCustomError(componentcode, reasoncode, true)
154+
}
155+
156+
func newCustomError(componentcode ComponentCode, reasoncode ReasonCode, generateStack bool) CallStackError {
157+
e := &hlError{}
158+
setupHLError(e, generateStack)
159+
e.componentcode = componentcode
160+
e.reasoncode = reasoncode
161+
return e
162+
}
163+
164+
func getStack(stack callstack) string {
165+
buf := bytes.Buffer{}
166+
if stack == nil {
167+
return fmt.Sprintf("No call stack available")
168+
}
169+
for _, pc := range stack {
170+
f := runtime.FuncForPC(pc)
171+
file, line := f.FileLine(pc)
172+
buf.WriteString(fmt.Sprintf("%s:%d %s\n", file, line, f.Name()))
173+
}
174+
175+
return fmt.Sprintf("%s", buf.Bytes())
176+
}
177+
178+
func noopGetStack(stack callstack) string {
179+
return ""
180+
}

core/errors/errors_json.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
const errorCodes string = `
20+
{"0" :
21+
{"0" :
22+
{"en": "An unknown error occured."}
23+
}
24+
}`

core/errors/errors_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
"fmt"
21+
"testing"
22+
)
23+
24+
func TestError(t *testing.T) {
25+
e := Error(Utility, UnknownError)
26+
s := e.GetStack()
27+
if s != "" {
28+
t.Fatalf("No error stack should have been recorded.")
29+
}
30+
}
31+
32+
func TestErrorWithCallstack(t *testing.T) {
33+
e := ErrorWithCallstack(Utility, UnknownError)
34+
s := e.GetStack()
35+
if s == "" {
36+
t.Fatalf("No error stack was recorded.")
37+
}
38+
}
39+
40+
func oops() CallStackError {
41+
return Error(Utility, UnknownError)
42+
}
43+
44+
func ExampleError() {
45+
err := oops()
46+
if err != nil {
47+
fmt.Printf("%s\n", err.Error())
48+
fmt.Printf("%s\n", err.GetErrorCode())
49+
fmt.Printf("%d\n", err.GetComponentCode())
50+
fmt.Printf("%d\n", err.GetReasonCode())
51+
fmt.Printf("%s\n", err.GetComponentCode().Message(err.GetReasonCode()))
52+
fmt.Printf("%s", err.GetComponentCode().MessageIn(err.GetReasonCode(), "en"))
53+
// Output:
54+
// An unknown error occured.
55+
// 0-0
56+
// 0
57+
// 0
58+
// An unknown error occured.
59+
// An unknown error occured.
60+
}
61+
}

0 commit comments

Comments
 (0)