1require 'grpc'
2require 'raven/base'
3
4require_relative 'sentry.rb'
5module GitalyServer
6  class SentryInterceptor < GRPC::ServerInterceptor
7    # Intercept a unary request response call
8    def request_response(request: nil, call: nil, method: nil)
9      start = Time.now
10      yield
11    rescue => e
12      time_ms = Time.now - start
13      handle_exception(e, call, method, time_ms)
14    end
15
16    # Intercept a server streaming call
17    def server_streamer(request: nil, call: nil, method: nil)
18      start = Time.now
19      yield
20    rescue => e
21      time_ms = Time.now - start
22      handle_exception(e, call, method, time_ms)
23    end
24
25    # Intercept a client streaming call
26    def client_streamer(call: nil, method: nil)
27      start = Time.now
28      yield
29    rescue => e
30      time_ms = Time.now - start
31      handle_exception(e, call, method, time_ms)
32    end
33
34    # Intercept a BiDi streaming call
35    def bidi_streamer(requests: nil, call: nil, method: nil)
36      start = Time.now
37      yield
38    rescue => e
39      time_ms = Time.now - start
40      handle_exception(e, call, method, time_ms)
41    end
42
43    private
44
45    def handle_exception(exc, call, method, time_ms)
46      raise exc unless GitalyServer::Sentry.enabled?
47
48      grpc_method = "#{method.owner.name}##{method.name}"
49      tags = {
50        'system' => 'gitaly-ruby',
51        'gitaly-ruby.method' => grpc_method,
52        'gitaly-ruby.time_ms' => format("%.0f", (time_ms * 1000)),
53        Labkit::Correlation::CorrelationId::LOG_KEY => Labkit::Correlation::CorrelationId.current_id
54      }
55      tags.merge!(call.metadata)
56
57      Raven.tags_context(tags)
58      Raven.capture_exception(exc, fingerprint: ['gitaly-ruby', grpc_method, exc.message])
59
60      raise exc
61    end
62  end
63end
64