-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib/go-chi/chi: add appsec monitoring (#1130)
- Loading branch information
1 parent
9bc920a
commit a9f9352
Showing
11 changed files
with
892 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016 Datadog, Inc. | ||
|
||
package chi | ||
|
||
import ( | ||
"net/http" | ||
|
||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" | ||
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo/instrumentation/httpsec" | ||
|
||
"github.com/go-chi/chi/v4" | ||
) | ||
|
||
func withAppsec(next http.Handler, r *http.Request, span tracer.Span) http.Handler { | ||
rctx := chi.RouteContext(r.Context()) | ||
if rctx == nil { | ||
return httpsec.WrapHandler(next, span, nil) | ||
} | ||
var pathParams map[string]string | ||
keys := rctx.URLParams.Keys | ||
values := rctx.URLParams.Values | ||
if len(keys) > 0 && len(keys) == len(values) { | ||
pathParams = make(map[string]string, len(keys)) | ||
for i, key := range keys { | ||
pathParams[key] = values[i] | ||
} | ||
} | ||
return httpsec.WrapHandler(next, span, pathParams) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016 Datadog, Inc. | ||
|
||
// Package chi provides tracing functions for tracing the go-chi/chi/v4 package (https://github.com/go-chi/chi). | ||
package chi // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi.v4" | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"net/http" | ||
"strconv" | ||
|
||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" | ||
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec" | ||
"gopkg.in/DataDog/dd-trace-go.v1/internal/log" | ||
|
||
"github.com/go-chi/chi/v4" | ||
"github.com/go-chi/chi/v4/middleware" | ||
) | ||
|
||
// Middleware returns middleware that will trace incoming requests. | ||
func Middleware(opts ...Option) func(next http.Handler) http.Handler { | ||
cfg := new(config) | ||
defaults(cfg) | ||
for _, fn := range opts { | ||
fn(cfg) | ||
} | ||
log.Debug("contrib/go-chi/chi.v4: Configuring Middleware: %#v", cfg) | ||
return func(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if cfg.ignoreRequest(r) { | ||
next.ServeHTTP(w, r) | ||
return | ||
} | ||
opts := []ddtrace.StartSpanOption{ | ||
tracer.SpanType(ext.SpanTypeWeb), | ||
tracer.ServiceName(cfg.serviceName), | ||
tracer.Tag(ext.HTTPMethod, r.Method), | ||
tracer.Tag(ext.HTTPURL, r.URL.Path), | ||
tracer.Measured(), | ||
} | ||
if !math.IsNaN(cfg.analyticsRate) { | ||
opts = append(opts, tracer.Tag(ext.EventSampleRate, cfg.analyticsRate)) | ||
} | ||
if spanctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(r.Header)); err == nil { | ||
opts = append(opts, tracer.ChildOf(spanctx)) | ||
} | ||
opts = append(opts, cfg.spanOpts...) | ||
span, ctx := tracer.StartSpanFromContext(r.Context(), "http.request", opts...) | ||
defer span.Finish() | ||
|
||
if appsec.Enabled() { | ||
next = withAppsec(next, r, span) | ||
// Note that the following response writer passed to the handler | ||
// implements the `interface { Status() int }` expected by httpsec. | ||
} | ||
|
||
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) | ||
|
||
// pass the span through the request context and serve the request to the next middleware | ||
next.ServeHTTP(ww, r.WithContext(ctx)) | ||
|
||
// set the resource name as we get it only once the handler is executed | ||
resourceName := chi.RouteContext(r.Context()).RoutePattern() | ||
if resourceName == "" { | ||
resourceName = "unknown" | ||
} | ||
resourceName = r.Method + " " + resourceName | ||
span.SetTag(ext.ResourceName, resourceName) | ||
|
||
// set the status code | ||
status := ww.Status() | ||
// 0 status means one has not yet been sent in which case net/http library will write StatusOK | ||
if ww.Status() == 0 { | ||
status = http.StatusOK | ||
} | ||
span.SetTag(ext.HTTPCode, strconv.Itoa(status)) | ||
|
||
if cfg.isStatusError(status) { | ||
// mark 5xx server error | ||
span.SetTag(ext.Error, fmt.Errorf("%d: %s", status, http.StatusText(status))) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.