Skip to content

Commit

Permalink
Allow integration-level config to override global config for server e…
Browse files Browse the repository at this point in the history
…rror statuses
  • Loading branch information
mtoffl01 committed Oct 24, 2024
1 parent 2edf445 commit 9c66f69
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 60 deletions.
2 changes: 1 addition & 1 deletion contrib/emicklei/go-restful.v3/restful.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func FilterFunc(configOpts ...Option) restful.FilterFunction {
spanOpts = append(spanOpts, httptrace.HeaderTagsFromRequest(req.Request, cfg.headerTags))
span, ctx := httptrace.StartRequestSpan(req.Request, spanOpts...)
defer func() {
httptrace.FinishRequestSpan(span, resp.StatusCode(), tracer.WithError(resp.Error()))
httptrace.FinishRequestSpan(span, resp.StatusCode(), nil, tracer.WithError(resp.Error()))
}()

// pass the span through the request context
Expand Down
4 changes: 2 additions & 2 deletions contrib/emicklei/go-restful/restful.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func FilterFunc(configOpts ...Option) restful.FilterFunction {
spanOpts = append(spanOpts, httptrace.HeaderTagsFromRequest(req.Request, cfg.headerTags))
span, ctx := httptrace.StartRequestSpan(req.Request, spanOpts...)
defer func() {
httptrace.FinishRequestSpan(span, resp.StatusCode(), tracer.WithError(resp.Error()))
httptrace.FinishRequestSpan(span, resp.StatusCode(), nil, tracer.WithError(resp.Error()))
}()

// pass the span through the request context
Expand All @@ -61,7 +61,7 @@ func FilterFunc(configOpts ...Option) restful.FilterFunction {
func Filter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
span, ctx := httptrace.StartRequestSpan(req.Request, tracer.ResourceName(req.SelectedRoutePath()))
defer func() {
httptrace.FinishRequestSpan(span, resp.StatusCode(), tracer.WithError(resp.Error()))
httptrace.FinishRequestSpan(span, resp.StatusCode(), nil, tracer.WithError(resp.Error()))
}()

// pass the span through the request context
Expand Down
2 changes: 1 addition & 1 deletion contrib/gin-gonic/gin/gintrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
opts = append(opts, httptrace.HeaderTagsFromRequest(c.Request, cfg.headerTags))
span, ctx := httptrace.StartRequestSpan(c.Request, opts...)
defer func() {
httptrace.FinishRequestSpan(span, c.Writer.Status())
httptrace.FinishRequestSpan(span, c.Writer.Status(), nil)
}()

// pass the span through the request context
Expand Down
7 changes: 1 addition & 6 deletions contrib/go-chi/chi.v5/chi.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package chi // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v5"

import (
"fmt"
"math"
"net/http"

Expand Down Expand Up @@ -56,11 +55,7 @@ func Middleware(opts ...Option) func(next http.Handler) http.Handler {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
defer func() {
status := ww.Status()
var opts []tracer.FinishOption
if cfg.isStatusError(status) {
opts = []tracer.FinishOption{tracer.WithError(fmt.Errorf("%d: %s", status, http.StatusText(status)))}
}
httptrace.FinishRequestSpan(span, status, opts...)
httptrace.FinishRequestSpan(span, status, cfg.isStatusError)
}()

// pass the span through the request context
Expand Down
78 changes: 66 additions & 12 deletions contrib/go-chi/chi.v5/chi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,10 @@ func TestWithModifyResourceName(t *testing.T) {
}

func TestError(t *testing.T) {
assertSpan := func(assert *assert.Assertions, spans []mocktracer.Span, code int) {
assert.Len(spans, 1)
if len(spans) < 1 {
t.Fatalf("no spans")
}
span := spans[0]
assertSpan := func(assert *assert.Assertions, span mocktracer.Span, code int) {
assert.Equal("http.request", span.OperationName())
assert.Equal("foobar", span.Tag(ext.ServiceName))

assert.Equal(strconv.Itoa(code), span.Tag(ext.HTTPCode))

wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
}

t.Run("default", func(t *testing.T) {
Expand All @@ -176,7 +167,11 @@ func TestError(t *testing.T) {

// verify the errors and status are correct
spans := mt.FinishedSpans()
assertSpan(assert, spans, code)
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
})

t.Run("custom", func(t *testing.T) {
Expand Down Expand Up @@ -206,7 +201,66 @@ func TestError(t *testing.T) {

// verify the errors and status are correct
spans := mt.FinishedSpans()
assertSpan(assert, spans, code)
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
})
t.Run("integration overrides global", func(t *testing.T) {
assert := assert.New(t)
mt := mocktracer.Start()
defer mt.Stop()

t.Setenv("DD_TRACE_HTTP_SERVER_ERROR_STATUSES", "500")

// setup
router := chi.NewRouter()
router.Use(Middleware(
WithServiceName("foobar"),
WithStatusCheck(func(statusCode int) bool {
return statusCode == 404
}),
))
code := 404
// a handler with an error and make the requests
router.Get("/404", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("%d!", code), code)
})
r := httptest.NewRequest("GET", "/404", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
response := w.Result()
defer response.Body.Close()
assert.Equal(response.StatusCode, code)

// verify the errors and status are correct
spans := mt.FinishedSpans()
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())

mt.Reset()

code = 500
router.Get("/500", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("%d!", code), code)
})
r = httptest.NewRequest("GET", "/500", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, r)
response = w.Result()
defer response.Body.Close()
assert.Equal(response.StatusCode, 500)

// verify that span does not have error tag
spans = mt.FinishedSpans()
assert.Len(spans, 1)
span = spans[0]
assertSpan(assert, span, 500)
assert.Empty(span.Tag(ext.Error))
})
}

Expand Down
7 changes: 1 addition & 6 deletions contrib/go-chi/chi/chi.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package chi // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi"

import (
"fmt"
"math"
"net/http"

Expand Down Expand Up @@ -56,11 +55,7 @@ func Middleware(opts ...Option) func(next http.Handler) http.Handler {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
defer func() {
status := ww.Status()
var opts []tracer.FinishOption
if cfg.isStatusError(status) {
opts = []tracer.FinishOption{tracer.WithError(fmt.Errorf("%d: %s", status, http.StatusText(status)))}
}
httptrace.FinishRequestSpan(span, status, opts...)
httptrace.FinishRequestSpan(span, status, cfg.isStatusError)
}()

// pass the span through the request context
Expand Down
78 changes: 66 additions & 12 deletions contrib/go-chi/chi/chi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,10 @@ func TestTrace200(t *testing.T) {
}

func TestError(t *testing.T) {
assertSpan := func(assert *assert.Assertions, spans []mocktracer.Span, code int) {
assert.Len(spans, 1)
if len(spans) < 1 {
t.Fatalf("no spans")
}
span := spans[0]
assertSpan := func(assert *assert.Assertions, span mocktracer.Span, code int) {
assert.Equal("http.request", span.OperationName())
assert.Equal("foobar", span.Tag(ext.ServiceName))

assert.Equal(strconv.Itoa(code), span.Tag(ext.HTTPCode))

wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
}

t.Run("default", func(t *testing.T) {
Expand All @@ -148,7 +139,11 @@ func TestError(t *testing.T) {

// verify the errors and status are correct
spans := mt.FinishedSpans()
assertSpan(assert, spans, code)
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
})

t.Run("custom", func(t *testing.T) {
Expand Down Expand Up @@ -178,7 +173,66 @@ func TestError(t *testing.T) {

// verify the errors and status are correct
spans := mt.FinishedSpans()
assertSpan(assert, spans, code)
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())
})
t.Run("integration overrides global", func(t *testing.T) {
assert := assert.New(t)
mt := mocktracer.Start()
defer mt.Stop()

t.Setenv("DD_TRACE_HTTP_SERVER_ERROR_STATUSES", "500")

// setup
router := chi.NewRouter()
router.Use(Middleware(
WithServiceName("foobar"),
WithStatusCheck(func(statusCode int) bool {
return statusCode == 404
}),
))
code := 404
// a handler with an error and make the requests
router.Get("/404", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("%d!", code), code)
})
r := httptest.NewRequest("GET", "/404", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
response := w.Result()
defer response.Body.Close()
assert.Equal(response.StatusCode, code)

// verify the errors and status are correct
spans := mt.FinishedSpans()
assert.Len(spans, 1)
span := spans[0]
assertSpan(assert, span, code)
wantErr := fmt.Sprintf("%d: %s", code, http.StatusText(code))
assert.Equal(wantErr, span.Tag(ext.Error).(error).Error())

mt.Reset()

code = 500
router.Get("/500", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("%d!", code), code)
})
r = httptest.NewRequest("GET", "/500", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, r)
response = w.Result()
defer response.Body.Close()
assert.Equal(response.StatusCode, 500)

// verify that span does not have error tag
spans = mt.FinishedSpans()
assert.Len(spans, 1)
span = spans[0]
assertSpan(assert, span, 500)
assert.Empty(span.Tag(ext.Error))
})
}

Expand Down
3 changes: 1 addition & 2 deletions contrib/internal/httptrace/before_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ func BeforeHandle(cfg *ServeConfig, w http.ResponseWriter, r *http.Request) (htt
span, ctx := StartRequestSpan(r, opts...)
rw, ddrw := wrapResponseWriter(w)
rt := r.WithContext(ctx)

closeSpan := func() {
FinishRequestSpan(span, ddrw.status, cfg.FinishOpts...)
FinishRequestSpan(span, ddrw.status, nil, cfg.FinishOpts...)
}
afterHandle := closeSpan
handled := false
Expand Down
12 changes: 9 additions & 3 deletions contrib/internal/httptrace/httptrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,25 @@ func StartRequestSpan(r *http.Request, opts ...ddtrace.StartSpanOption) (tracer.

// FinishRequestSpan finishes the given HTTP request span and sets the expected response-related tags such as the status
// code. Any further span finish option can be added with opts.
func FinishRequestSpan(s tracer.Span, status int, opts ...tracer.FinishOption) {
func FinishRequestSpan(s tracer.Span, status int, errorFn func(int) bool, opts ...tracer.FinishOption) {
var statusStr string
var fn func(int) bool
if errorFn == nil {
fn = cfg.isStatusError
} else {
fn = errorFn
}
// if status is 0, treat it like 200 unless 0 was called out in DD_TRACE_HTTP_SERVER_ERROR_STATUSES
if status == 0 {
if cfg.isStatusError(status) {
if fn(status) {
statusStr = "0"
s.SetTag(ext.Error, fmt.Errorf("%s: %s", statusStr, http.StatusText(status)))
} else {
statusStr = "200"
}
} else {
statusStr = strconv.Itoa(status)
if cfg.isStatusError(status) {
if fn(status) {
s.SetTag(ext.Error, fmt.Errorf("%s: %s", statusStr, http.StatusText(status)))
}
}
Expand Down
4 changes: 2 additions & 2 deletions contrib/internal/httptrace/httptrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestConfiguredErrorStatuses(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/test", nil)
for i, status := range statuses {
sp, _ := StartRequestSpan(r)
FinishRequestSpan(sp, status)
FinishRequestSpan(sp, status, nil)
spans := mt.FinishedSpans()
require.Len(t, spans, i+1)

Expand Down Expand Up @@ -130,7 +130,7 @@ func TestConfiguredErrorStatuses(t *testing.T) {

r := httptest.NewRequest(http.MethodGet, "/test", nil)
sp, _ := StartRequestSpan(r)
FinishRequestSpan(sp, 0)
FinishRequestSpan(sp, 0, nil)
spans := mt.FinishedSpans()
require.Len(t, spans, 1)
assert.Equal(t, "0", spans[0].Tag(ext.HTTPCode))
Expand Down
Loading

0 comments on commit 9c66f69

Please sign in to comment.