forked from dgraph-io/dgraph
-
Notifications
You must be signed in to change notification settings - Fork 1
/
error.go
190 lines (167 loc) · 5.44 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
* Copyright 2016 Dgraph Labs, Inc.
*
* 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 x
// This file contains some functions for error handling. Note that we are moving
// towards using x.Trace, i.e., rpc tracing using net/tracer. But for now, these
// functions are useful for simple checks logged on one machine.
// Some common use cases are:
// (1) You receive an error from external lib, and would like to check/log fatal.
// For this, use x.Check, x.Checkf. These will check for err != nil, which is
// more common in Go. If you want to check for boolean being true, use
// x.Assert, x.Assertf.
// (2) You receive an error from external lib, and would like to pass on with some
// stack trace information. In this case, use x.Wrap or x.Wrapf.
// (3) You want to generate a new error with stack trace info. Use x.Errorf.
import (
"bytes"
"context"
"fmt"
"log"
"strings"
"github.com/pkg/errors"
)
// Check logs fatal if err != nil.
func Check(err error) {
if err != nil {
log.Fatalf("%+v", Wrap(err))
}
}
// Checkf is Check with extra info.
func Checkf(err error, format string, args ...interface{}) {
if err != nil {
log.Fatalf("%+v", Wrapf(err, format, args...))
}
}
// Check2 acts as convenience wrapper around Check, using the 2nd argument as error.
func Check2(_ interface{}, err error) {
Check(err)
}
// Check2f acts as convenience wrapper around Checkf, using the 2nd argument as error.
func Check2f(_ interface{}, err error, format string, args ...interface{}) {
Checkf(err, format, args)
}
// AssertTrue asserts that b is true. Otherwise, it would log fatal.
func AssertTrue(b bool) {
if !b {
log.Fatalf("%+v", Errorf("Assert failed"))
}
}
// AssertTruef is AssertTrue with extra info.
func AssertTruef(b bool, format string, args ...interface{}) {
if !b {
log.Fatalf("%+v", Errorf(format, args...))
}
}
// Wrap wraps errors from external lib.
func Wrap(err error) error {
if !*debugMode {
return err
}
return errors.Wrap(err, "")
}
// Wrapf is Wrap with extra info.
func Wrapf(err error, format string, args ...interface{}) error {
if !*debugMode {
if err == nil {
return nil
}
return fmt.Errorf(format+" error: %+v", append(args, err)...)
}
return errors.Wrapf(err, format, args...)
}
// Errorf creates a new error with stack trace, etc.
func Errorf(format string, args ...interface{}) error {
if !*debugMode {
return fmt.Errorf(format, args...)
}
return errors.Errorf(format, args...)
}
// Fatalf logs fatal.
func Fatalf(format string, args ...interface{}) {
log.Fatalf("%+v", Errorf(format, args...))
}
const (
dgraphPrefix = "github.com/dgraph-io/dgraph/"
runtimePrefix = "src/runtime/"
testingPrefix = "src/testing/"
)
func writeLineRef(buf *bytes.Buffer, s string) {
for _, p := range []string{dgraphPrefix, runtimePrefix, testingPrefix} {
idx := strings.Index(s, p)
if idx >= 0 {
buf.WriteString(s[idx+len(p):])
return
}
}
buf.WriteString(strings.TrimSpace(s))
}
func shortenedErrorString(err error) string {
buf := bytes.NewBuffer(make([]byte, 0, 40))
// Here is a sample input:
// some error
// github.com/dgraph-io/dgraph/x.Errorf
// /home/jchiu/go/src/github.com/dgraph-io/dgraph/x/error.go:89
// main.f
// /home/jchiu/go/src/github.com/dgraph-io/dgraph/x/tmp/main.go:10
// main.main
// /home/jchiu/go/src/github.com/dgraph-io/dgraph/x/tmp/main.go:15
// runtime.main
// /usr/lib/go-1.7/src/runtime/proc.go:183
// runtime.goexit
// /usr/lib/go-1.7/src/runtime/asm_amd64.s:2086
// Here is a sample output:
// some error; x.Errorf (x/error.go:90) main.f (x/tmp/main.go:10) main.main (x/tmp/main.go:15) runtime.main (proc.go:183) runtime.goexit (asm_amd64.s:2086)
// First, split into lines.
lines := strings.Split(fmt.Sprintf("%+v\n", err), "\n")
// The tab prefix tells us whether this line is a line reference.
// We have two states. If lineRef is true, the last line we have seen is a line
// reference. The initial value is true.
lineRef := true
// Second, process each line, depending on whether it is a line ref or not.
for _, line := range lines {
if strings.HasPrefix(line, "\t") {
// This is a line reference. We want to shorten the filename and put it
// inside parentheses.
buf.WriteString(" (")
writeLineRef(buf, line)
buf.WriteString(") ")
lineRef = true
} else {
// This line is not a line reference.
if !lineRef {
// If the previous line is not a line reference either, we want to
// separate them by semicolons.
buf.WriteString("; ")
}
// Even though it is not a line reference, it might still contain "dgraph".
// Let's trim that away if possible.
buf.WriteString(strings.Replace(line, dgraphPrefix, "", -1))
lineRef = false
}
}
return buf.String()
}
// TraceError is like Trace but it logs just an error, which may have stacktrace.
func TraceError(ctx context.Context, err error) {
if err == nil {
return
}
if !*debugMode {
Trace(ctx, err.Error())
return
}
Trace(ctx, shortenedErrorString(err))
}