-
Notifications
You must be signed in to change notification settings - Fork 250
Expand file tree
/
Copy pathsentryiris.go
More file actions
135 lines (112 loc) · 3.64 KB
/
sentryiris.go
File metadata and controls
135 lines (112 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package sentryiris
import (
"context"
"fmt"
"net/http"
"time"
"github.com/getsentry/sentry-go"
"github.com/kataras/iris/v12"
)
// The identifier of the Iris SDK.
const (
// sdkIdentifier is the identifier of the Iris SDK.
sdkIdentifier = "sentry.go.iris"
// valuesKey is used as a key to store the Sentry Hub instance on the iris.Context.
valuesKey = "sentry"
// transactionKey is used as a key to store the Sentry transaction on the iris.Context.
transactionKey = "sentry_transaction"
)
type handler struct {
repanic bool
waitForDelivery bool
timeout time.Duration
}
type Options struct {
// Repanic configures whether Sentry should repanic after recovery, in most cases it should be set to true,
// as iris.Default includes it's own Recovery middleware what handles http responses.
Repanic bool
// WaitForDelivery configures whether you want to block the request before moving forward with the response.
// Because Iris's default Recovery handler doesn't restart the application,
// it's safe to either skip this option or set it to false.
WaitForDelivery bool
// Timeout for the event delivery requests.
Timeout time.Duration
}
// New returns a function that satisfies iris.Handler interface
// It can be used with Use() method.
func New(options Options) iris.Handler {
if options.Timeout == 0 {
options.Timeout = 2 * time.Second
}
return (&handler{
repanic: options.Repanic,
timeout: options.Timeout,
waitForDelivery: options.WaitForDelivery,
}).handle
}
func (h *handler) handle(ctx iris.Context) {
hub := sentry.GetHubFromContext(ctx.Request().Context())
if hub == nil {
hub = sentry.CurrentHub().Clone()
}
if client := hub.Client(); client != nil {
client.SetSDKIdentifier(sdkIdentifier)
}
r := ctx.Request()
options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceRoute),
sentry.WithSpanOrigin(sentry.SpanOriginIris),
}
currentRoute := ctx.GetCurrentRoute()
transaction := sentry.StartTransaction(
sentry.SetHubOnContext(ctx, hub),
fmt.Sprintf("%s %s", currentRoute.Method(), currentRoute.Path()),
options...,
)
defer func() {
transaction.SetData("http.response.status_code", ctx.GetStatusCode())
transaction.Status = sentry.HTTPtoSpanStatus(ctx.GetStatusCode())
transaction.Finish()
}()
transaction.SetData("http.request.method", r.Method)
hub.Scope().SetRequest(r)
ctx.Values().Set(valuesKey, hub)
ctx.Values().Set(transactionKey, transaction)
defer h.recoverWithSentry(hub, r)
ctx.Next()
}
func (h *handler) recoverWithSentry(hub *sentry.Hub, r *http.Request) {
if err := recover(); err != nil {
eventID := hub.RecoverWithContext(
context.WithValue(r.Context(), sentry.RequestContextKey, r),
err,
)
if eventID != nil && h.waitForDelivery {
hub.Flush(h.timeout)
}
if h.repanic {
panic(err)
}
}
}
// GetHubFromContext retrieves attached *sentry.Hub instance from iris.Context.
func GetHubFromContext(ctx iris.Context) *sentry.Hub {
if hub, ok := ctx.Values().Get(valuesKey).(*sentry.Hub); ok {
return hub
}
return nil
}
// SetHubOnContext attaches a *sentry.Hub instance to iris.Context.
func SetHubOnContext(ctx iris.Context, hub *sentry.Hub) {
ctx.Values().Set(valuesKey, hub)
}
// GetSpanFromContext retrieves attached *sentry.Span instance from iris.Context.
// If there is no transaction on iris.Context, it will return nil.
func GetSpanFromContext(ctx iris.Context) *sentry.Span {
if span, ok := ctx.Values().Get(transactionKey).(*sentry.Span); ok {
return span
}
return nil
}