Welcome to mirror list, hosted at ThFree Co, Russian Federation.

sentryhandler.go « sentryhandler « middleware « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ee79ea494985337790bdb1c8cb61bb7cbbae95f1 (plain)
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
package sentryhandler

import (
	"strings"
	"time"

	raven "github.com/getsentry/raven-go"
	"github.com/grpc-ecosystem/go-grpc-middleware/tags"
	"gitlab.com/gitlab-org/gitaly/internal/helper"

	"fmt"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

// UnaryLogHandler handles access times and errors for unary RPC's
func UnaryLogHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	start := time.Now()
	resp, err := handler(ctx, req)

	if err != nil {
		logGrpcErrorToSentry(ctx, info.FullMethod, start, err)
	}

	return resp, err
}

// StreamLogHandler handles access times and errors for stream RPC's
func StreamLogHandler(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
	start := time.Now()
	err := handler(srv, stream)

	if err != nil {
		logGrpcErrorToSentry(stream.Context(), info.FullMethod, start, err)
	}

	return err
}

func stringMap(incoming map[string]interface{}) map[string]string {
	result := make(map[string]string)
	for i, v := range incoming {
		result[i] = fmt.Sprintf("%v", v)
	}
	return result
}

func methodToCulprit(methodName string) string {
	methodName = strings.TrimPrefix(methodName, "/gitaly.")
	methodName = strings.Replace(methodName, "/", "::", 1)
	return methodName
}

func logErrorToSentry(err error) (code codes.Code, bypass bool) {
	code = helper.GrpcCode(err)

	bypass = code == codes.OK || code == codes.Canceled
	return code, bypass
}

func generateRavenPacket(ctx context.Context, method string, start time.Time, err error) (*raven.Packet, map[string]string) {
	grpcErrorCode, bypass := logErrorToSentry(err)
	if bypass {
		return nil, nil
	}

	tags := grpc_ctxtags.Extract(ctx)
	ravenDetails := stringMap(tags.Values())

	ravenDetails["grpc.code"] = grpcErrorCode.String()
	ravenDetails["grpc.method"] = method
	ravenDetails["grpc.time_ms"] = fmt.Sprintf("%.0f", time.Since(start).Seconds()*1000)
	ravenDetails["system"] = "grpc"

	// Skip the stacktrace as it's not helpful in this context
	packet := raven.NewPacket(err.Error(), raven.NewException(err, nil))
	grpcMethod := methodToCulprit(method)

	// Details on fingerprinting
	// https://docs.sentry.io/learn/rollups/#customize-grouping-with-fingerprints
	packet.Fingerprint = []string{"grpc", grpcMethod, grpcErrorCode.String()}
	packet.Culprit = grpcMethod
	return packet, ravenDetails
}

func logGrpcErrorToSentry(ctx context.Context, method string, start time.Time, err error) {
	packet, tags := generateRavenPacket(ctx, method, start, err)
	if packet == nil {
		return
	}

	raven.Capture(packet, tags)
}