Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(performance): start transaction for fasthttp integration #723

Merged
merged 10 commits into from
Mar 26, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Accept `interface{}` for span data values ([#784](https://github.com/getsentry/sentry-go/pull/784))
- Automatic transactions for Echo integration ([#722](https://github.com/getsentry/sentry-go/pull/722))
- Automatic transactions for Fasthttp integration ([#732](https://github.com/getsentry/sentry-go/pull/723))

## 0.27.0

Expand Down
19 changes: 19 additions & 0 deletions _examples/fasthttp/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"

"github.com/getsentry/sentry-go"
Expand All @@ -13,6 +14,24 @@ func enhanceSentryEvent(handler fasthttp.RequestHandler) fasthttp.RequestHandler
if hub := sentryfasthttp.GetHubFromContext(ctx); hub != nil {
hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
}

expensiveThing := func(ctx context.Context) error {
span := sentry.StartTransaction(ctx, "expensive_thing")
defer span.Finish()
// do resource intensive thing
return nil
}

// Acquire transaction on current hub that's created by the SDK.
// Be careful, it might be a nil value if you didn't set up sentryecho middleware.
sentrySpan := sentryfasthttp.GetSpanFromContext(ctx)
// Pass in the `.Context()` method from `*sentry.Span` struct.
// The `context.Context` instance inherits the context from `echo.Context`.
err := expensiveThing(sentrySpan.Context())
if err != nil {
sentry.CaptureException(err)
}

handler(ctx)
}
}
Expand Down
50 changes: 40 additions & 10 deletions fasthttp/sentryfasthttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import (
"github.com/valyala/fasthttp"
)

// The identifier of the FastHTTP SDK.
const sdkIdentifier = "sentry.go.fasthttp"

type contextKey int

const ContextKey = contextKey(1)
const valuesKey = "sentry"
const (
ContextKey = contextKey(1)
// The identifier of the FastHTTP SDK.
sdkIdentifier = "sentry.go.fasthttp"
valuesKey = "sentry"
transactionKey = "sentry_transaction"
)

type Handler struct {
repanic bool
Expand Down Expand Up @@ -56,20 +58,39 @@ func New(options Options) *Handler {
// Handle wraps fasthttp.RequestHandler and recovers from caught panics.
func (h *Handler) Handle(handler fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// Unlike for other integrations, we don't support getting an existing
// hub from the current request context because fasthttp doesn't use the
// standard net/http.Request and because fasthttp.RequestCtx implements
// context.Context but requires string keys.
hub := sentry.CurrentHub().Clone()

if client := hub.Client(); client != nil {
client.SetSDKIdentifier(sdkIdentifier)
}

convertedHTTPRequest := convert(ctx)

options := []sentry.SpanOption{
sentry.WithOpName("http.server"),
sentry.ContinueFromRequest(convertedHTTPRequest),
sentry.WithTransactionSource(sentry.SourceRoute),
}

method := string(ctx.Method())

transaction := sentry.StartTransaction(
sentry.SetHubOnContext(ctx, hub),
vaind marked this conversation as resolved.
Show resolved Hide resolved
fmt.Sprintf("%s %s", method, string(ctx.Path())),
options...,
)
defer func() {
transaction.Status = sentry.HTTPtoSpanStatus(ctx.Response.StatusCode())
transaction.Finish()
}()

transaction.SetData("http.request.method", method)

scope := hub.Scope()
scope.SetRequest(convert(ctx))
scope.SetRequest(convertedHTTPRequest)
scope.SetRequestBody(ctx.Request.Body())
ctx.SetUserValue(valuesKey, hub)
ctx.SetUserValue(transactionKey, transaction)
defer h.recoverWithSentry(hub, ctx)
handler(ctx)
}
Expand Down Expand Up @@ -99,6 +120,15 @@ func GetHubFromContext(ctx *fasthttp.RequestCtx) *sentry.Hub {
return nil
}

// GetSpanFromContext retrieves attached *sentry.Span instance from *fasthttp.RequestCtx.
// If there is no transaction on *fasthttp.RequestCtx, it will return nil.
func GetSpanFromContext(ctx *fasthttp.RequestCtx) *sentry.Span {
if span, ok := ctx.UserValue(transactionKey).(*sentry.Span); ok {
return span
}
return nil
}

func convert(ctx *fasthttp.RequestCtx) *http.Request {
defer func() {
if err := recover(); err != nil {
Expand Down
Loading
Loading