-
Notifications
You must be signed in to change notification settings - Fork 14
/
errors.go
142 lines (120 loc) · 3.39 KB
/
errors.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
package common
import (
"context"
"fmt"
"net/http"
)
type Kind int
const (
UnknownError Kind = iota
BadRequestError
InternalError
UnauthorizedError
DownstreamUnavailableError
DownstreamTimeoutError
DownstreamUnauthorizedError // 401 from downstream
DownstreamUnexpectedResponseError // unexpected response from downstream
DownstreamResponseError // application-leve error response from downstream
)
const downstreamResponseSnippetMaxLength = 128
func (k Kind) String() string {
switch k {
case BadRequestError:
return "Missing one or more of the required parameters"
case InternalError:
return "Internal Server Error"
case UnauthorizedError:
return "Unauthorized error"
case DownstreamUnavailableError:
return "Downstream system is unavailable"
case DownstreamTimeoutError:
return "Time out from down stream services"
case DownstreamUnauthorizedError:
return "Unauthorized error from downstream services"
case DownstreamUnexpectedResponseError:
return "Unexpected response from downstream services"
case DownstreamResponseError:
return "Error response from downstream services"
default:
return "Internal Server Error"
}
}
type ErrorKinder interface {
ErrorKind() Kind
}
type ServerError struct {
Kind Kind
Message string
Cause error
}
func (e *ServerError) Error() string {
return fmt.Sprintf("ServerError(Kind=%s, Message=%s, Cause=%s)", e.Kind, e.Message, e.Cause)
}
func (e *ServerError) ErrorKind() Kind {
return e.Kind
}
func (e *ServerError) Unwrap() error {
return e.Cause
}
func CreateError(ctx context.Context, kind Kind, message string, cause error) error {
// we may push the error to NR here
if err := CheckContextTimeout(ctx, message, cause); err != nil {
return err
}
switch cause.(type) {
case ErrorKinder, CustomError, wrappedError:
return cause
default:
return &ServerError{Kind: kind, Message: message, Cause: cause}
}
}
func CheckContextTimeout(ctx context.Context, message string, cause error) error {
if ctx.Err() == context.DeadlineExceeded {
return &ServerError{Kind: DownstreamTimeoutError, Message: message, Cause: cause}
}
return nil
}
type DownstreamError struct {
Kind Kind
Response *http.Response
Body []byte
Cause error
}
func (e *DownstreamError) ErrorKind() Kind {
return e.Kind
}
func (e *DownstreamError) Error() string {
return fmt.Sprintf("DownstreamError(Kind=%s, Method=%s, URL=%s, StatusCode=%d, ContentType=%s, ContentLength=%d, Snippet=%s, Cause=%s)",
e.Kind.String(),
e.Response.Request.Method,
e.Response.Request.URL.String(),
e.Response.StatusCode,
e.Response.Header.Get("Content-Type"),
e.Response.ContentLength,
string(e.Body),
e.Cause)
}
func (e *DownstreamError) Unwrap() error {
return e.Cause
}
func CreateDownstreamError(ctx context.Context, kind Kind, response *http.Response, body []byte, cause error) error {
// we may push the error to NR here
// add the request method and url as message, make the troubleshooting easier
if err := CheckContextTimeout(ctx, fmt.Sprintf("%s %s", response.Request.Method, response.Request.URL.String()), cause); err != nil {
return err
}
err := &DownstreamError{
Kind: kind,
Response: response,
Cause: cause,
}
bodyLength := len(body)
switch {
case bodyLength == 0:
case bodyLength > downstreamResponseSnippetMaxLength:
err.Body = body[:downstreamResponseSnippetMaxLength]
default:
err.Body = body
}
return err
}