Skip to content

Commit

Permalink
[exporter/sentryexporter] make status more detailed (#13407)
Browse files Browse the repository at this point in the history
* [exporter/sentryexporter] make status more detailed

* [exporter/sentryexporter] make status more detailed: add changelog

* [exporter/sentryexporter] make status more detailed: add issue to changelog
  • Loading branch information
anguisa authored Sep 26, 2022
1 parent 0ea5dc4 commit 36b3f5b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 13 deletions.
77 changes: 66 additions & 11 deletions exporter/sentryexporter/sentry_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,43 @@ const (
otelSentryExporterName = "sentry.opentelemetry"
)

// canonicalCodes maps OpenTelemetry span codes to Sentry's span status.
// See numeric codes in https://github.com/open-telemetry/opentelemetry-proto/blob/6cf77b2f544f6bc7fe1e4b4a8a52e5a42cb50ead/opentelemetry/proto/trace/v1/trace.proto#L303
var canonicalCodes = [...]sentry.SpanStatus{
sentry.SpanStatusUndefined,
sentry.SpanStatusOK,
sentry.SpanStatusUnknown,
// See OpenTelemetry span statuses in https://github.com/open-telemetry/opentelemetry-proto/blob/6cf77b2f544f6bc7fe1e4b4a8a52e5a42cb50ead/opentelemetry/proto/trace/v1/trace.proto#L303

// OpenTelemetry span status can be Unset, Ok, Error. HTTP and Grpc codes contained in tags can make it more detailed.

// canonicalCodesHTTPMap maps some HTTP codes to Sentry's span statuses. See possible mapping in https://develop.sentry.dev/sdk/event-payloads/span/
var canonicalCodesHTTPMap = map[string]sentry.SpanStatus{
"400": sentry.SpanStatusFailedPrecondition, // SpanStatusInvalidArgument, SpanStatusOutOfRange
"401": sentry.SpanStatusUnauthenticated,
"403": sentry.SpanStatusPermissionDenied,
"404": sentry.SpanStatusNotFound,
"409": sentry.SpanStatusAborted, // SpanStatusAlreadyExists
"429": sentry.SpanStatusResourceExhausted,
"499": sentry.SpanStatusCanceled,
"500": sentry.SpanStatusInternalError, // SpanStatusDataLoss, SpanStatusUnknown
"501": sentry.SpanStatusUnimplemented,
"503": sentry.SpanStatusUnavailable,
"504": sentry.SpanStatusDeadlineExceeded,
}

// canonicalCodesGrpcMap maps some GRPC codes to Sentry's span statuses. See description in grpc documentation.
var canonicalCodesGrpcMap = map[string]sentry.SpanStatus{
"1": sentry.SpanStatusCanceled,
"2": sentry.SpanStatusUnknown,
"3": sentry.SpanStatusInvalidArgument,
"4": sentry.SpanStatusDeadlineExceeded,
"5": sentry.SpanStatusNotFound,
"6": sentry.SpanStatusAlreadyExists,
"7": sentry.SpanStatusPermissionDenied,
"8": sentry.SpanStatusResourceExhausted,
"9": sentry.SpanStatusFailedPrecondition,
"10": sentry.SpanStatusAborted,
"11": sentry.SpanStatusOutOfRange,
"12": sentry.SpanStatusUnimplemented,
"13": sentry.SpanStatusInternalError,
"14": sentry.SpanStatusUnavailable,
"15": sentry.SpanStatusDataLoss,
"16": sentry.SpanStatusUnauthenticated,
}

// SentryExporter defines the Sentry Exporter.
Expand Down Expand Up @@ -235,7 +266,7 @@ func convertToSentrySpan(span ptrace.Span, library pcommon.InstrumentationScope,
tags[k] = v
}

status, message := statusFromSpanStatus(span.Status())
status, message := statusFromSpanStatus(span.Status(), tags)

if message != "" {
tags["status_message"] = message
Expand Down Expand Up @@ -360,13 +391,37 @@ func generateTagsFromAttributes(attrs pcommon.Map) map[string]string {
return tags
}

func statusFromSpanStatus(spanStatus ptrace.SpanStatus) (status sentry.SpanStatus, message string) {
func statusFromSpanStatus(spanStatus ptrace.SpanStatus, tags map[string]string) (status sentry.SpanStatus, message string) {
code := spanStatus.Code()
if code < 0 || int(code) >= len(canonicalCodes) {
if code < 0 || int(code) > 2 {
return sentry.SpanStatusUnknown, fmt.Sprintf("error code %d", code)
}

return canonicalCodes[code], spanStatus.Message()
httpCode, foundHTTPCode := tags["http.status_code"]
grpcCode, foundGrpcCode := tags["rpc.grpc.status_code"]
var sentryStatus sentry.SpanStatus
switch {
case code == 1 || code == 0:
sentryStatus = sentry.SpanStatusOK
case foundHTTPCode:
httpStatus, foundHTTPStatus := canonicalCodesHTTPMap[httpCode]
switch {
case foundHTTPStatus:
sentryStatus = httpStatus
default:
sentryStatus = sentry.SpanStatusUnknown
}
case foundGrpcCode:
grpcStatus, foundGrpcStatus := canonicalCodesGrpcMap[grpcCode]
switch {
case foundGrpcStatus:
sentryStatus = grpcStatus
default:
sentryStatus = sentry.SpanStatusUnknown
}
default:
sentryStatus = sentry.SpanStatusUnknown
}
return sentryStatus, spanStatus.Message()
}

// spanIsTransaction determines if a span should be sent to Sentry as a transaction.
Expand Down
51 changes: 49 additions & 2 deletions exporter/sentryexporter/sentry_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,15 +483,17 @@ type SpanStatusCase struct {
// output
status sentry.SpanStatus
message string
tags map[string]string
}

func TestStatusFromSpanStatus(t *testing.T) {
testCases := []SpanStatusCase{
{
testName: "with empty status",
spanStatus: ptrace.NewSpanStatus(),
status: sentry.SpanStatusUndefined,
status: sentry.SpanStatusOK,
message: "",
tags: map[string]string{},
},
{
testName: "with status code",
Expand All @@ -504,6 +506,7 @@ func TestStatusFromSpanStatus(t *testing.T) {
}(),
status: sentry.SpanStatusUnknown,
message: "message",
tags: map[string]string{},
},
{
testName: "with unimplemented status code",
Expand All @@ -516,12 +519,56 @@ func TestStatusFromSpanStatus(t *testing.T) {
}(),
status: sentry.SpanStatusUnknown,
message: "error code 1337",
tags: map[string]string{},
},
{
testName: "with ok status code",
spanStatus: func() ptrace.SpanStatus {
spanStatus := ptrace.NewSpanStatus()
spanStatus.SetMessage("message")
spanStatus.SetCode(ptrace.StatusCodeOk)

return spanStatus
}(),
status: sentry.SpanStatusOK,
message: "message",
tags: map[string]string{},
},
{
testName: "with 400 http status code",
spanStatus: func() ptrace.SpanStatus {
spanStatus := ptrace.NewSpanStatus()
spanStatus.SetMessage("message")
spanStatus.SetCode(ptrace.StatusCodeError)

return spanStatus
}(),
status: sentry.SpanStatusUnauthenticated,
message: "message",
tags: map[string]string{
"http.status_code": "401",
},
},
{
testName: "with canceled grpc status code",
spanStatus: func() ptrace.SpanStatus {
spanStatus := ptrace.NewSpanStatus()
spanStatus.SetMessage("message")
spanStatus.SetCode(ptrace.StatusCodeError)

return spanStatus
}(),
status: sentry.SpanStatusCanceled,
message: "message",
tags: map[string]string{
"rpc.grpc.status_code": "1",
},
},
}

for _, test := range testCases {
t.Run(test.testName, func(t *testing.T) {
status, message := statusFromSpanStatus(test.spanStatus)
status, message := statusFromSpanStatus(test.spanStatus, test.tags)
assert.Equal(t, test.status, status)
assert.Equal(t, test.message, message)
})
Expand Down
16 changes: 16 additions & 0 deletions unreleased/sentry_exporter_statuses.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: sentryexporter

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Make sentry status more detailed - use HTTP and Grpc codes from tags

# One or more tracking issues related to the change
issues: [13407]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

0 comments on commit 36b3f5b

Please sign in to comment.