@@ -2,6 +2,7 @@ package sentryecho
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"net/http"
6
7
"time"
7
8
@@ -13,6 +14,7 @@ import (
13
14
const sdkIdentifier = "sentry.go.echo"
14
15
15
16
const valuesKey = "sentry"
17
+ const transactionKey = "sentry_transaction"
16
18
17
19
type handler struct {
18
20
repanic bool
@@ -22,7 +24,7 @@ type handler struct {
22
24
23
25
type Options struct {
24
26
// Repanic configures whether Sentry should repanic after recovery, in most cases it should be set to true,
25
- // as echo includes it's own Recover middleware what handles http responses.
27
+ // as echo includes its own Recover middleware what handles http responses.
26
28
Repanic bool
27
29
// WaitForDelivery configures whether you want to block the request before moving forward with the response.
28
30
// Because Echo's Recover handler doesn't restart the application,
@@ -57,10 +59,51 @@ func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
57
59
client .SetSDKIdentifier (sdkIdentifier )
58
60
}
59
61
60
- hub .Scope ().SetRequest (ctx .Request ())
62
+ r := ctx .Request ()
63
+
64
+ transactionName := r .URL .Path
65
+ transactionSource := sentry .SourceURL
66
+
67
+ if path := ctx .Path (); path != "" {
68
+ transactionName = path
69
+ transactionSource = sentry .SourceRoute
70
+ }
71
+
72
+ options := []sentry.SpanOption {
73
+ sentry .WithOpName ("http.server" ),
74
+ sentry .ContinueFromRequest (r ),
75
+ sentry .WithTransactionSource (transactionSource ),
76
+ }
77
+
78
+ transaction := sentry .StartTransaction (
79
+ sentry .SetHubOnContext (r .Context (), hub ),
80
+ fmt .Sprintf ("%s %s" , r .Method , transactionName ),
81
+ options ... ,
82
+ )
83
+
84
+ defer func () {
85
+ if err := ctx .Get ("error" ); err != nil {
86
+ if httpError , ok := err .(* echo.HTTPError ); ok {
87
+ transaction .Status = sentry .HTTPtoSpanStatus (httpError .Code )
88
+ }
89
+ } else {
90
+ transaction .Status = sentry .HTTPtoSpanStatus (ctx .Response ().Status )
91
+ }
92
+ transaction .Finish ()
93
+ }()
94
+
95
+ hub .Scope ().SetRequest (r )
61
96
ctx .Set (valuesKey , hub )
62
- defer h .recoverWithSentry (hub , ctx .Request ())
63
- return next (ctx )
97
+ ctx .Set (transactionKey , transaction )
98
+ defer h .recoverWithSentry (hub , r )
99
+
100
+ err := next (ctx )
101
+ if err != nil {
102
+ // Store the error so it can be used in the deferred function
103
+ ctx .Set ("error" , err )
104
+ }
105
+
106
+ return err
64
107
}
65
108
}
66
109
@@ -86,3 +129,12 @@ func GetHubFromContext(ctx echo.Context) *sentry.Hub {
86
129
}
87
130
return nil
88
131
}
132
+
133
+ // GetSpanFromContext retrieves attached *sentry.Span instance from echo.Context.
134
+ // If there is no transaction on echo.Context, it will return nil.
135
+ func GetSpanFromContext (ctx echo.Context ) * sentry.Span {
136
+ if span , ok := ctx .Get (transactionKey ).(* sentry.Span ); ok {
137
+ return span
138
+ }
139
+ return nil
140
+ }
0 commit comments