forked from vmkteam/zenrpc-middleware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sentry.go
97 lines (82 loc) · 3.26 KB
/
sentry.go
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
package middleware
import (
"context"
"encoding/json"
"net/http"
"time"
"github.com/getsentry/sentry-go"
"github.com/vmkteam/zenrpc/v2"
)
const ctxSentryHubKey contextKey = "sentryHub"
// NewSentryHubContext creates new context with Sentry Hub.
func NewSentryHubContext(ctx context.Context, sentryHub *sentry.Hub) context.Context {
if sentryHub == nil {
return ctx
}
return context.WithValue(ctx, ctxSentryHubKey, sentryHub)
}
// sentryHubFromContext returns Sentry Hub from context.
func sentryHubFromContext(ctx context.Context) (*sentry.Hub, bool) {
r, ok := ctx.Value(ctxSentryHubKey).(*sentry.Hub)
return r, ok
}
// WithSentry sets additional parameters for current Sentry scope. Extras: params, duration, ip. Tags: platform,
// version, method.
func WithSentry(serverName string) zenrpc.MiddlewareFunc {
return func(h zenrpc.InvokeFunc) zenrpc.InvokeFunc {
return func(ctx context.Context, w http.ResponseWriter, method string, params json.RawMessage) zenrpc.Response {
if hub, ok := sentryHubFromContext(ctx); ok {
start, platform, version, ip, xRequestID := time.Now(), PlatformFromContext(ctx), VersionFromContext(ctx), IPFromContext(ctx), XRequestIDFromContext(ctx)
methodName := fullMethodName(serverName, zenrpc.NamespaceFromContext(ctx), method)
hub.Scope().SetExtras(map[string]interface{}{
"params": params,
"duration": time.Since(start).String(),
"ip": ip,
})
hub.Scope().SetTags(map[string]string{
"platform": platform,
"version": version,
"method": methodName,
"xRequestId": xRequestID,
})
}
return h(ctx, w, method, params)
}
}
}
// WithErrorLogger logs all errors (ErrorCode==500 or < 0) via Printf func and sends them to Sentry. It also removes
// sensitive error data from response. It is good to use pkg/errors for stack trace support in sentry.
func WithErrorLogger(pf Printf, serverName string) zenrpc.MiddlewareFunc {
return func(h zenrpc.InvokeFunc) zenrpc.InvokeFunc {
return func(ctx context.Context, w http.ResponseWriter, method string, params json.RawMessage) zenrpc.Response {
start, platform, version, ip, xRequestID := time.Now(), PlatformFromContext(ctx), VersionFromContext(ctx), IPFromContext(ctx), XRequestIDFromContext(ctx)
namespace := zenrpc.NamespaceFromContext(ctx)
r := h(ctx, w, method, params)
if r.Error != nil && (r.Error.Code == http.StatusInternalServerError || r.Error.Code < 0) {
duration := time.Since(start)
methodName := fullMethodName(serverName, namespace, method)
pf("ip=%s platform=%q version=%q method=%s duration=%v params=%s xRequestId=%q err=%q", ip, platform, version, methodName, duration, params, xRequestID, r.Error)
sentry.WithScope(func(scope *sentry.Scope) {
scope.SetExtras(map[string]interface{}{
"params": params,
"duration": duration.String(),
"ip": ip,
"error.data": r.Error.Data,
"error.code": r.Error.Code,
})
scope.SetTags(map[string]string{
"platform": platform,
"version": version,
"method": methodName,
"xRequestId": xRequestID,
})
sentry.CaptureException(r.Error)
})
// remove sensitive error data from response
r.Error.Err = nil
r.Error.Message = "Internal error"
}
return r
}
}
}