Skip to content

Commit

Permalink
[exporter/datadog]: update error formatting for error spans that are …
Browse files Browse the repository at this point in the history
…not exceptions. (#3701)

* [exporter/datadog]: add better fallbacks and metadata for error formatting

* [exporter/datadog]: add tests for error formatting fallback behavior

* Update exporter/datadogexporter/translate_traces_test.go

Co-authored-by: Pablo Baeyens <pablo.baeyens@datadoghq.com>

* [exporter/datadog]: fix specs and cleanup logic for error fallback

* [exporter/datadog]: apply correct defaults

* [exporter/datadog]: better code comments around error tag behavior

* [exporter/datadog]: clean up if statement complexity

* [exporter/datadog]: clean up more nested complexity

* [exporter/datadog]: linting, the cause of, and solution to, all of lifes problems

Co-authored-by: Pablo Baeyens <pablo.baeyens@datadoghq.com>
  • Loading branch information
ericmustin and mx-psi authored Jun 8, 2021
1 parent 9306fc3 commit a95cdc0
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 7 deletions.
24 changes: 17 additions & 7 deletions exporter/datadogexporter/translate_traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,17 +549,27 @@ func getSpanErrorAndSetTags(s pdata.Span, tags map[string]string) int32 {

if isError == errorCode {
extractErrorTagsFromEvents(s, tags)
// If we weren't able to pull an error type or message, go ahead and set
// these to the old defaults
if _, ok := tags[ext.ErrorType]; !ok {
tags[ext.ErrorType] = "ERR_CODE_" + strconv.FormatInt(int64(status.Code()), 10)
}

// if the error message doesnt exist, try to infer useful error information from
// the status message, and http status code / http status text.
// if we find useful error.message info, also set a fallback error.type
// otherwise leave these tags unset and allow the UI to handle defaults
if _, ok := tags[ext.ErrorMsg]; !ok {
if status.Message() != "" {
tags[ext.ErrorMsg] = status.Message()
} else {
tags[ext.ErrorMsg] = "ERR_CODE_" + strconv.FormatInt(int64(status.Code()), 10)
// look for useful http metadata if it exists and add that as a fallback for the error message
} else if statusCode, ok := tags[conventions.AttributeHTTPStatusCode]; ok {
if statusText, ok := tags[conventions.AttributeHTTPStatusText]; ok {
tags[ext.ErrorMsg] = fmt.Sprintf("%s %s", statusCode, statusText)
} else {
tags[ext.ErrorMsg] = statusCode
}
}

// If we weren't able to pull an error type, but we do have an error message
// set to a reasonable default
if (tags[ext.ErrorType] == "") && (tags[ext.ErrorMsg] != "") {
tags[ext.ErrorType] = "error"
}
}
}
Expand Down
51 changes: 51 additions & 0 deletions exporter/datadogexporter/translate_traces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,57 @@ func TestTracesTranslationErrorsAndResource(t *testing.T) {
assert.Contains(t, datadogPayload.Traces[0].Spans[0].Meta[tagContainersTags], "container_id:3249847017410247")
assert.Contains(t, datadogPayload.Traces[0].Spans[0].Meta[tagContainersTags], "pod_name:example-pod-name")
assert.Contains(t, datadogPayload.Traces[0].Spans[0].Meta[tagContainersTags], "arn:aws:ecs:ap-southwest-1:241423265983:task/test-environment-test-echo-Cluster-2lrqTJKFjACT/746bf64740324812835f688c30cf1512")

// ensure that span error type uses a fallback of "error" if a status code exists
assert.Equal(t, "error", datadogPayload.Traces[0].Spans[0].Meta["error.type"])
}

func TestTracesFallbackErrorMessage(t *testing.T) {
hostname := "testhostname"
denylister := newDenylister([]string{})

// generate mock trace, span and parent span ids
mockTraceID := [16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}
mockSpanID := [8]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8}
mockParentSpanID := [8]byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}
mockEndTime := time.Now().Round(time.Second)
pdataEndTime := pdata.TimestampFromTime(mockEndTime)
startTime := mockEndTime.Add(-90 * time.Second)
pdataStartTime := pdata.TimestampFromTime(startTime)

rs := pdata.NewResourceSpans()
ilss := rs.InstrumentationLibrarySpans()
ils := ilss.AppendEmpty()
ils.InstrumentationLibrary().SetName("test_il_name")
ils.InstrumentationLibrary().SetVersion("test_il_version")
span := ils.Spans().AppendEmpty()

traceID := pdata.NewTraceID(mockTraceID)
spanID := pdata.NewSpanID(mockSpanID)
parentSpanID := pdata.NewSpanID(mockParentSpanID)
span.SetTraceID(traceID)
span.SetSpanID(spanID)
span.SetParentSpanID(parentSpanID)
span.SetName("End-To-End Here")
span.SetKind(pdata.SpanKindServer)
span.SetStartTimestamp(pdataStartTime)
span.SetEndTimestamp(pdataEndTime)
span.SetTraceState("tracestatekey=tracestatevalue")

status := span.Status()
status.SetCode(pdata.StatusCodeError)

span.Attributes().InsertString(conventions.AttributeHTTPStatusCode, "404")
span.Attributes().InsertString(conventions.AttributeHTTPStatusText, "Not Found")

// translate mocks to datadog traces
datadogPayload := resourceSpansToDatadogSpans(rs, hostname, &config.Config{}, denylister, map[string]string{})

// ensure that span error type uses a fallback of "error"
assert.Equal(t, "error", datadogPayload.Traces[0].Spans[0].Meta["error.type"])

// ensure that span error type uses a fallback of "status_code" and "status_text"
assert.Equal(t, "404 Not Found", datadogPayload.Traces[0].Spans[0].Meta["error.msg"])
}

// Ensures that if more than one error event occurs in a span, the last one is used for translation
Expand Down

0 comments on commit a95cdc0

Please sign in to comment.