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 echo middleware/integration #722

Merged
merged 10 commits into from
Mar 26, 2024
35 changes: 35 additions & 0 deletions echo/example_test.go
ribice marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package sentryecho_test

import (
"context"
"net/http"

"github.com/getsentry/sentry-go"
sentryecho "github.com/getsentry/sentry-go/echo"
"github.com/labstack/echo/v4"
)

func ExampleGetTransactionFromContext() {
router := echo.New()
router.Use(sentryecho.New(sentryecho.Options{}))
router.GET("/", func(c echo.Context) error {
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 := sentryecho.GetTransactionFromContext(c)
// 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 {
return err
}

return c.NoContent(http.StatusOK)
})
}
36 changes: 35 additions & 1 deletion echo/sentryecho.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sentryecho

import (
"context"
"fmt"
"net/http"
"time"

Expand All @@ -13,6 +14,7 @@ import (
const sdkIdentifier = "sentry.go.echo"

const valuesKey = "sentry"
const transactionKey = "sentry_transaction"

type handler struct {
repanic bool
Expand All @@ -22,7 +24,7 @@ type handler struct {

type Options struct {
// Repanic configures whether Sentry should repanic after recovery, in most cases it should be set to true,
// as echo includes it's own Recover middleware what handles http responses.
// as echo includes its own Recover middleware what handles http responses.
Repanic bool
// WaitForDelivery configures whether you want to block the request before moving forward with the response.
// Because Echo's Recover handler doesn't restart the application,
Expand Down Expand Up @@ -57,8 +59,31 @@ func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
client.SetSDKIdentifier(sdkIdentifier)
}

var transactionName = ctx.Path()
var transactionSource = sentry.SourceRoute

options := []sentry.SpanOption{
sentry.WithOpName("http.server"),
sentry.ContinueFromRequest(ctx.Request()),
sentry.WithTransactionSource(transactionSource),
}

transaction := sentry.StartTransaction(
sentry.SetHubOnContext(ctx.Request().Context(), hub),
fmt.Sprintf("%s %s", ctx.Request().Method, transactionName),
options...,
)
defer func() {
// TODO: For nil handler (or not found routes), ctx.Response().Status will always be 200
// instead of 404.
ribice marked this conversation as resolved.
Show resolved Hide resolved
transaction.Status = sentry.HTTPtoSpanStatus(ctx.Response().Status)
transaction.Finish()
}()

// We can't reassign `ctx.Request()`, so we'd need to put it inside echo.Context
hub.Scope().SetRequest(ctx.Request())
ctx.Set(valuesKey, hub)
ctx.Set(transactionKey, transaction)
defer h.recoverWithSentry(hub, ctx.Request())
return next(ctx)
}
Expand Down Expand Up @@ -86,3 +111,12 @@ func GetHubFromContext(ctx echo.Context) *sentry.Hub {
}
return nil
}

// GetTransactionFromContext retrieves attached *sentry.Span instance from echo.Context.
// If there is no transaction on echo.Context, it will return nil.
func GetTransactionFromContext(ctx echo.Context) *sentry.Span {
if span, ok := ctx.Get(transactionKey).(*sentry.Span); ok {
return span
}
return nil
}
ribice marked this conversation as resolved.
Show resolved Hide resolved
Loading