diff --git a/README.md b/README.md index efda98eb..917f2d66 100644 --- a/README.md +++ b/README.md @@ -393,6 +393,51 @@ func main() { } ``` +## gofiber instrumentation + +Support for incoming requests with [gofiber/fiber](https://github.com/gofiber/fiber): + +```go +package main + +import ( + "github.com/aws/aws-xray-sdk-go/xray" + "github.com/aws/aws-xray-sdk-go/xraylog" + "github.com/gofiber/fiber/v2" + "log" + "os" +) + +func index(ctx *fiber.Ctx) error { + _, err := ctx.WriteString("Welcome!") + return err +} + +func init() { + if err := xray.Configure(xray.Config{ + DaemonAddr: "xray:2000", + ServiceVersion: "0.1", + }); err != nil { + panic(err) + } + + xray.SetLogger(xraylog.NewDefaultLogger(os.Stdout, xraylog.LogLevelDebug)) +} + +func main() { + app := fiber.New() + fh := xray.NewFiberInstrumentor(nil) + + middleware := func(name string, handler fiber.Handler) fiber.Handler { + return fh.Handler(xray.NewFixedSegmentNamer(name), handler) + } + + app.Get("/", middleware("index", index)) + + log.Fatal(app.Listen(":8080")) +} +``` + ## License The AWS X-Ray SDK for Go is licensed under the Apache 2.0 License. See LICENSE and NOTICE.txt for more information. diff --git a/go.mod b/go.mod index c6cb168f..49894519 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.6.0 github.com/aws/aws-sdk-go-v2/service/route53 v1.6.2 github.com/aws/smithy-go v1.4.0 + github.com/gofiber/fiber/v2 v2.31.0 github.com/golang/protobuf v1.4.3 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 18a4e5a0..ced6bfcd 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofiber/fiber/v2 v2.31.0 h1:M2rWPQbD5fDVAjcoOLjKRXTIlHesI5Eq7I5FEQPt4Ow= +github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -82,6 +84,7 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/xray/fiber.go b/xray/fiber.go new file mode 100644 index 00000000..7ec796bd --- /dev/null +++ b/xray/fiber.go @@ -0,0 +1,61 @@ +package xray + +import ( + "context" + "github.com/aws/aws-xray-sdk-go/header" + "github.com/gofiber/fiber/v2" +) + +type FiberHandler interface { + Handler(SegmentNamer, fiber.Handler) fiber.Handler +} + +type fiberHandler struct { + cfg *Config +} + +// NewFiberInstrumentor returns a new FiberHandler that +// provides a Handler function to satisfy the fiber.Handler +// interface. +func NewFiberInstrumentor(cfg *Config) FiberHandler { + return &fiberHandler{ + cfg: cfg, + } +} + +// Handler wraps the provided fiber.Handler. +func (h *fiberHandler) Handler(sn SegmentNamer, handler fiber.Handler) fiber.Handler { + return func(ctx *fiber.Ctx) error { + auxCtx := context.Background() + if h.cfg != nil { + auxCtx = context.WithValue(context.Background(), fasthttpContextConfigKey, h.cfg) + ctx.Locals(fasthttpContextConfigKey, h.cfg) + } + + name := sn.Name(ctx.Hostname()) + traceHeader := header.FromString(ctx.Get(TraceIDHeaderKey)) + + req, err := fasthttpToNetHTTPRequest(ctx.Context()) + if err != nil { + return err + } + + _, seg := NewSegmentFromHeader(auxCtx, name, req, traceHeader) + defer seg.Close(nil) + + ctx.Locals(fasthttpContextKey, seg) + httpCaptureRequest(seg, req) + return fiberTrace(seg, handler, ctx, traceHeader) + } +} + +func fiberTrace(seg *Segment, handler fiber.Handler, ctx *fiber.Ctx, traceHeader *header.Header) error { + ctx.Set(TraceIDHeaderKey, generateTraceIDHeaderValue(seg, traceHeader)) + handlerErr := handler(ctx) + + seg.Lock() + seg.GetHTTP().GetResponse().ContentLength = ctx.Context().Response.Header.ContentLength() + seg.Unlock() + HttpCaptureResponse(seg, ctx.Response().StatusCode()) + return handlerErr +} diff --git a/xray/fiber_test.go b/xray/fiber_test.go new file mode 100644 index 00000000..2f0fc343 --- /dev/null +++ b/xray/fiber_test.go @@ -0,0 +1,37 @@ +package xray + +import ( + "github.com/gofiber/fiber/v2" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestFiberHandler(t *testing.T) { + ctx1, td := NewTestDaemon() + cfg := GetRecorder(ctx1) + defer td.Close() + + fh := NewFiberInstrumentor(cfg) + handler := fh.Handler(NewFixedSegmentNamer("test"), func(ctx *fiber.Ctx) error { + return nil + }) + rc := genericFiberRequestCtx() + if err := handler(rc); err != nil { + t.Error("Error calling handler:", err) + } + + seg, err := td.Recv() + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, fiber.StatusOK, rc.Response().StatusCode()) + assert.Equal(t, fiber.MethodPost, seg.HTTP.Request.Method) + assert.Equal(t, "http://localhost/path", seg.HTTP.Request.URL) + assert.Equal(t, "1.2.3.5", seg.HTTP.Request.ClientIP) + assert.Equal(t, "UA_test", seg.HTTP.Request.UserAgent) +} + +func genericFiberRequestCtx() *fiber.Ctx { + return fiber.New().AcquireCtx(genericRequestCtx()) +}