-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathstatus_code.go
197 lines (175 loc) · 5.52 KB
/
status_code.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
191
192
193
194
195
196
197
// Copyright The OpenTelemetry Authors
//
// 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 zipkin
import (
"fmt"
"math"
"strconv"
tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
tracetranslator "go.opentelemetry.io/collector/translator/trace"
)
type status struct {
codePtr *int32
message string
}
// statusMapper contains codes translated from different sources to OC status codes
type statusMapper struct {
// oc status code extracted from "status.code" tags
fromStatus status
// oc status code extracted from "census.status_code" tags
fromCensus status
// oc status code extracted from "http.status_code" tags
fromHTTP status
// oc status code extracted from "error" tags
fromErrorTag status
// oc status code 'unknown' when the "error" tag exists but is invalid
fromErrorTagUnknown status
}
// ocStatus returns an OC status from the best possible extraction source.
// It'll first try to return status extracted from "census.status_code" to account for zipkin
// then fallback on code extracted from "status.code" tags
// and finally fallback on code extracted and translated from "http.status_code"
// ocStatus must be called after all tags/attributes are processed with the `fromAttribute` method.
func (m *statusMapper) ocStatus() *tracepb.Status {
var s status
switch {
case m.fromCensus.codePtr != nil:
s = m.fromCensus
case m.fromStatus.codePtr != nil:
s = m.fromStatus
case m.fromErrorTag.codePtr != nil:
s = m.fromErrorTag
if m.fromCensus.message != "" {
s.message = m.fromCensus.message
} else if m.fromStatus.message != "" {
s.message = m.fromStatus.message
}
case m.fromHTTP.codePtr != nil:
s = m.fromHTTP
default:
s = m.fromErrorTagUnknown
}
if s.codePtr != nil {
code := int32(0)
if s.codePtr != nil {
code = *s.codePtr
}
return &tracepb.Status{
Code: code,
Message: s.message,
}
}
return nil
}
func (m *statusMapper) fromAttribute(key string, attrib *tracepb.AttributeValue) bool {
switch key {
case tracetranslator.TagZipkinCensusCode:
code, err := attribToStatusCode(attrib)
if err == nil {
m.fromCensus.codePtr = &code
}
return true
case tracetranslator.TagZipkinCensusMsg, tracetranslator.TagZipkinOpenCensusMsg:
m.fromCensus.message = attrib.GetStringValue().GetValue()
return true
case tracetranslator.TagStatusCode:
code, err := attribToStatusCode(attrib)
if err == nil {
m.fromStatus.codePtr = &code
}
return true
case tracetranslator.TagStatusMsg:
m.fromStatus.message = attrib.GetStringValue().GetValue()
return true
case tracetranslator.TagHTTPStatusCode:
httpCode, err := attribToStatusCode(attrib)
if err == nil {
code := tracetranslator.OCStatusCodeFromHTTP(httpCode)
m.fromHTTP.codePtr = &code
}
case tracetranslator.TagHTTPStatusMsg:
m.fromHTTP.message = attrib.GetStringValue().GetValue()
case tracetranslator.TagError:
code, ok := extractStatusFromError(attrib)
if ok {
m.fromErrorTag.codePtr = code
return true
}
m.fromErrorTagUnknown.codePtr = code
}
return false
}
// attribToStatusCode maps an integer or string attribute value to a status code.
// The function return nil if the value is of another type or cannot be converted to an int32 value.
func attribToStatusCode(attr *tracepb.AttributeValue) (int32, error) {
if attr == nil {
return 0, fmt.Errorf("nil attribute")
}
switch val := attr.Value.(type) {
case *tracepb.AttributeValue_IntValue:
return toInt32(int(val.IntValue))
case *tracepb.AttributeValue_StringValue:
i, err := strconv.Atoi(val.StringValue.GetValue())
if err != nil {
return 0, err
}
return toInt32(i)
}
return 0, fmt.Errorf("invalid attribute type")
}
func toInt32(i int) (int32, error) {
if i <= math.MaxInt32 && i >= math.MinInt32 {
return int32(i), nil
}
return 0, fmt.Errorf("outside of the int32 range")
}
func extractStatusFromError(attrib *tracepb.AttributeValue) (*int32, bool) {
// The status is stored with the "error" key
// See https://github.com/census-instrumentation/opencensus-go/blob/1eb9a13c7dd02141e065a665f6bf5c99a090a16a/exporter/zipkin/zipkin.go#L160-L165
var unknown int32 = 2
switch val := attrib.Value.(type) {
case *tracepb.AttributeValue_StringValue:
canonicalCodeStr := val.StringValue.GetValue()
if canonicalCodeStr == "" {
return nil, true
}
code, set := canonicalCodesMap[canonicalCodeStr]
if set {
return &code, true
}
default:
break
}
return &unknown, false
}
var canonicalCodesMap = map[string]int32{
// https://github.com/googleapis/googleapis/blob/bee79fbe03254a35db125dc6d2f1e9b752b390fe/google/rpc/code.proto#L33-L186
"OK": 0,
"CANCELLED": 1,
"UNKNOWN": 2,
"INVALID_ARGUMENT": 3,
"DEADLINE_EXCEEDED": 4,
"NOT_FOUND": 5,
"ALREADY_EXISTS": 6,
"PERMISSION_DENIED": 7,
"RESOURCE_EXHAUSTED": 8,
"FAILED_PRECONDITION": 9,
"ABORTED": 10,
"OUT_OF_RANGE": 11,
"UNIMPLEMENTED": 12,
"INTERNAL": 13,
"UNAVAILABLE": 14,
"DATA_LOSS": 15,
"UNAUTHENTICATED": 16,
}