From 1f0e0f163a0dc3d7c9be2b13cb54990e4cbd30dd Mon Sep 17 00:00:00 2001 From: Will McCutchen <will@mccutch.org> Date: Thu, 31 Mar 2022 12:16:09 -0400 Subject: [PATCH] Add example demonstrating custom instrumentation (#75) --- README.md | 17 ++++++++-- examples/custom-instrumentation/README.md | 17 ++++++++++ examples/custom-instrumentation/go.mod | 15 +++++++++ examples/custom-instrumentation/go.sum | 21 ++++++++++++ examples/custom-instrumentation/main.go | 40 +++++++++++++++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 examples/custom-instrumentation/README.md create mode 100644 examples/custom-instrumentation/go.mod create mode 100644 examples/custom-instrumentation/go.sum create mode 100644 examples/custom-instrumentation/main.go diff --git a/README.md b/README.md index c97c5a3..565f36b 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ $ docker run -e HTTPS_CERT_FILE='/tmp/server.crt' -e HTTPS_KEY_FILE='/tmp/server ``` The `github.com/mccutchen/go-httpbin/httpbin/v2` package can also be used as a -library for testing an applications interactions with an upstream HTTP service, -like so: +library for testing an application's interactions with an upstream HTTP +service, like so: ```go package httpbin_test @@ -87,6 +87,17 @@ func TestSlowResponse(t *testing.T) { ``` +## Custom instrumentation + +If you're running go-httpbin in your own infrastructure and would like custom +instrumentation (metrics, structured logging, request tracing, etc), you'll +need to wrap this package in your own code and use the included +[Observer][observer] mechanism to instrument requests as necessary. + +See [examples/custom-instrumentation][custom-instrumentation] for an example +that instruments every request using DataDog. + + ## Installation To add go-httpbin to an existing golang project: @@ -147,3 +158,5 @@ make imagepush [httpbin-repo]: https://github.com/kennethreitz/httpbin [ahmet]: https://github.com/ahmetb/go-httpbin [docker-hub]: https://hub.docker.com/r/mccutchen/go-httpbin/ +[observer]: https://pkg.go.dev/github.com/mccutchen/go-httpbin/v2/httpbin#Observer +[custom-instrumentation]: ./examples/custom-instrumentation/ diff --git a/examples/custom-instrumentation/README.md b/examples/custom-instrumentation/README.md new file mode 100644 index 0000000..6fc07a4 --- /dev/null +++ b/examples/custom-instrumentation/README.md @@ -0,0 +1,17 @@ +# Custom Instrumentation + +This example demonstrates how to use go-httpbin's [`Observer`][1] mechanism to +add custom instrumentation to a go-httpbin instance. + +An _observer_ is a function that will be called with an [`httpbin.Result`][2] +struct after every request, which provides a hook for custom logging, metrics, +or other instrumentation. + +Note: This does require building your own small wrapper around go-httpbin, as +you can see in [main.go](./main.go) here. That's because go-httpbin has no +dependencies outside of the Go stdlib, to make sure that it is as +safe/lightweight as possible to include as a dependency in other applications' +test suites where useful. + +[1]: https://pkg.go.dev/github.com/mccutchen/go-httpbin/v2/httpbin#Observer +[2]: https://pkg.go.dev/github.com/mccutchen/go-httpbin/v2/httpbin#Result diff --git a/examples/custom-instrumentation/go.mod b/examples/custom-instrumentation/go.mod new file mode 100644 index 0000000..bb85219 --- /dev/null +++ b/examples/custom-instrumentation/go.mod @@ -0,0 +1,15 @@ +module httpbin-instrumentation + +go 1.18 + +require ( + github.com/DataDog/datadog-go v4.8.3+incompatible + github.com/mccutchen/go-httpbin/v2 v2.3.0 +) + +require ( + github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/testify v1.3.0 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect +) diff --git a/examples/custom-instrumentation/go.sum b/examples/custom-instrumentation/go.sum new file mode 100644 index 0000000..6c5e5f0 --- /dev/null +++ b/examples/custom-instrumentation/go.sum @@ -0,0 +1,21 @@ +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/mccutchen/go-httpbin/v2 v2.3.0 h1:NqVqPVI8Ushb/YJIe9bXiruBl0CDBSvp7BQSPXrY5qs= +github.com/mccutchen/go-httpbin/v2 v2.3.0/go.mod h1:+DBHcmg6EOeoizuiOI8iL12VIHXx+9YQNlz+gjB9uxk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/examples/custom-instrumentation/main.go b/examples/custom-instrumentation/main.go new file mode 100644 index 0000000..4415fc4 --- /dev/null +++ b/examples/custom-instrumentation/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "log" + "net/http" + + "github.com/DataDog/datadog-go/statsd" + + "github.com/mccutchen/go-httpbin/v2/httpbin" +) + +func main() { + statsdClient, _ := statsd.New("") + + h := httpbin.New( + httpbin.WithObserver(datadogObserver(statsdClient)), + ) + + listenAddr := "0.0.0.0:8080" + http.ListenAndServe(listenAddr, h.Handler()) +} + +func datadogObserver(client statsd.ClientInterface) httpbin.Observer { + return func(result httpbin.Result) { + // Log the request + log.Printf("%d %s %s %s", result.Status, result.Method, result.URI, result.Duration) + + // Submit a new distribution metric to datadog with tags that allow + // graphing request rate, timing, errors broken down by + // method/status/path. + tags := []string{ + fmt.Sprintf("method:%s", result.Method), + fmt.Sprintf("status_code:%d", result.Status), + fmt.Sprintf("status_class:%dxx", result.Status/100), + fmt.Sprintf("uri:%s", result.URI), + } + client.Distribution("httpbin.request", float64(result.Duration.Milliseconds()), tags, 1.0) + } +} -- GitLab